diff --git a/.ci.yaml b/.ci.yaml new file mode 100644 index 0000000000000..037020e0ea059 --- /dev/null +++ b/.ci.yaml @@ -0,0 +1,335 @@ +# Describes the targets run in continuous integration environment. +# +# Flutter infra uses this file to generate a checklist of tasks to be performed +# for every commit. +# +# More information at: +# * https://github.com/flutter/cocoon/blob/master/CI_YAML.md +enabled_branches: + - master + - dev + - beta + - stable + +platform_properties: + linux: + properties: + build_host: "false" + build_fuchsia: "false" + build_android_debug: "false" + build_android_aot: "false" + build_android_vulkan: "false" + build_ios: "false" + build_windows_uwp: "false" + build_android_jit_release: "false" + gcs_goldens_bucket: "" + ios_debug: "false" + ios_profile: "false" + ios_release: "false" + no_bitcode: "false" + caches: >- + [ + {"name":"builder_linux_engine","path":"builder"}, + {"name":"openjdk","path":"java"} + ] + # CIPD flutter_internal/java/openjdk/$platform + dependencies: >- + [ + {"dependency": "open_jdk", "version": "version:1.8.0u202-b08"} + ] + device_type: none + os: Linux + mac: + properties: + build_host: "false" + build_fuchsia: "false" + build_android_debug: "false" + build_android_aot: "false" + build_android_vulkan: "false" + build_ios: "false" + build_windows_uwp: "false" + build_android_jit_release: "false" + gcs_goldens_bucket: "" + ios_debug: "false" + ios_profile: "false" + ios_release: "false" + no_bitcode: "false" + caches: >- + [ + {"name":"flutter_cocoapods","path":"cocoapods"}, + {"name":"old_osx_sdk","path":"osx_sdk"}, + {"name":"builder_mac_engine","path":"builder"}, + {"name":"openjdk","path":"java"} + ] + # CIPD flutter_internal/java/openjdk/$platform + dependencies: >- + [ + {"dependency": "open_jdk", "version": "version:1.8.0u202-b08"} + ] + device_type: none + mac_model: "Macmini8,1" + os: Mac-10.15 + xcode: 12a7209 # xcode 12 + windows: + properties: + build_host: "false" + build_fuchsia: "false" + build_android_debug: "false" + build_android_aot: "false" + build_android_vulkan: "false" + build_ios: "false" + build_windows_uwp: "false" + build_android_jit_release: "false" + gcs_goldens_bucket: "" + ios_debug: "false" + ios_profile: "false" + ios_release: "false" + no_bitcode: "false" + caches: >- + [ + {"name":"builder_win_engine","path":"builder"}, + {"name":"openjdk","path":"java"} + ] + # CIPD flutter_internal/java/openjdk/$platform + dependencies: >- + [ + {"dependency": "open_jdk", "version": "version:1.8.0u202-b08"} + ] + device_type: none + os: Windows-10 + +targets: + - name: Linux Android AOT Engine + recipe: engine + properties: + add_recipes_cq: "true" + build_android_aot: "true" + android_sdk_license: \n24333f8a63b6825ea9c5514f83c2829b004d1fee + android_sdk_preview_license: \n84831b9409646a918e30573bab4c9c91346d8abd + timeout: 60 + scheduler: luci + + - name: Linux Android Debug Engine + recipe: engine + properties: + add_recipes_cq: "true" + build_android_debug: "true" + build_android_jit_release: "true" + build_android_vulkan: "true" + android_sdk_license: \n24333f8a63b6825ea9c5514f83c2829b004d1fee + android_sdk_preview_license: \n84831b9409646a918e30573bab4c9c91346d8abd + timeout: 60 + scheduler: luci + + - name: Linux Android Scenarios + bringup: true # Recipe issue https://github.com/flutter/flutter/issues/86427 + recipe: engine/scenarios + properties: + upload_packages: "true" + clobber: "true" + timeout: 60 + scheduler: luci + + - name: Linux Benchmarks + enabled_branches: + - master + recipe: engine/engine_metrics + presubmit: false + properties: + build_host: "true" + timeout: 60 + scheduler: luci + + - name: Linux Fuchsia + recipe: engine + properties: + add_recipes_cq: "true" + build_fuchsia: "true" + fuchsia_ctl_version: version:0.0.27 + timeout: 90 + scheduler: luci + + - name: Linux Fuchsia FEMU + recipe: femu_test + properties: + add_recipes_cq: "true" + build_fuchsia: "true" + fuchsia_ctl_version: version:0.0.27 + timeout: 60 + scheduler: luci + + - name: Linux Framework Smoke Tests + recipe: engine/framework_smoke + timeout: 60 + scheduler: luci + + - name: Linux Host Engine + recipe: engine + properties: + add_recipes_cq: "true" + build_host: "true" + timeout: 60 + scheduler: luci + + - name: Linux Unopt + recipe: engine_unopt + properties: + add_recipes_cq: "true" + timeout: 60 + scheduler: luci + + - name: Linux Arm Host Engine + recipe: engine/engine_arm + properties: + add_recipes_cq: "true" + build_host: "true" + timeout: 90 + scheduler: luci + + - name: Linux Web Engine + recipe: web_engine + properties: + add_recipes_cq: "true" + gcs_goldens_bucket: flutter_logs + timeout: 60 + scheduler: luci + runIf: + - DEPS + - lib/web_ui/** + - web_sdk/** + - tools/** + - ci/** + - flutter_frontend_server/** + + - name: Linux Web Framework tests + recipe: engine/web_engine_framework + properties: + add_recipes_cq: "true" + framework: "true" + shard: web_tests + subshards: >- + ["0", "1", "2", "3", "4", "5", "6", "7_last"] + timeout: 60 + scheduler: luci + runIf: + - DEPS + - lib/web_ui/** + - web_sdk/** + - tools/** + - ci/** + - flutter_frontend_server/** + + - name: Mac Android AOT Engine + recipe: engine + properties: + android_sdk_license: \n24333f8a63b6825ea9c5514f83c2829b004d1fee + android_sdk_preview_license: \n84831b9409646a918e30573bab4c9c91346d8abd + build_android_aot: "true" + timeout: 60 + scheduler: luci + + - name: Mac Host Engine + recipe: engine + properties: + add_recipes_cq: "true" + build_host: "true" + timeout: 75 + scheduler: luci + + - name: Mac Unopt + recipe: engine_unopt + properties: + add_recipes_cq: "true" + jazzy_version: 0.9.5 + timeout: 75 + scheduler: luci + + - name: Mac iOS Engine + recipe: engine + properties: + build_ios: "true" + ios_debug: "true" + jazzy_version: 0.9.5 + timeout: 60 + scheduler: luci + + - name: Mac Web Engine + recipe: web_engine + properties: + gcs_goldens_bucket: flutter_logs + timeout: 60 + scheduler: luci + runIf: + - DEPS + - lib/web_ui/** + - web_sdk/** + - tools/** + - ci/** + - flutter_frontend_server/** + + - name: Windows Android AOT Engine + recipe: engine + properties: + build_android_aot: "true" + android_sdk_license: \n24333f8a63b6825ea9c5514f83c2829b004d1fee + android_sdk_preview_license: \n84831b9409646a918e30573bab4c9c91346d8abd + timeout: 60 + scheduler: luci + + - name: Windows Host Engine + recipe: engine + timeout: 60 + properties: + add_recipes_cq: "true" + build_host: "true" + scheduler: luci + + - name: Windows Unopt + recipe: engine_unopt + properties: + add_recipes_cq: "true" + timeout: 75 + scheduler: luci + + - name: Windows UWP Engine + recipe: engine + properties: + build_windows_uwp: "true" + timeout: 60 + scheduler: luci + + - name: Windows Web Engine + recipe: web_engine + properties: + gcs_goldens_bucket: flutter_logs + timeout: 60 + scheduler: luci + runIf: + - DEPS + - lib/web_ui/** + - web_sdk/** + + - name: Mac iOS Engine Profile + presubmit: false + recipe: engine + properties: + build_ios: "true" + ios_profile: "true" + jazzy_version: 0.9.5 + timeout: 90 + scheduler: luci + + - name: Mac iOS Engine Release + presubmit: false + recipe: engine + properties: + build_ios: "true" + ios_release: "true" + jazzy_version: 0.9.5 + timeout: 90 + scheduler: luci + + - name: Linux ci_yaml engine roller + bringup: true + recipe: infra/ci_yaml + scheduler: luci diff --git a/.cirrus.yml b/.cirrus.yml index d30aa975a1063..da0e3ed875472 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,10 +1,11 @@ -gcp_credentials: ENCRYPTED[987a78af29b91ce8489594c9ab3fec21845bbe5ba68294b8f6def3cf0d380830f06687a89ea69c87344c5ade369700fe] +gcp_credentials: ENCRYPTED[!0e63b52bd7e4fda1cd7b7bf2b4fe515a27fadbeaced01f5ad8b699b81d3611ed64c5d3271bcd8426dd914ef41cba48a0!] # LINUX task: gke_container: dockerfile: "ci/docker/build/Dockerfile" builder_image_name: docker-builder # gce vm image + builder_image_project: flutter-cirrus cluster_name: build-32-cluster zone: us-central1-a namespace: default @@ -19,9 +20,6 @@ task: FRAMEWORK_PATH: "/tmp/master_framework" PATH: "$FLUTTER_ENGINE/third_party/dart/tools/sdks/dart-sdk/bin:$DEPOT_TOOLS:$PATH" USE_ANDROID: "False" - # TODO(liyuqian): currently we're using flutter-cirrus GCP project. Migrate - # to flutter-infra project once the metrics_center service is stabilized, - BENCHMARK_GCP_CREDENTIALS: ENCRYPTED[da76d2b7b39894de70fae1fc9182c97cc41400adc93f0f1c49bc7442f15fb933da8d756ed88523810a9a77c34f51a693] setup_script: | git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS mkdir -p $ENGINE_PATH/src @@ -32,27 +30,6 @@ task: mv $CIRRUS_WORKING_DIR flutter gclient sync matrix: - - name: build_and_benchmark_linux_release - only_if: $CIRRUS_BRANCH == 'master' # Only run for post-submit commits. - compile_host_script: | - cd $ENGINE_PATH/src - ./flutter/tools/gn --runtime-mode=release - ninja -C out/host_release - benchmark_host_script: | - cd $ENGINE_PATH/src/out/host_release/ - ./txt_benchmarks --benchmark_format=json > txt_benchmarks.json - ./fml_benchmarks --benchmark_format=json > fml_benchmarks.json - ./shell_benchmarks --benchmark_format=json > shell_benchmarks.json - ./ui_benchmarks --benchmark_format=json > ui_benchmarks.json - cd $ENGINE_PATH/src/flutter/testing/benchmark - pub get - dart bin/parse_and_send.dart ../../../out/host_release/txt_benchmarks.json - dart bin/parse_and_send.dart ../../../out/host_release/fml_benchmarks.json - dart bin/parse_and_send.dart ../../../out/host_release/shell_benchmarks.json - dart bin/parse_and_send.dart ../../../out/host_release/ui_benchmarks.json - - # The following test depends on Flutter framework repo. It may fail if the - # framework repo is currently broken. - name: build_and_test_linux_unopt_debug compile_host_script: | cd $ENGINE_PATH/src @@ -65,21 +42,10 @@ task: mkdir -p $FRAMEWORK_PATH cd $FRAMEWORK_PATH git clone https://github.com/flutter/flutter.git - test_web_script: | - cd $FRAMEWORK_PATH/flutter/dev/integration_tests/web - ../../../bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web - ../../../bin/flutter --local-engine=host_debug_unopt build web -v analyze_framework_script: | cd $FRAMEWORK_PATH/flutter rm -rf bin/cache/pkg/sky_engine + mkdir -p bin/cache/pkg/ cp -r $ENGINE_PATH/src/out/host_debug_unopt/gen/dart-pkg/sky_engine bin/cache/pkg/ bin/flutter update-packages --local-engine=host_debug_unopt - bin/flutter analyze --dartdocs --flutter-repo --local-engine=host_debug_unopt - test_framework_script: | - cd $FRAMEWORK_PATH/flutter/packages/flutter - ../../bin/flutter test --local-engine=host_debug_unopt - - - name: build_test - build_script: | - cd $ENGINE_PATH/src/flutter - ./ci/build.sh + bin/flutter analyze --flutter-repo --local-engine=host_debug_unopt diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9aab6bc2b8d1b..26f5225b9fa53 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,52 +1,30 @@ -## Description +*Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* -*Replace this paragraph with a description of what this PR is doing. If you're -modifying existing behavior, describe the existing behavior, how this PR is -changing it, and what motivated the change.* +*List which issues are fixed by this PR. You must list at least one issue.* -## Related Issues +*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* -*Replace this paragraph with a list of issues related to this PR from our [issue -database]. Indicate, which of these issues are resolved or fixed by this PR. -There should be at least one issue listed here.* +## Pre-launch Checklist -## Tests - -I added the following tests: - -*Replace this with a list of the tests that you added as part of this PR. A -change in behaviour with no test covering it will likely get reverted -accidentally sooner or later. PRs must include tests for all -changed/updated/fixed behaviors. See [testing the engine] for instructions on -writing and running engine tests.* - -## Checklist - -Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (`[x]`). This will ensure a smooth and quick review process. - -- [ ] I read the [contributor guide] and followed the process outlined there for submitting PRs. +- [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. +- [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. +- [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. +- [ ] I listed at least one issue that this PR fixes in the description above. +- [ ] I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on +writing and running engine tests. +- [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. -- [ ] I read and followed the [C++, Objective-C, Java style guides] for the engine. -- [ ] I read the [tree hygiene] wiki page, which explains my responsibilities. -- [ ] I updated/added relevant documentation. - [ ] All existing and new tests are passing. -- [ ] I am willing to follow-up on review comments in a timely manner. -## Breaking Change - -Did any tests fail when you ran them? Please read [handling breaking changes]. - -- [ ] No, no existing tests failed, so this is *not* a breaking change. -- [ ] Yes, this is a breaking change. *If not, delete the remainder of this section.* - - [ ] I wrote a design doc: https://flutter.dev/go/template *Replace this with a link to your design doc's short link* - - [ ] I got input from the developer relations team, specifically from: *Replace with the names of who gave advice* - - [ ] I wrote a migration guide: https://flutter.dev/go/breaking-changes-template *Replace this with a link to a pull request that adds the migration guide to https://flutter.dev/docs/release/breaking-changes* +If you need help, consider asking for advice on the #hackers-new channel on [Discord]. -[issue database]: https://github.com/flutter/flutter/issues -[contributor guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview -[testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine +[Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview +[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene +[Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/master/CONTRIBUTING.md#style +[testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ -[tree hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene -[handling breaking changes]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes +[flutter/tests]: https://github.com/flutter/tests +[breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes +[Discord]: https://github.com/flutter/flutter/wiki/Chat diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml deleted file mode 100644 index 8a18797fd9d6e..0000000000000 --- a/.github/auto_assign.yml +++ /dev/null @@ -1,38 +0,0 @@ -# This is the config file for `auto-assign` bot. -# https://github.com/kentaro-m/auto-assign/ - -# Set to true to add reviewers to pull requests -addReviewers: true - -# Set to true to add assignees to pull requests -addAssignees: false - -# A list of reviewers to be added to pull requests (GitHub user name) -# Note: Add new engine contributors here when joining the team. -reviewers: - - gaaclarke - - liyuqian - - gw280 - - chinmaygarde - - GaryQian - - jason-simmons - - iskakaushik - - flar - -# A number of reviewers added to the pull request -# Set 0 to add all the reviewers (default: 0) -numberOfReviewers: 1 - -# A list of assignees, overrides reviewers if set -# assignees: -# - assigneeA - -# A number of assignees to add to the pull request -# Set to 0 to add all of the assignees. -# Uses numberOfReviewers if unset. -# numberOfAssignees: 2 - -# A list of keywords to be skipped the process that add reviewers if pull requests include it -skipKeywords: - - Roll - - web diff --git a/.gitignore b/.gitignore index f92944ad0c5ff..74bb88e9104da 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .*.sw? .DS_Store .ccls-cache +.cache .classpath .clangd/ .cproject @@ -100,6 +101,7 @@ unlinked_spec.ds **/ios/.generated/ **/ios/Flutter/App.framework **/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.xcframework **/ios/Flutter/Flutter.podspec **/ios/Flutter/Generated.xcconfig **/ios/Flutter/app.flx @@ -128,3 +130,6 @@ app.*.symbols !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock + +# Prebuilt binaries. +/prebuilts/ diff --git a/AUTHORS b/AUTHORS index 984e9f525e7c1..455f2c6699c8f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -16,4 +16,8 @@ Dwayne Slater Tetsuhiro Ueda shoryukenn SOTEC GmbH & Co. KG -Hidenori Matsubayashi \ No newline at end of file +Hidenori Matsubayashi +Sarbagya Dhaubanjar +Callum Moffat +Koutaro Mori +TheOneWithTheBraid \ No newline at end of file diff --git a/BUILD.gn b/BUILD.gn index 82575d2ff3755..fe1135605fc13 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -4,7 +4,9 @@ import("//flutter/common/config.gni") import("//flutter/shell/platform/config.gni") +import("//flutter/shell/platform/glfw/config.gni") import("//flutter/testing/testing.gni") +import("//third_party/dart/build/dart/copy_tree.gni") # Whether to build the dartdevc sdk, libraries, and source files # required for the flutter web sdk. @@ -20,12 +22,10 @@ config("config") { } } - # This define is transitional and will be removed after the embedder API - # transition is complete. - # - # TODO(bugs.fuchsia.dev/54041): Remove when no longer neccesary. - if (is_fuchsia && flutter_enable_legacy_fuchsia_embedder) { - defines = [ "LEGACY_FUCHSIA_EMBEDDER" ] + defines = [] + + if (is_debug) { + defines += [ "FLUTTER_ENABLE_DIFF_CONTEXT" ] } } @@ -36,6 +36,35 @@ config("export_dynamic_symbols") { } } +if (flutter_prebuilt_dart_sdk) { + copy_trees("_copy_trees") { + sources = [ + { + target = "copy_dart_sdk" + visibility = [ ":dart_sdk" ] + source = target_prebuilt_dart_sdk + dest = "$root_out_dir/dart-sdk" + ignore_patterns = "{}" + }, + ] + } +} + +# Flutter SDK artifacts should only be built when either doing host builds, or +# for cross-compiled desktop targets. +_build_engine_artifacts = + current_toolchain == host_toolchain || (is_linux && !is_chromeos) || is_mac + +group("dart_sdk") { + if (_build_engine_artifacts) { + if (flutter_prebuilt_dart_sdk) { + public_deps = [ ":copy_dart_sdk" ] + } else { + public_deps = [ "//third_party/dart:create_sdk" ] + } + } +} + group("flutter") { testonly = true @@ -45,11 +74,6 @@ group("flutter") { "//flutter/sky", ] - # Flutter SDK artifacts should only be built when either doing host builds, or - # for cross-compiled desktop targets. - build_engine_artifacts = - current_toolchain == host_toolchain || (is_linux && !is_chromeos) - # If enbaled, compile the SDK / snapshot. if (!is_fuchsia) { public_deps += [ @@ -57,10 +81,15 @@ group("flutter") { "//flutter/lib/snapshot:kernel_platform_files", ] - if (build_engine_artifacts) { + if (_build_engine_artifacts) { public_deps += [ + ":dart_sdk", "//flutter/flutter_frontend_server:frontend_server", - "//third_party/dart:create_sdk", + + # This must be listed explicitly for desktop cross-builds since + # //flutter/lib/snapshot:generate_snapshot_bin will only build + # gen_snapshot for the host and not the target. + "//third_party/dart/runtime/bin:gen_snapshot", ] if (full_dart_sdk) { @@ -69,7 +98,7 @@ group("flutter") { } } - if (build_engine_artifacts) { + if (_build_engine_artifacts) { public_deps += [ "//flutter/shell/testing", "//flutter/tools/const_finder", @@ -87,19 +116,41 @@ group("flutter") { ] } + if ((flutter_runtime_mode == "debug" || flutter_runtime_mode == "profile") && + (is_ios || is_android)) { + public_deps += [ "//flutter/testing/scenario_app" ] + } + + if (is_android && flutter_runtime_mode == "profile" && + current_cpu == "arm64") { + public_deps += [ "//flutter/testing/android_background_image" ] + } + # Compile all unittests targets if enabled. if (enable_unittests) { public_deps += [ "//flutter/flow:flow_unittests", "//flutter/fml:fml_unittests", + "//flutter/lib/spirv/test/exception_shaders:spirv_compile_exception_shaders", "//flutter/lib/ui:ui_unittests", + "//flutter/runtime:no_dart_plugin_registrant_unittests", "//flutter/runtime:runtime_unittests", "//flutter/shell/common:shell_unittests", + "//flutter/shell/platform/embedder:embedder_proctable_unittests", "//flutter/shell/platform/embedder:embedder_unittests", "//flutter/testing:testing_unittests", + "//flutter/testing/dart", + "//flutter/testing/smoke_test_failure", + "//flutter/third_party/tonic/tests:tonic_unittests", "//flutter/third_party/txt:txt_unittests", ] + # The accessibility library only supports Mac and Windows at the moment. + if (is_mac || is_win) { + public_deps += + [ "//flutter/third_party/accessibility:accessibility_unittests" ] + } + if (is_fuchsia) { public_deps += [ "//flutter/shell/platform/fuchsia:tests" ] } @@ -121,15 +172,15 @@ group("flutter") { # embeddings are being built. if (enable_desktop_embeddings) { public_deps += [ - "//flutter/shell/platform/common/cpp:common_cpp_core_unittests", - "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests", + "//flutter/shell/platform/common:common_cpp_core_unittests", + "//flutter/shell/platform/common/client_wrapper:client_wrapper_unittests", ] if (!is_fuchsia) { # These tests require the embedder and thus cannot run on fuchsia. # TODO(): Enable when embedder works on fuchsia. public_deps += - [ "//flutter/shell/platform/common/cpp:common_cpp_unittests" ] + [ "//flutter/shell/platform/common:common_cpp_unittests" ] # These tests require GLFW and thus cannot run on fuchsia. public_deps += [ "//flutter/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests" ] @@ -138,6 +189,10 @@ group("flutter") { if (is_linux) { public_deps += [ "//flutter/shell/platform/linux:flutter_linux_unittests" ] + if (build_glfw_shell) { + public_deps += + [ "//flutter/shell/platform/glfw:flutter_glfw_unittests" ] + } } if (is_mac) { @@ -145,10 +200,17 @@ group("flutter") { } if (is_win) { - public_deps += [ - "//flutter/shell/platform/windows:flutter_windows_unittests", - "//flutter/shell/platform/windows/client_wrapper:client_wrapper_windows_unittests", - ] + if (target_os == "winuwp") { + # TODO: Add winnup variant of client_wrapper_windows_unittests here; see + # https://github.com/flutter/flutter/issues/70197 + public_deps += + [ "//flutter/shell/platform/windows:flutter_windows_unittests" ] + } else { + public_deps += [ + "//flutter/shell/platform/windows:flutter_windows_unittests", + "//flutter/shell/platform/windows/client_wrapper:client_wrapper_windows_unittests", + ] + } } } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86c46f4b65e33..0ffb52ade03d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,18 +20,30 @@ contributing guide. The Flutter engine follows Google style for the languages it uses: - [C++](https://google.github.io/styleguide/cppguide.html) -- [Objective-C](https://google.github.io/styleguide/objcguide.html) (including - [Objective-C++](https://google.github.io/styleguide/objcguide.html#objective-c)) -- [Java](https://google.github.io/styleguide/javaguide.html) - -C++ and Objective-C/C++ files are formatted with `clang-format`, and GN files with `gn format`. + - **Note**: The Linux embedding generally follows idiomatic GObject-based C + style. Use of C++ is discouraged in that embedding to avoid creating hybrid + code that feels unfamiliar to either developers used to working with + `GObject` or C++ developers. For example, do not use STL collections or + `std::string`. Exceptions: + - C-style casts are forbidden; use C++ casts. + - Use `nullptr` rather than `NULL`. + - Avoid `#define`; for internal constants use `static constexpr` instead. +- [Objective-C][objc_style] (including [Objective-C++][objcc_style]) +- [Java][java_style] + +C/C++ and Objective-C/C++ files are formatted with `clang-format`, and GN files +with `gn format`. [build_status]: https://cirrus-ci.com/github/flutter/engine [code_of_conduct]: https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md [contrib_guide]: https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md [engine_dev_setup]: https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment +[objc_style]: https://google.github.io/styleguide/objcguide.html +[objcc_style]: https://google.github.io/styleguide/objcguide.html#objective-c +[java_style]: https://google.github.io/styleguide/javaguide.html ### Fuchsia Contributions from Googlers -Googlers contributing to Fuchsia should follow the additional steps at: go/flutter-fuchsia-pr-policy. +Googlers contributing to Fuchsia should follow the additional steps at: +go/flutter-fuchsia-pr-policy. diff --git a/DEPS b/DEPS index 17907a3f27031..66d0090a08d95 100644 --- a/DEPS +++ b/DEPS @@ -21,12 +21,17 @@ vars = { 'chromium_git': 'https://chromium.googlesource.com', 'swiftshader_git': 'https://swiftshader.googlesource.com', 'dart_git': 'https://dart.googlesource.com', + 'flutter_git': 'https://flutter.googlesource.com', 'fuchsia_git': 'https://fuchsia.googlesource.com', 'github_git': 'https://github.com', 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'a87c5076a876e63664566bba55e16b6d4d410234', + 'skia_revision': 'f61ec43f84dd73508a566c36ef085156b40285f0', + + # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY + # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. + 'canvaskit_cipd_instance': 'srPdrAoUIWOde-KMPE5S8koi64mejhae7NzvfR_7m3gC', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the @@ -34,39 +39,29 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'ddbeaabe8b3dc6299d9c963d414f820198edf8cd', + 'dart_revision': 'f2b0d387684d62383b24bb802fae829f24f658d9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py - 'dart_args_tag': '1.6.0', - 'dart_boringssl_gen_rev': '429ccb1877f7987a6f3988228bc2440e61293499', - 'dart_boringssl_rev': '4dfd5af70191b068aebe567b8e29ce108cee85ce', - 'dart_collection_rev': '52e219581f72a3eac013d6f5550c580962677425', - 'dart_dart_style_tag': '1.3.7', - 'dart_http_retry_tag': '0.1.1', + 'dart_boringssl_gen_rev': '7322fc15cc065d8d2957fccce6b62a509dc4d641', + 'dart_boringssl_rev': '1607f54fed72c6589d560254626909a64124f091', + 'dart_clock_rev': 'a494269254ba978e7ef8f192c5f7fec3fc05b9d3', + 'dart_collection_rev': '75a7a5510979a3cd70143af85bcc1667ee233674', + 'dart_devtools_rev': '64cffbed6366329ad05e44d48fa2298367643bb6', 'dart_http_throttle_tag': '1.0.2', - 'dart_intl_tag': '0.16.1', - 'dart_linter_tag': '0.1.119', - 'dart_oauth2_tag': '1.6.0', - 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '04e237f78b2302d7f20d0b362554425e8deb8add', - 'dart_pub_semver_tag': 'v1.4.4', - 'dart_quiver-dart_tag': '246e754fe45cecb6aa5f3f13b4ed61037ff0d784', - 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', - 'dart_root_certificates_rev': '7e5ec82c99677a2e5b95ce296c4d68b0d3378ed8', - 'dart_shelf_packages_handler_tag': '2.0.0', - 'dart_shelf_proxy_tag': '0.1.0+7', - 'dart_shelf_static_rev': 'v0.2.8', - 'dart_shelf_web_socket_tag': '0.2.2+3', - 'dart_sse_tag': 'e5cf68975e8e87171a3dc297577aa073454a91dc', - 'dart_stack_trace_tag': 'a958966148516dfa64e2b54c14492175da5cc8e1', - 'dart_stagehand_tag': 'v3.3.9', - 'dart_stream_channel_tag': 'c446774fd077c9bdbd6235a7aadc661ef60a9727', - 'dart_test_reflective_loader_tag': '0.1.9', - 'dart_tflite_native_rev': '3c777c40608a2a9f1427bfe0028ab48e7116b4c1', - 'dart_typed_data_tag': 'f94fc57b8e8c0e4fe4ff6cfd8290b94af52d3719', - 'dart_usage_tag': '3.4.0', - 'dart_watcher_rev': 'fc3c9aae5d31d707b3013b42634dde8d8a1161b4', + 'dart_intl_tag': '0.17.0-nullsafety', + 'dart_linter_tag': '1.9.0', + 'dart_protobuf_rev': 'c1eb6cb51af39ccbaa1a8e19349546586a5c8e31', + 'dart_pub_rev': '9f2ddc02f066c68fb638ff65fbb73761ece23267', + 'dart_resource_rev': '6b79867d0becf5395e5819a75720963b8298e9a7', + 'dart_root_certificates_rev': '692f6d6488af68e0121317a9c2c9eb393eb0ee50', + 'dart_shelf_proxy_tag': 'v1.0.0', + 'dart_shelf_static_rev': '202ec1a53c9a830c17cf3b718d089cf7eba568ad', + 'dart_sse_tag': 'd505b383768889a1e3e90097684e929a9e6d6b8f', + 'dart_stack_trace_tag': '6788afc61875079b71b3d1c3e65aeaa6a25cbc2f', + 'dart_stream_channel_tag': 'd7251e61253ec389ee6e045ee1042311bced8f1d', + 'dart_watcher_rev': '3924194385fb215cef483193ed2879a618a3d69c', + 'dart_webdev_rev': '50fe70a3137d9665fbe94cd34af5277b65d95079', 'ocmock_tag': 'v3.7.1', @@ -74,7 +69,7 @@ vars = { 'ios_tools_revision': '69b7c1b160e7107a6a98d948363772dc9caea46f', # Checkout Android dependencies only on platforms where we build for Android targets. - 'download_android_deps': 'host_os == "mac" or host_os == "linux"', + 'download_android_deps': 'host_cpu == "x64" and (host_os == "mac" or host_os == "linux")', # Checkout Windows dependencies only if we are building on Windows. 'download_windows_deps' : 'host_os == "win"', @@ -99,13 +94,14 @@ gclient_gn_args = [ # If you need to add a new host, contact chrome infrastructure team. allowed_hosts = [ 'chromium.googlesource.com', + 'flutter.googlesource.com', 'fuchsia.googlesource.com', 'github.com', 'skia.googlesource.com', ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'f83d1d75216e97fb696434bca1cb9a4e7a570fb6', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '97a255eb289ca5b6e03a6c93e2182cdcffce0b76', # Fuchsia compatibility # @@ -116,14 +112,11 @@ deps = { 'src/third_party/benchmark': Var('fuchsia_git') + '/third_party/benchmark' + '@' + 'a779ffce872b4c811beef482e18bd0b63626aa42', - 'src/third_party/googletest': - Var('fuchsia_git') + '/third_party/googletest' + '@' + '3fef9015bfe7617d57806cd7c73a653b28559fae', - 'src/third_party/rapidjson': Var('fuchsia_git') + '/third_party/rapidjson' + '@' + 'ef3564c5c8824989393b87df25355baf35ff544b', 'src/third_party/harfbuzz': - Var('fuchsia_git') + '/third_party/harfbuzz' + '@' + '9c55f4cf3313d68d68f68419e7a57fb0771fcf49', + Var('fuchsia_git') + '/third_party/harfbuzz' + '@' + '9a15acd28cf9c62a5820b6ed1013c4a7f8717d8c', 'src/third_party/libcxx': Var('fuchsia_git') + '/third_party/libcxx' + '@' + '7524ef50093a376f334a62a7e5cebf5d238d4c99', @@ -134,6 +127,22 @@ deps = { 'src/third_party/glfw': Var('fuchsia_git') + '/third_party/glfw' + '@' + '999f3556fdd80983b10051746264489f2cb1ef16', + 'src/third_party/shaderc': + Var('github_git') + '/google/shaderc.git' + '@' + '948660cccfbbc303d2590c7f44a4cee40b66fdd6', + + 'src/third_party/glslang': + Var('github_git') + '/KhronosGroup/glslang.git' + '@' + '9431c53c84c14fa9e9cd37678262ebba55c62c87', + + 'src/third_party/spirv_tools': + Var('github_git') + '/KhronosGroup/SPIRV-Tools.git' + '@' + '1020e394cb1267332d58497150d2b024371a8e41', + + 'src/third_party/spirv_headers': + Var('github_git') + '/KhronosGroup/SPIRV-Headers.git' + '@' + '85b7e00c7d785962ffe851a177c84353d037dcb6', + + 'src/third_party/spirv_cross': + Var('github_git') + '/KhronosGroup/SPIRV-Cross.git' + '@' + '418542eaefdb609f548d25a1e3962fb69d80da63', + + # Chromium-style # # As part of integrating with Fuchsia, we should eventually remove all these @@ -143,11 +152,14 @@ deps = { Var('chromium_git') + '/chromium/src/ios.git' + '@' + Var('ios_tools_revision'), 'src/third_party/icu': - Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '8d29692df640668ed7e4d1817715440c4e05697a', + Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '2a822c5626ab1ed40366758e4740b4f0ea40237d', 'src/third_party/khronos': Var('chromium_git') + '/chromium/src/third_party/khronos.git' + '@' + '7122230e90547962e0f0c627f62eeed3c701f275', + 'src/third_party/googletest': + Var('github_git') + '/google/googletest' + '@' + 'f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4', + 'src/third_party/boringssl': Var('github_git') + '/dart-lang/boringssl_gen.git' + '@' + Var('dart_boringssl_gen_rev'), @@ -159,65 +171,68 @@ deps = { # WARNING: Unused Dart dependencies in the list below till "WARNING:" marker are removed automatically - see create_updated_flutter_deps.py. + 'src/third_party/dart/third_party/devtools': + {'packages': [{'version': 'git_revision:64cffbed6366329ad05e44d48fa2298367643bb6', 'package': 'dart/third_party/flutter/devtools'}], 'dep_type': 'cipd'}, + 'src/third_party/dart/third_party/pkg/args': - Var('dart_git') + '/args.git' + '@' + Var('dart_args_tag'), + Var('dart_git') + '/args.git@bf4c8796881b62fd5d3f6d86ab43014f9651eb20', 'src/third_party/dart/third_party/pkg/async': - Var('dart_git') + '/async.git@38ace5fa83697928d5f29911e346e6311dd00857', + Var('dart_git') + '/async.git@88cc6079037da08dcb31cf8dcd62173c2ddf1e89', 'src/third_party/dart/third_party/pkg/bazel_worker': - Var('dart_git') + '/bazel_worker.git@26680d5e249b249c7216ab2fed0ac8ed4ee285c5', + Var('dart_git') + '/bazel_worker.git@0885637b037979afbf5bcd05fd748b309fd669c0', 'src/third_party/dart/third_party/pkg/boolean_selector': Var('dart_git') + '/boolean_selector.git@665e6921ab246569420376f827bff4585dff0b14', 'src/third_party/dart/third_party/pkg/charcode': - Var('dart_git') + '/charcode.git@4a685faba42d86ebd9d661eadd1e79d0a1c34c43', + Var('dart_git') + '/charcode.git@84ea427711e24abf3b832923959caa7dd9a8514b', 'src/third_party/dart/third_party/pkg/cli_util': - Var('dart_git') + '/cli_util.git@0.2.0', + Var('dart_git') + '/cli_util.git@8c504de5deb08fe32ecf51f9662bb37d8c708e57', + + 'src/third_party/dart/third_party/pkg/clock': + Var('dart_git') + '/clock.git' + '@' + Var('dart_clock_rev'), 'src/third_party/dart/third_party/pkg/collection': Var('dart_git') + '/collection.git' + '@' + Var('dart_collection_rev'), 'src/third_party/dart/third_party/pkg/convert': - Var('dart_git') + '/convert.git@c1b01f832835d3d8a06b0b246a361c0eaab35d3c', + Var('dart_git') + '/convert.git@e063fdca4bebffecbb5e6aa5525995120982d9ce', 'src/third_party/dart/third_party/pkg/crypto': - Var('dart_git') + '/crypto.git@f7c48b334b1386bc5ab0f706fbcd6df8496a87fc', + Var('dart_git') + '/crypto.git@b5024e4de2b1c474dd558bef593ddbf0bfade152', 'src/third_party/dart/third_party/pkg/csslib': - Var('dart_git') + '/csslib.git@6f77b3dcee957d3e2d5083f666221a220e9ed1f1', - - 'src/third_party/dart/third_party/pkg/dart2js_info': - Var('dart_git') + '/dart2js_info.git@0632a623b08e1f601c7eba99e0186a581ae799e9', + Var('dart_git') + '/csslib.git@e411d862fd8cc50415c1badf2632e017373b3f47', 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@2bef0f260594b822f55c8c8f777d9c4c1ea8f76c', + Var('dart_git') + '/dartdoc.git@5f39ec674d81f5c199151d823fa4ecd01fc59eb2', 'src/third_party/dart/third_party/pkg/ffi': - Var('dart_git') + '/ffi.git@454ab0f9ea6bd06942a983238d8a6818b1357edb', + Var('dart_git') + '/ffi.git@4dd32429880a57b64edaf54c9d5af8a9fa9a4ffb', + + 'src/third_party/dart/third_party/pkg/file': + Var('dart_git') + '/external/github.com/google/file.dart/@0e09370f581ab6388d46fda4cdab66638c0171a1', 'src/third_party/dart/third_party/pkg/fixnum': Var('dart_git') + '/fixnum.git@16d3890c6dc82ca629659da1934e412292508bba', 'src/third_party/dart/third_party/pkg/glob': - Var('dart_git') + '/glob.git@e9f4e6b7ae8abe5071461cf8f47191bb19cf7ef6', + Var('dart_git') + '/glob.git@a62acf590598f458d3198d9f2930c1c9dd4b1379', 'src/third_party/dart/third_party/pkg/html': - Var('dart_git') + '/html.git@22f17e97fedeacaa1e945cf84d8016284eed33a6', + Var('dart_git') + '/html.git@00cd3c22dac0e68e6ed9e7e4945101aedb1b3109', 'src/third_party/dart/third_party/pkg/http': - Var('dart_git') + '/http.git@ca418355b5fc60cf981de3bd7364ec0dd943fa8f', + Var('dart_git') + '/http.git@778174bca2c13becd88ef3353309190b1e8b9479', 'src/third_party/dart/third_party/pkg/http_multi_server': - Var('dart_git') + '/http_multi_server.git@ea269f79321d659208402088f3297e8920a88ee6', + Var('dart_git') + '/http_multi_server.git@de1b312164c24a1690b46c6e97bd47eff40c4649', 'src/third_party/dart/third_party/pkg/http_parser': - Var('dart_git') + '/http_parser.git@6e63a97b5aaa2b4d1215fe01683e51fb73258e54', - - 'src/third_party/dart/third_party/pkg/http_retry': - Var('dart_git') + '/http_retry.git' + '@' + Var('dart_http_retry_tag'), + Var('dart_git') + '/http_parser.git@202391286ddc13c4c3c284ac5b511f04697250ed', 'src/third_party/dart/third_party/pkg/http_throttle': Var('dart_git') + '/http_throttle.git' + '@' + Var('dart_http_throttle_tag'), @@ -226,40 +241,37 @@ deps = { Var('dart_git') + '/intl.git' + '@' + Var('dart_intl_tag'), 'src/third_party/dart/third_party/pkg/json_rpc_2': - Var('dart_git') + '/json_rpc_2.git@8f189db8f0c299187a0e8fa959dba7e9b0254be5', + Var('dart_git') + '/json_rpc_2.git@7e00f893440a72de0637970325e4ea44bd1e8c8e', 'src/third_party/dart/third_party/pkg/linter': Var('dart_git') + '/linter.git' + '@' + Var('dart_linter_tag'), 'src/third_party/dart/third_party/pkg/logging': - Var('dart_git') + '/logging.git@1590ba0b648a51e7eb3895c612e4b72f72623b6f', + Var('dart_git') + '/logging.git@575781ef196e4fed4fb737e38fb4b73d62727187', 'src/third_party/dart/third_party/pkg/markdown': - Var('dart_git') + '/markdown.git@dbeafd47759e7dd0a167602153bb9c49fb5e5fe7', + Var('dart_git') + '/markdown.git@9c4beaac96d8f008078e00b027915f81b665d2de', 'src/third_party/dart/third_party/pkg/matcher': - Var('dart_git') + '/matcher.git@9cae8faa7868bf3a88a7ba45eb0bd128e66ac515', + Var('dart_git') + '/matcher.git@6ba4a6d68bdfacff3d572c9ea98333dfc66fd6bf', 'src/third_party/dart/third_party/pkg/mime': - Var('dart_git') + '/mime.git@0.9.7', + Var('dart_git') + '/mime.git@c931f4bed87221beaece356494b43731445ce7b8', 'src/third_party/dart/third_party/pkg/mockito': Var('dart_git') + '/mockito.git@d39ac507483b9891165e422ec98d9fb480037c8b', - 'src/third_party/dart/third_party/pkg/mustache': - Var('dart_git') + '/external/github.com/xxgreg/mustache@664737ecad027e6b96d0d1e627257efa0e46fcb1', - 'src/third_party/dart/third_party/pkg/oauth2': - Var('dart_git') + '/oauth2.git' + '@' + Var('dart_oauth2_tag'), + Var('dart_git') + '/oauth2.git@7cd3284049fe5badbec9f2bea2afc41d14c01057', 'src/third_party/dart/third_party/pkg/path': - Var('dart_git') + '/path.git@62ecd5a78ffe5734d14ed0df76d20309084cd04a', + Var('dart_git') + '/path.git@c20d73c3516d3a0061c90f14b761ff532b9bf707', 'src/third_party/dart/third_party/pkg/pedantic': - Var('dart_git') + '/pedantic.git@24b38df72430d7e21cb4257828580becb9a39c72', + Var('dart_git') + '/pedantic.git@66f2f6c27581c7936482e83be80b27be2719901c', 'src/third_party/dart/third_party/pkg/pool': - Var('dart_git') + '/pool.git@eedbd5fde84f9a1a8da643b475305a81841da599', + Var('dart_git') + '/pool.git@7abe634002a1ba8a0928eded086062f1307ccfae', 'src/third_party/dart/third_party/pkg/protobuf': Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_rev'), @@ -268,19 +280,16 @@ deps = { Var('dart_git') + '/pub.git' + '@' + Var('dart_pub_rev'), 'src/third_party/dart/third_party/pkg/pub_semver': - Var('dart_git') + '/pub_semver.git' + '@' + Var('dart_pub_semver_tag'), - - 'src/third_party/dart/third_party/pkg/quiver': - Var('chromium_git') + '/external/github.com/google/quiver-dart.git' + '@' + Var('dart_quiver-dart_tag'), + Var('dart_git') + '/pub_semver.git@f50d80ef10c4b2fa5f4c8878036a4d9342c0cc82', 'src/third_party/dart/third_party/pkg/resource': Var('dart_git') + '/resource.git' + '@' + Var('dart_resource_rev'), 'src/third_party/dart/third_party/pkg/shelf': - Var('dart_git') + '/shelf.git@289309adc6c39aab0a63db676d550c517fc1cc2d', + Var('dart_git') + '/shelf.git@46483f896cc4308ee3d8e997030ae799b72aa16a', 'src/third_party/dart/third_party/pkg/shelf_packages_handler': - Var('dart_git') + '/shelf_packages_handler.git' + '@' + Var('dart_shelf_packages_handler_tag'), + Var('dart_git') + '/shelf_packages_handler.git@78302e67c035047e6348e692b0c1182131f0fe35', 'src/third_party/dart/third_party/pkg/shelf_proxy': Var('dart_git') + '/shelf_proxy.git' + '@' + Var('dart_shelf_proxy_tag'), @@ -289,7 +298,7 @@ deps = { Var('dart_git') + '/shelf_static.git' + '@' + Var('dart_shelf_static_rev'), 'src/third_party/dart/third_party/pkg/shelf_web_socket': - Var('dart_git') + '/shelf_web_socket.git' + '@' + Var('dart_shelf_web_socket_tag'), + Var('dart_git') + '/shelf_web_socket.git@24fb8a04befa75a94ac63a27047b231d1a22aab4', 'src/third_party/dart/third_party/pkg/source_map_stack_trace': Var('dart_git') + '/source_map_stack_trace.git@1c3026f69d9771acf2f8c176a1ab750463309cce', @@ -298,7 +307,7 @@ deps = { Var('dart_git') + '/source_maps.git@53eb92ccfe6e64924054f83038a534b959b12b3e', 'src/third_party/dart/third_party/pkg/source_span': - Var('dart_git') + '/source_span.git@cc7c4288a83f71ecef3414199947b52a8c112c65', + Var('dart_git') + '/source_span.git@1be3c44045a06dff840d2ed3a13e6082d7a03a23', 'src/third_party/dart/third_party/pkg/sse': Var('dart_git') + '/sse.git' + '@' + Var('dart_sse_tag'), @@ -306,9 +315,6 @@ deps = { 'src/third_party/dart/third_party/pkg/stack_trace': Var('dart_git') + '/stack_trace.git' + '@' + Var('dart_stack_trace_tag'), - 'src/third_party/dart/third_party/pkg/stagehand': - Var('dart_git') + '/stagehand.git' + '@' + Var('dart_stagehand_tag'), - 'src/third_party/dart/third_party/pkg/stream_channel': Var('dart_git') + '/stream_channel.git' + '@' + Var('dart_stream_channel_tag'), @@ -319,45 +325,48 @@ deps = { Var('dart_git') + '/term_glyph.git@6a0f9b6fb645ba75e7a00a4e20072678327a0347', 'src/third_party/dart/third_party/pkg/test': - Var('dart_git') + '/test.git@e37a93bbeae23b215972d1659ac865d71287ff6a', + Var('dart_git') + '/test.git@099dcc4d052a30c6921489cfbefa1c8531d12975', 'src/third_party/dart/third_party/pkg/test_reflective_loader': - Var('dart_git') + '/test_reflective_loader.git' + '@' + Var('dart_test_reflective_loader_tag'), - - 'src/third_party/dart/third_party/pkg/tflite_native': - Var('dart_git') + '/tflite_native.git' + '@' + Var('dart_tflite_native_rev'), + Var('dart_git') + '/test_reflective_loader.git@54e930a11c372683792e22bddad79197728c91ce', 'src/third_party/dart/third_party/pkg/typed_data': - Var('dart_git') + '/typed_data.git' + '@' + Var('dart_typed_data_tag'), + Var('dart_git') + '/typed_data.git@29ce5a92b03326d0b8035916ac04f528874994bd', 'src/third_party/dart/third_party/pkg/usage': - Var('dart_git') + '/usage.git' + '@' + Var('dart_usage_tag'), + Var('dart_git') + '/usage.git@e0780cd8b2f8af69a28dc52678ffe8492da27d06', 'src/third_party/dart/third_party/pkg/watcher': Var('dart_git') + '/watcher.git' + '@' + Var('dart_watcher_rev'), 'src/third_party/dart/third_party/pkg/web_socket_channel': - Var('dart_git') + '/web_socket_channel.git@490061ef0e22d3c8460ad2802f9948219365ad6b', + Var('dart_git') + '/web_socket_channel.git@6448ce532445a8a458fa191d9346df071ae0acad', + + 'src/third_party/dart/third_party/pkg/webdev': + Var('dart_git') + '/webdev.git' + '@' + Var('dart_webdev_rev'), 'src/third_party/dart/third_party/pkg/yaml': - Var('dart_git') + '/yaml.git@e5de429147a6b0fcb7e8ddb3c8e4674dc5dd0ecc', + Var('dart_git') + '/yaml.git@b4c4411631bda556ce9a45af1ab0eecaf9f3ac53', 'src/third_party/dart/third_party/pkg_tested/dart_style': - Var('dart_git') + '/dart_style.git' + '@' + Var('dart_dart_style_tag'), + Var('dart_git') + '/dart_style.git@14d9b6fd58cc4744676c12be3cc5eee2a779db82', 'src/third_party/dart/third_party/pkg_tested/package_config': - Var('dart_git') + '/package_config.git@9c586d04bd26fef01215fd10e7ab96a3050cfa64', + Var('dart_git') + '/package_config.git@fb736aa12316dd2d882b202a438a6946a4b4bea0', 'src/third_party/dart/tools/sdks': - {'packages': [{'version': 'version:2.10.0-110.3.beta', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, + {'packages': [{'version': 'version:2.14.0-377.4.beta', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, # WARNING: end of dart dependencies list that is cleaned up automatically - see create_updated_flutter_deps.py. 'src/third_party/colorama/src': Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', + 'src/third_party/expat': + Var('flutter_git') + '/third_party/expat' + '@' + '1441fc5252746e53b285d15be7e65e4cd74d97eb', + 'src/third_party/freetype2': - Var('fuchsia_git') + '/third_party/freetype2' + '@' + 'edab12c07ac05d1185616688f338b1ad15936796', + Var('flutter_git') + '/third_party/freetype2' + '@' + '1f03c1b2d7f2ae832a4fbe9d12bd96c3c15bbece', 'src/third_party/root_certificates': Var('dart_git') + '/root_certificates.git' + '@' + Var('dart_root_certificates_rev'), @@ -371,17 +380,20 @@ deps = { 'src/third_party/libjpeg-turbo': Var('fuchsia_git') + '/third_party/libjpeg-turbo' + '@' + '0fb821f3b2e570b2783a94ccd9a2fb1f4916ae9f', + 'src/third_party/libpng': + Var('flutter_git') + '/third_party/libpng' + '@' + 'afda123a503bdc41df13f86316cf5f83bc204761', + 'src/third_party/libwebp': - Var('chromium_git') + '/webm/libwebp.git' + '@' + '0.6.0', + Var('chromium_git') + '/webm/libwebp.git' + '@' + '1.2.0', 'src/third_party/wuffs': - Var('skia_git') + '/external/github.com/google/wuffs.git' + '@' + '00cc8a50aa0c86b6bcb37e9ece8fb100047cc17b', + Var('skia_git') + '/external/github.com/google/wuffs.git' + '@' + 'c86add25f790360f0aca026c4f1c2c4e2d12408d', 'src/third_party/fontconfig/src': Var('chromium_git') + '/external/fontconfig.git' + '@' + 'c336b8471877371f0190ba06f7547c54e2b890ba', 'src/third_party/gyp': - Var('chromium_git') + '/external/gyp.git' + '@' + '4801a5331ae62da9769a327f11c4213d32fb0dad', + Var('chromium_git') + '/external/gyp.git' + '@' + 'caa60026e223fc501e8b337fd5086ece4028b1c6', # Headers for Vulkan 1.1 'src/third_party/vulkan': @@ -391,16 +403,53 @@ deps = { Var('swiftshader_git') + '/SwiftShader.git' + '@' + '5d1e8540407c138f47028d64684f3da599430aa4', 'src/third_party/angle': - Var('github_git') + '/google/angle.git' + '@' + 'f4e6ae915edaca2dd3b0efc555c1dbbb6b8abac4', + Var('github_git') + '/google/angle.git' + '@' + '06d194e2ae7b1d7e0eda0c0c911eff92dec7d3d1', + + 'src/third_party/angle/third_party/vulkan-deps/vulkan-headers/src': + Var('fuchsia_git') + '/third_party/Vulkan-Headers.git' + '@' + '5de4e8fab88ef0bd6994d9ddbcc864e3179b9e79', + + 'src/third_party/abseil-cpp': + Var('chromium_git') + '/chromium/src/third_party/abseil-cpp.git' + '@' + '2d8c1340f0350828f1287c4eaeebefcf317bcfc9', + + # Dart packages + 'src/third_party/pkg/archive': + Var('github_git') + '/brendan-duncan/archive.git' + '@' + '3.1.2', + + 'src/third_party/pkg/equatable': + Var('github_git') + '/felangel/equatable.git' + '@' + '0ba67c72db8bed75877fc1caafa74112ee0bd921', # 2.0.2 + + 'src/third_party/pkg/file': + Var('github_git') + '/google/file.dart.git' + '@' + '427bb20ccc852425d67f2880da2a9b4707c266b4', # 6.1.0 + + 'src/third_party/pkg/flutter_packages': + Var('github_git') + '/flutter/packages.git' + '@' + '5617d089f26dd52da3bf05c9fa4620ef11a7419b', # various + + 'src/third_party/pkg/gcloud': + Var('github_git') + '/dart-lang/gcloud.git' + '@' + '92a33a9d95ea94a4354b052a28b98088d660e0e7', # 0.8.0-dev + + 'src/third_party/pkg/googleapis': + Var('github_git') + '/google/googleapis.dart.git' + '@' + '07f01b7aa6985e4cafd0fd4b98724841bc9e85a1', # various + + 'src/third_party/pkg/platform': + Var('github_git') + '/google/platform.dart.git' + '@' + 'f63fd0bc3021354a0687dc935962c9acc003f47e', # 3.0.1 + + 'src/third_party/pkg/process': + Var('github_git') + '/google/process.dart.git' + '@' + '0c9aeac86dcc4e3a6cf760b76fed507107e244d5', # 4.2.1 + + 'src/third_party/pkg/process_runner': + Var('github_git') + '/google/process_runner.git' + '@' + 'd632ea0bfd814d779fcc53a361ed33eaf3620a0b', # 4.0.1 + + 'src/third_party/pkg/vector_math': + Var('github_git') + '/google/vector_math.dart.git' + '@' + 'v2.1.0', 'src/third_party/pkg/when': - Var('dart_git') + '/when.git' + '@' + '0.2.0', + Var('dart_git') + '/when.git' + '@' + '0.2.0', - 'src/third_party/android_tools/ndk': { + 'src/third_party/android_tools/ndk': { 'packages': [ { 'package': 'flutter/android/ndk/${{platform}}', - 'version': 'version:r21.0.6113669' + 'version': 'version:r22.0.7026061' } ], 'condition': 'download_android_deps', @@ -425,7 +474,7 @@ deps = { 'packages': [ { 'package': 'flutter/android/sdk/build-tools/${{platform}}', - 'version': 'version:30.0.1' + 'version': 'version:30.0.2' } ], 'condition': 'download_android_deps', @@ -465,11 +514,54 @@ deps = { 'dep_type': 'cipd', }, + 'src/third_party/android_tools/sdk/cmdline-tools': { + 'packages': [ + { + 'package': 'flutter/android/sdk/cmdline-tools', + 'version': 'latest', + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_tools/sdk/licenses': { + 'packages': [ + { + 'package': 'flutter/android/sdk/licenses', + 'version': 'latest', + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + 'src/third_party/android_embedding_dependencies': { 'packages': [ { 'package': 'flutter/android/embedding_bundle', - 'version': 'last_updated:2020-05-20T01:36:16-0700' + 'version': 'last_updated:2021-08-10T22:12:57-0700' + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + + 'src/third_party/web_dependencies': { + 'packages': [ + { + 'package': 'flutter/web/canvaskit_bundle', + 'version': Var('canvaskit_cipd_instance') + } + ], + 'dep_type': 'cipd', + }, + + 'src/third_party/java/openjdk': { + 'packages': [ + { + 'package': 'flutter/java/openjdk/${{platform}}', + 'version': 'version:11' } ], 'condition': 'download_android_deps', @@ -480,20 +572,33 @@ deps = { 'packages': [ { 'package': 'gn/gn/${{platform}}', - 'version': 'git_revision:1e3fd10c5df6b704fc764ee388149e4f32862823' + 'version': 'git_revision:24e2f7df92641de0351a96096fb2c490b2436bb8' }, ], 'dep_type': 'cipd', }, - 'src/buildtools/{host_os}-x64/clang': { + # Clang on mac and linux are expected to typically be the same revision. + # They are separated out so that the autoroller can more easily manage them. + 'src/buildtools/mac-x64/clang': { 'packages': [ { - 'package': 'fuchsia/third_party/clang/${{platform}}', - 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' + 'package': 'fuchsia/third_party/clang/mac-amd64', + 'version': 'GUxdhOlaKw1Xbg8SVnYiK3kDkTOQYAr-xInC_WKtsNUC' } ], - 'condition': 'host_os == "mac" or host_os == "linux"', + 'condition': 'host_os == "mac"', + 'dep_type': 'cipd', + }, + + 'src/buildtools/linux-x64/clang': { + 'packages': [ + { + 'package': 'fuchsia/third_party/clang/linux-amd64', + 'version': 'eDzL2utDIub9b8u_zmphLvcLIvA5H4IH31T4VG0f9kQC' + } + ], + 'condition': 'host_os == "linux"', 'dep_type': 'cipd', }, @@ -516,17 +621,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'gZ122oeKO8o63jCEfdrVDN8mIa9IIcta8bJSAAuVoHgC' - } - ], - 'condition': 'host_os == "mac"', - 'dep_type': 'cipd', - }, - 'src/fuchsia/toolchain/mac': { - 'packages': [ - { - 'package': 'fuchsia/clang/mac-amd64', - 'version': 'OzTZOKkICT0yD82Dbx0jvVn5hN5eOSi6ByVTDseE7i0C' + 'version': 'kAQ_HmUN5Gf-dk1tcRCO5Zd9ZzSJLkVwqANB_ymedUUC' } ], 'condition': 'host_os == "mac"', @@ -536,48 +631,45 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'CG6NCkBX9NVzB8GEXPIrAZfAF_rnSSAgvO0dFGp4P0wC' + 'version': '7UO7XyLyknwWMwBpxmbArfj2jTnW8qeV7_SiiXIBBlEC' } ], 'condition': 'host_os == "linux"', 'dep_type': 'cipd', }, - 'src/fuchsia/toolchain/linux': { - 'packages': [ - { - 'package': 'fuchsia/clang/linux-amd64', - 'version': 'OT6p30bQQhyCzRSy7xPsSbZ88J3PWOnneenkMZ0j7kIC' - } - ], - 'condition': 'host_os == "linux"', - 'dep_type': 'cipd', - }, -} -hooks = [ - { - # This clobbers when necessary (based on get_landmines.py). It must be the - # first hook so that other things that get/generate into the output - # directory will not subsequently be clobbered. - 'name': 'landmines', - 'pattern': '.', - 'action': [ - 'python', - 'src/build/landmines.py', + # Windows SDK + 'src/third_party/windows_sdk': { + 'packages': [ + { + 'package': 'flutter/windows/windows-sdk', + 'version': 'build:10.0.19041.0' + } ], + 'condition': 'download_windows_deps', + 'dep_type': 'cipd', }, + # CppWinRT tooling for UWP builds + 'src/third_party/cppwinrt': { + 'packages': [ + { + 'package': 'flutter/cppwinrt/win-amd64', + 'version': 'build:2.0.210505.3' + } + ], + 'condition': 'download_windows_deps', + 'dep_type': 'cipd', + }, + +} + +hooks = [ { # Update the Windows toolchain if necessary. 'name': 'win_toolchain', 'condition': 'download_windows_deps', 'pattern': '.', - 'action': ['python', 'src/build/vs_toolchain.py', 'update'], - }, - { - 'name': 'generate_package_files', - 'pattern': '.', - 'cwd': 'src/', - 'action': ['python', 'flutter/tools/generate_package_files.py'], + 'action': ['python3', 'src/build/vs_toolchain.py', 'update'], }, { # Ensure that we don't accidentally reference any .pyc files whose @@ -585,33 +677,17 @@ hooks = [ 'name': 'remove_stale_pyc_files', 'pattern': 'src/tools/.*\\.py', 'action': [ - 'python', + 'python3', 'src/tools/remove_stale_pyc_files.py', 'src/tools', ], }, - { - 'name': '7zip', - 'pattern': '.', - 'condition': 'download_windows_deps', - 'action': [ - 'download_from_google_storage', - '--no_auth', - '--no_resume', - '--bucket', - 'dart-dependencies', - '--platform=win32', - '--extract', - '-s', - 'src/third_party/dart/third_party/7zip.tar.gz.sha1', - ], - }, { 'name': 'dia_dll', 'pattern': '.', 'condition': 'download_windows_deps', 'action': [ - 'python', + 'python3', 'src/flutter/tools/dia_dll.py', ], }, @@ -620,7 +696,7 @@ hooks = [ 'pattern': '.', 'condition': 'download_linux_deps', 'action': [ - 'python', + 'python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=x64'], }, @@ -629,16 +705,43 @@ hooks = [ 'pattern': '.', 'condition': 'download_linux_deps', 'action': [ - 'python', + 'python3', 'src/build/linux/sysroot_scripts/install-sysroot.py', '--arch=arm64'], }, { - 'name': 'dart package config', + 'name': 'pub get --offline', + 'pattern': '.', + 'action': [ + 'python3', + 'src/flutter/tools/pub_get_offline.py', + ] + }, + { + # This must run whenever the cppwinrt dependency is updated + # to regenerate winrt headers + 'name': 'Generate winrt headers', + 'pattern': '.', + 'condition': 'download_windows_deps', + 'action': [ + 'python3', + 'src/build/win/generate_winrt_headers.py', + ] + }, + { + 'name': 'Download prebuilt Dart SDK', + 'pattern': '.', + 'action': [ + 'python3', + 'src/flutter/tools/download_dart_sdk.py', + ] + }, + { + 'name': 'Setup githooks', 'pattern': '.', 'action': [ - 'python', - 'src/flutter/tools/run_third_party_dart.py', + 'python3', + 'src/flutter/tools/githooks/setup.py', ] } ] diff --git a/README.md b/README.md index c271701f1ecd0..5dcb6503d6d34 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,10 @@ Flutter Engine [![Build Status - Cirrus][]][Build status] -Flutter is Google's mobile app SDK for crafting high-quality native interfaces -in record time. Flutter works with existing code, is used by developers and -organizations around the world, and is free and open source. +Flutter is Google's SDK for crafting beautiful, fast user experiences for +mobile, web, and desktop from a single codebase. Flutter works with existing +code, is used by developers and organizations around the world, and is free +and open source. The Flutter Engine is a portable runtime for hosting [Flutter](https://flutter.dev) applications. It implements Flutter's core @@ -30,5 +31,6 @@ Information on how to get started can be found at our [contributor guide](CONTRIBUTING.md). [Build Status - Cirrus]: https://api.cirrus-ci.com/github/flutter/engine.svg?branch=master + [Build status]: https://cirrus-ci.com/github/flutter/engine diff --git a/analysis_options.yaml b/analysis_options.yaml index a92f9fb784c8f..13adc0083294f 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,24 +1,22 @@ # Specify analysis options. # -# This file is a copy of analysis_options_repo.yaml from flutter repo -# as of 2018-05-30, but with: +# This file is a copy of analysis_options.yaml from flutter repo +# as of 2021-03-19, but with: # - "always_require_non_null_named_parameters" disabled (because we -# can't import the meta package from the SDK), and +# can't import the meta package from the SDK), +# - "avoid_equals_and_hash_code_on_mutable_classes" disabled (same reason), +# - "missing_whitespace_between_adjacent_strings" disabled (too many false +# positives), # - "sort_constructors_first" disabled (because we have private fake # constructors), # - "prefer_final_fields" disabled (because we do weird things with -# private fields, especially on the Window object): +# private fields, especially on the PlatformDispatcher object), +# - "public_member_api_docs" enabled. analyzer: - enable-experiment: - - non-nullable - exclude: [ - # this test pretends to be part of dart:ui and results in lots of false - # positives. - testing/dart/window_hooks_integration_test.dart, + exclude: # Fixture depends on dart:ui and raises false positives. - flutter_frontend_server/test/fixtures/lib/main.dart - ] + - flutter_frontend_server/test/fixtures/lib/main.dart strong-mode: implicit-casts: false implicit-dynamic: false @@ -32,9 +30,8 @@ analyzer: todo: ignore # allow dart:ui to import dart:_internal import_internal_library: ignore - # `flutter analyze` (without `--watch`) just ignores directories - # that contain a .dartignore file, and this file does not have any - # effect on what files are actually analyzed. + # Turned off until null-safe rollout is complete. + unnecessary_null_comparison: ignore linter: rules: @@ -44,56 +41,89 @@ linter: - always_declare_return_types - always_put_control_body_on_new_line # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 - # always_require_non_null_named_parameters # DIFFERENT FROM FLUTTER/FLUTTER + # - always_require_non_null_named_parameters # DIFFERENT FROM FLUTTER/FLUTTER - always_specify_types + # - always_use_package_imports # we do this commonly - annotate_overrides # - avoid_annotating_with_dynamic # conflicts with always_specify_types - # - avoid_as # required for implicit-casts: false - # - avoid_bool_literals_in_conditional_expressions # not yet tested + - avoid_bool_literals_in_conditional_expressions # - avoid_catches_without_on_clauses # we do this commonly # - avoid_catching_errors # we do this commonly - avoid_classes_with_only_static_members # We want to avoid classes that can be instantiated but only have statics + # - avoid_double_and_int_checks # only useful when targeting JS runtime + # - avoid_dynamic_calls # not yet tested - avoid_empty_else + # - avoid_equals_and_hash_code_on_mutable_classes # DIFFERENT FROM FLUTTER/FLUTTER + # - avoid_escaping_inner_quotes # not yet tested + - avoid_field_initializers_in_const_classes - avoid_function_literals_in_foreach_calls + # - avoid_implementing_value_types # not yet tested - avoid_init_to_null + # - avoid_js_rounded_ints # only useful when targeting JS runtime - avoid_null_checks_in_equality_operators # - avoid_positional_boolean_parameters # not yet tested + # - avoid_print # not yet tested # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) + # - avoid_redundant_argument_values # not yet tested - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters - # - avoid_returning_null # we do this commonly - # - avoid_returning_this # https://github.com/dart-lang/linter/issues/842 + # - avoid_returning_null # there are plenty of valid reasons to return null + # - avoid_returning_null_for_future # not yet tested + - avoid_returning_null_for_void + # - avoid_returning_this # there are plenty of valid reasons to return this # - avoid_setters_without_getters # not yet tested - # - avoid_single_cascade_in_expression_statements # not yet tested + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements - avoid_slow_async_io - # - avoid_types_as_parameter_names # https://github.com/dart-lang/linter/pull/954/files + - avoid_type_to_string + - avoid_types_as_parameter_names # - avoid_types_on_closure_parameters # conflicts with always_specify_types - # - avoid_unused_constructor_parameters # https://github.com/dart-lang/linter/pull/847 + - avoid_unnecessary_containers + - avoid_unused_constructor_parameters + - avoid_void_async + # - avoid_web_libraries_in_flutter # not yet tested - await_only_futures + - camel_case_extensions - camel_case_types - cancel_subscriptions # - cascade_invocations # not yet tested - # - close_sinks # https://github.com/flutter/flutter/issues/5789 - # - comment_references # blocked on https://github.com/dart-lang/dartdoc/issues/1153 - # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 + - cast_nullable_to_non_nullable + # - close_sinks # not reliable enough + # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142 + # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally + # - curly_braces_in_flow_control_structures # not required by flutter style + - deprecated_consistency + # - diagnostic_describe_all_properties # not yet tested - directives_ordering + # - do_not_use_environment # we do this commonly - empty_catches - empty_constructor_bodies - empty_statements + - exhaustive_cases + - file_names + - flutter_style_todos - hash_and_equals - implementation_imports - # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 + # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811 - iterable_contains_unrelated_type - # - join_return_with_assignment # not yet tested + # - join_return_with_assignment # not required by flutter style + - leading_newlines_in_multiline_strings - library_names - library_prefixes + # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type - # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 + # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 + # - missing_whitespace_between_adjacent_strings # DIFFERENT FROM FLUTTER/FLUTTER - no_adjacent_strings_in_list + # - no_default_cases # too many false positives - no_duplicate_case_values + - no_logic_in_create_state + # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names + - null_check_on_nullable_type_parameter + - null_closures # - omit_local_variable_types # opposite of always_specify_types # - one_member_abstracts # too many false positives # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 @@ -104,51 +134,88 @@ linter: # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists + # - prefer_asserts_with_message # not required by flutter style - prefer_collection_literals - prefer_conditional_assignment - prefer_const_constructors - prefer_const_constructors_in_immutables - prefer_const_declarations - prefer_const_literals_to_create_immutables - # - prefer_constructors_over_static_methods # not yet tested + # - prefer_constructors_over_static_methods # far too many false positives - prefer_contains + # - prefer_double_quotes # opposite of prefer_single_quotes - prefer_equal_for_default_values # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods # - prefer_final_fields # DIFFERENT FROM FLUTTER/FLUTTER + - prefer_final_in_for_each - prefer_final_locals + - prefer_for_elements_to_map_fromIterable - prefer_foreach - # - prefer_function_declarations_over_variables # not yet tested + - prefer_function_declarations_over_variables - prefer_generic_function_type_aliases + - prefer_if_elements_to_conditional_expressions + - prefer_if_null_operators - prefer_initializing_formals - # - prefer_interpolation_to_compose_strings # not yet tested + - prefer_inlined_adds + # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants + # - prefer_interpolation_to_compose_strings # doesn't work with raw strings, see https://github.com/dart-lang/linter/issues/2490 - prefer_is_empty - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_whereType + # - prefer_mixin # https://github.com/dart-lang/language/issues/32 + - prefer_null_aware_operators + # - prefer_relative_imports # incompatible with sub-package imports - prefer_single_quotes + - prefer_spread_collections - prefer_typing_uninitialized_variables - - public_member_api_docs # this is the only difference from analysis_options.yaml + - prefer_void_to_null + - provide_deprecation_message + - public_member_api_docs # DIFFERENT FROM FLUTTER/FLUTTER - recursive_getters + - sized_box_for_whitespace - slash_for_doc_comments + # - sort_child_properties_last # not yet tested # - sort_constructors_first # DIFFERENT FROM FLUTTER/FLUTTER + # - sort_pub_dependencies # prevents separating pinned transitive dependencies - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally + - tighten_type_of_initializing_formals # - type_annotate_public_apis # subset of always_specify_types - type_init_formals - # - unawaited_futures # https://github.com/flutter/flutter/issues/5793 + # - unawaited_futures # too many false positives + - unnecessary_await_in_return - unnecessary_brace_in_string_interps - unnecessary_const + # - unnecessary_final # conflicts with prefer_final_locals - unnecessary_getters_setters - # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498 + # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 - unnecessary_new - unnecessary_null_aware_assignments + # - unnecessary_null_checks # not yet tested - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations - unnecessary_overrides - unnecessary_parenthesis - # - unnecessary_statements # not yet tested + # - unnecessary_raw_strings # not yet tested + - unnecessary_statements + - unnecessary_string_escapes + - unnecessary_string_interpolations - unnecessary_this - unrelated_type_equality_checks + # - unsafe_html # not yet tested + - use_full_hex_values_for_flutter_colors + - use_function_type_syntax_for_parameters + # - use_if_null_to_convert_nulls_to_bools # not yet tested + - use_is_even_rather_than_modulo + - use_key_in_widget_constructors + # - use_late_for_private_fields_and_variables # not yet tested + # - use_named_constants # not yet tested + - use_raw_strings - use_rethrow_when_possible # - use_setters_to_change_properties # not yet tested - # - use_string_buffers # https://github.com/dart-lang/linter/pull/664 + # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review - valid_regexps + - void_checks diff --git a/assets/asset_manager.cc b/assets/asset_manager.cc index 60d169a31ebb2..d52fc6eea7f2c 100644 --- a/assets/asset_manager.cc +++ b/assets/asset_manager.cc @@ -29,6 +29,34 @@ void AssetManager::PushBack(std::unique_ptr resolver) { resolvers_.push_back(std::move(resolver)); } +void AssetManager::UpdateResolverByType( + std::unique_ptr updated_asset_resolver, + AssetResolver::AssetResolverType type) { + if (updated_asset_resolver == nullptr) { + return; + } + bool updated = false; + std::deque> new_resolvers; + for (auto& old_resolver : resolvers_) { + if (!updated && old_resolver->GetType() == type) { + // Push the replacement updated resolver in place of the old_resolver. + new_resolvers.push_back(std::move(updated_asset_resolver)); + updated = true; + } else { + new_resolvers.push_back(std::move(old_resolver)); + } + } + // Append resolver to the end if not used as a replacement. + if (!updated) { + new_resolvers.push_back(std::move(updated_asset_resolver)); + } + resolvers_.swap(new_resolvers); +} + +std::deque> AssetManager::TakeResolvers() { + return std::move(resolvers_); +} + // |AssetResolver| std::unique_ptr AssetManager::GetAsMapping( const std::string& asset_name) const { @@ -47,9 +75,38 @@ std::unique_ptr AssetManager::GetAsMapping( return nullptr; } +// |AssetResolver| +std::vector> AssetManager::GetAsMappings( + const std::string& asset_pattern, + const std::optional& subdir) const { + std::vector> mappings; + if (asset_pattern.size() == 0) { + return mappings; + } + TRACE_EVENT1("flutter", "AssetManager::GetAsMappings", "pattern", + asset_pattern.c_str()); + for (const auto& resolver : resolvers_) { + auto resolver_mappings = resolver->GetAsMappings(asset_pattern, subdir); + mappings.insert(mappings.end(), + std::make_move_iterator(resolver_mappings.begin()), + std::make_move_iterator(resolver_mappings.end())); + } + return mappings; +} + // |AssetResolver| bool AssetManager::IsValid() const { return resolvers_.size() > 0; } +// |AssetResolver| +bool AssetManager::IsValidAfterAssetManagerChange() const { + return false; +} + +// |AssetResolver| +AssetResolver::AssetResolverType AssetManager::GetType() const { + return AssetResolverType::kAssetManager; +} + } // namespace flutter diff --git a/assets/asset_manager.h b/assets/asset_manager.h index 2278742f50113..335bd84116365 100644 --- a/assets/asset_manager.h +++ b/assets/asset_manager.h @@ -9,6 +9,7 @@ #include #include +#include #include "flutter/assets/asset_resolver.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" @@ -25,13 +26,54 @@ class AssetManager final : public AssetResolver { void PushBack(std::unique_ptr resolver); + //-------------------------------------------------------------------------- + /// @brief Replaces an asset resolver of the specified `type` with + /// `updated_asset_resolver`. The matching AssetResolver is + /// removed and replaced with `updated_asset_resolvers`. + /// + /// AssetResolvers should be updated when the existing resolver + /// becomes obsolete and a newer one becomes available that + /// provides updated access to the same type of assets as the + /// existing one. This update process is meant to be performed + /// at runtime. + /// + /// If a null resolver is provided, nothing will be done. If no + /// matching resolver is found, the provided resolver will be + /// added to the end of the AssetManager resolvers queue. The + /// replacement only occurs with the first matching resolver. + /// Any additional matching resolvers are untouched. + /// + /// @param[in] updated_asset_resolver The asset resolver to replace the + /// resolver of matching type with. + /// + /// @param[in] type The type of AssetResolver to update. Only resolvers of + /// the specified type will be replaced by the updated + /// resolver. + /// + void UpdateResolverByType( + std::unique_ptr updated_asset_resolver, + AssetResolver::AssetResolverType type); + + std::deque> TakeResolvers(); + // |AssetResolver| bool IsValid() const override; + // |AssetResolver| + bool IsValidAfterAssetManagerChange() const override; + + // |AssetResolver| + AssetResolver::AssetResolverType GetType() const override; + // |AssetResolver| std::unique_ptr GetAsMapping( const std::string& asset_name) const override; + // |AssetResolver| + std::vector> GetAsMappings( + const std::string& asset_pattern, + const std::optional& subdir) const override; + private: std::deque> resolvers_; diff --git a/assets/asset_resolver.h b/assets/asset_resolver.h index b6cdb88b84ac0..4bf1eeb23e6f1 100644 --- a/assets/asset_resolver.h +++ b/assets/asset_resolver.h @@ -8,6 +8,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" @@ -19,11 +20,67 @@ class AssetResolver { virtual ~AssetResolver() = default; + //---------------------------------------------------------------------------- + /// @brief Identifies the type of AssetResolver an instance is. + /// + enum AssetResolverType { + kAssetManager, + kApkAssetProvider, + kDirectoryAssetBundle + }; + virtual bool IsValid() const = 0; + //---------------------------------------------------------------------------- + /// @brief Certain asset resolvers are still valid after the asset + /// manager is replaced before a hot reload, or after a new run + /// configuration is created during a hot restart. By preserving + /// these resolvers and re-inserting them into the new resolver or + /// run configuration, the tooling can avoid needing to sync all + /// application assets through the Dart devFS upon connecting to + /// the VM Service. Besides improving the startup performance of + /// running a Flutter application, it also reduces the occurrence + /// of tool failures due to repeated network flakes caused by + /// damaged cables or hereto unknown bugs in the Dart HTTP server + /// implementation. + /// + /// @return Returns whether this resolver is valid after the asset manager + /// or run configuration is updated. + /// + virtual bool IsValidAfterAssetManagerChange() const = 0; + + //---------------------------------------------------------------------------- + /// @brief Gets the type of AssetResolver this is. Types are defined in + /// AssetResolverType. + /// + /// @return Returns the AssetResolverType that this resolver is. + /// + virtual AssetResolverType GetType() const = 0; + [[nodiscard]] virtual std::unique_ptr GetAsMapping( const std::string& asset_name) const = 0; + //-------------------------------------------------------------------------- + /// @brief Same as GetAsMapping() but returns mappings for all files + /// who's name matches a given pattern. Returns empty vector + /// if no matching assets are found. + /// + /// @param[in] asset_pattern The pattern to match file names against. + /// + /// @param[in] subdir Optional subdirectory in which to search for files. + /// If supplied this function does a flat search within the + /// subdirectory instead of a recursive search through the entire + /// assets directory. + /// + /// @return Returns a vector of mappings of files which match the search + /// parameters. + /// + [[nodiscard]] virtual std::vector> + GetAsMappings(const std::string& asset_pattern, + const std::optional& subdir) const { + return {}; + }; + private: FML_DISALLOW_COPY_AND_ASSIGN(AssetResolver); }; diff --git a/assets/directory_asset_bundle.cc b/assets/directory_asset_bundle.cc index 5ad7297313c99..7896756614593 100644 --- a/assets/directory_asset_bundle.cc +++ b/assets/directory_asset_bundle.cc @@ -4,19 +4,24 @@ #include "flutter/assets/directory_asset_bundle.h" +#include #include #include "flutter/fml/eintr_wrapper.h" #include "flutter/fml/file.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/trace_event.h" namespace flutter { -DirectoryAssetBundle::DirectoryAssetBundle(fml::UniqueFD descriptor) +DirectoryAssetBundle::DirectoryAssetBundle( + fml::UniqueFD descriptor, + bool is_valid_after_asset_manager_change) : descriptor_(std::move(descriptor)) { if (!fml::IsDirectory(descriptor_)) { return; } + is_valid_after_asset_manager_change_ = is_valid_after_asset_manager_change; is_valid_ = true; } @@ -27,6 +32,16 @@ bool DirectoryAssetBundle::IsValid() const { return is_valid_; } +// |AssetResolver| +bool DirectoryAssetBundle::IsValidAfterAssetManagerChange() const { + return is_valid_after_asset_manager_change_; +} + +// |AssetResolver| +AssetResolver::AssetResolverType DirectoryAssetBundle::GetType() const { + return AssetResolver::AssetResolverType::kDirectoryAssetBundle; +} + // |AssetResolver| std::unique_ptr DirectoryAssetBundle::GetAsMapping( const std::string& asset_name) const { @@ -45,4 +60,54 @@ std::unique_ptr DirectoryAssetBundle::GetAsMapping( return mapping; } +std::vector> DirectoryAssetBundle::GetAsMappings( + const std::string& asset_pattern, + const std::optional& subdir) const { + std::vector> mappings; + if (!is_valid_) { + FML_DLOG(WARNING) << "Asset bundle was not valid."; + return mappings; + } + + std::regex asset_regex(asset_pattern); + fml::FileVisitor visitor = [&](const fml::UniqueFD& directory, + const std::string& filename) { + TRACE_EVENT0("flutter", "DirectoryAssetBundle::GetAsMappings FileVisitor"); + + if (std::regex_match(filename, asset_regex)) { + TRACE_EVENT0("flutter", "Matched File"); + + fml::UniqueFD fd = fml::OpenFile(directory, filename.c_str(), false, + fml::FilePermission::kRead); + + if (fml::IsDirectory(fd)) { + return true; + } + + auto mapping = std::make_unique(fd); + + if (mapping && mapping->IsValid()) { + mappings.push_back(std::move(mapping)); + } else { + FML_LOG(ERROR) << "Mapping " << filename << " failed"; + } + } + return true; + }; + if (!subdir) { + fml::VisitFilesRecursively(descriptor_, visitor); + } else { + fml::UniqueFD subdir_fd = + fml::OpenFileReadOnly(descriptor_, subdir.value().c_str()); + if (!fml::IsDirectory(subdir_fd)) { + FML_LOG(ERROR) << "Subdirectory path " << subdir.value() + << " is not a directory"; + return mappings; + } + fml::VisitFiles(subdir_fd, visitor); + } + + return mappings; +} + } // namespace flutter diff --git a/assets/directory_asset_bundle.h b/assets/directory_asset_bundle.h index 0a0a94c7aba15..3621300f39954 100644 --- a/assets/directory_asset_bundle.h +++ b/assets/directory_asset_bundle.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_ASSETS_DIRECTORY_ASSET_BUNDLE_H_ #define FLUTTER_ASSETS_DIRECTORY_ASSET_BUNDLE_H_ +#include #include "flutter/assets/asset_resolver.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" @@ -14,21 +15,34 @@ namespace flutter { class DirectoryAssetBundle : public AssetResolver { public: - explicit DirectoryAssetBundle(fml::UniqueFD descriptor); + DirectoryAssetBundle(fml::UniqueFD descriptor, + bool is_valid_after_asset_manager_change); ~DirectoryAssetBundle() override; private: const fml::UniqueFD descriptor_; bool is_valid_ = false; + bool is_valid_after_asset_manager_change_ = false; // |AssetResolver| bool IsValid() const override; + // |AssetResolver| + bool IsValidAfterAssetManagerChange() const override; + + // |AssetResolver| + AssetResolver::AssetResolverType GetType() const override; + // |AssetResolver| std::unique_ptr GetAsMapping( const std::string& asset_name) const override; + // |AssetResolver| + std::vector> GetAsMappings( + const std::string& asset_pattern, + const std::optional& subdir) const override; + FML_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle); }; diff --git a/benchmarking/benchmarking.cc b/benchmarking/benchmarking.cc index 97b7f868e335a..d364fc96870fd 100644 --- a/benchmarking/benchmarking.cc +++ b/benchmarking/benchmarking.cc @@ -5,14 +5,18 @@ #include "benchmarking.h" #include "flutter/fml/backtrace.h" +#include "flutter/fml/command_line.h" #include "flutter/fml/icu_util.h" namespace benchmarking { int Main(int argc, char** argv) { fml::InstallCrashHandler(); + fml::CommandLine cmd = fml::CommandLineFromArgcArgv(argc, argv); benchmark::Initialize(&argc, argv); - fml::icu::InitializeICU("icudtl.dat"); + std::string icudtl_path = + cmd.GetOptionValueWithDefault("icu-data-file-path", "icudtl.dat"); + fml::icu::InitializeICU(icudtl_path); ::benchmark::RunSpecifiedBenchmarks(); return 0; } diff --git a/build/concurrent_jobs.gni b/build/concurrent_jobs.gni new file mode 100644 index 0000000000000..a930b25568276 --- /dev/null +++ b/build/concurrent_jobs.gni @@ -0,0 +1,12 @@ +# 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. + +_script = "//flutter/build/get_concurrent_jobs.py" +_args = [ + "--reserve-memory=1GB", + "--memory-per-job", + "dart=1GB", +] + +concurrent_jobs = exec_script(_script, _args, "json", [ _script ]) diff --git a/build/copy_info_plist.py b/build/copy_info_plist.py index a87e1a8c98867..f393039121eb2 100644 --- a/build/copy_info_plist.py +++ b/build/copy_info_plist.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -13,9 +13,9 @@ usage: copy_info_plist.py --bitcode= """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function + + + import subprocess import sys diff --git a/build/dart/BUILD.gn b/build/dart/BUILD.gn new file mode 100644 index 0000000000000..a52bb11e409d7 --- /dev/null +++ b/build/dart/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/build/concurrent_jobs.gni") + +declare_args() { + # Maximum number of Dart processes to run in parallel. + # + # To avoid out-of-memory errors we explicitly reduce the number of jobs. + concurrent_dart_jobs = concurrent_jobs.dart +} + +pool("dart_pool") { + depth = concurrent_dart_jobs +} diff --git a/build/dart/rules.gni b/build/dart/rules.gni index dcafa6caf4617..3ffe4d4aaab47 100644 --- a/build/dart/rules.gni +++ b/build/dart/rules.gni @@ -2,10 +2,171 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# This file has rules for making Dart packages and Dart-based Mojo applications. -# The entrypoint is the dart_pkg rule. +# This file has rules for making Dart packages and snapshots. +import("//build/compiled_action.gni") import("//build/module_args/dart.gni") +import("//flutter/common/config.gni") +import("//third_party/dart/build/dart/dart_action.gni") + +# Creates a dart kernel (dill) file suitable for use with gen_snapshot, as well +# as the app-jit, aot-elf, or aot-assembly snapshot for Android or iOS. +# +# Invoker must supply dart_main and package_config. Invoker may optionally +# supply aot as a boolean and product as a boolean. +template("dart_snapshot") { + assert(!is_fuchsia) + assert(defined(invoker.main_dart), "main_dart is a required parameter.") + assert(defined(invoker.package_config), + "package_config is a required parameter.") + + kernel_target = "_${target_name}_kernel" + snapshot_target = "_${target_name}_snapshot" + is_aot = + flutter_runtime_mode == "profile" || flutter_runtime_mode == "release" + + kernel_output = "$target_gen_dir/kernel_blob.bin" + + prebuilt_dart_action(kernel_target) { + script = "//flutter/flutter_frontend_server/bin/starter.dart" + + main_dart = rebase_path(invoker.main_dart) + package_config = rebase_path(invoker.package_config) + flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") + + deps = [ "//flutter/lib/snapshot:strong_platform" ] + + inputs = [ + main_dart, + package_config, + ] + + outputs = [ kernel_output ] + + depfile = "$kernel_output.d" + abs_depfile = rebase_path(depfile) + rebased_output = rebase_path(kernel_output, root_build_dir) + vm_args = [ + "--depfile=$abs_depfile", + "--depfile_output_filename=$rebased_output", + "--disable-dart-dev", + ] + + args = [ + "--packages=" + rebase_path(package_config), + "--target=flutter", + "--sdk-root=" + flutter_patched_sdk, + "--output-dill=" + rebase_path(kernel_output), + ] + + if (is_aot) { + args += [ + "--aot", + "--tfa", + ] + } else { + # --no-link-platform is only valid when --aot isn't specified + args += [ "--no-link-platform" ] + } + + if (defined(invoker.product) && invoker.product) { + # Setting this flag in a non-product release build for AOT (a "profile" + # build) causes the vm service isolate code to be tree-shaken from an app. + # See the pragma on the entrypoint here: + # + # https://github.com/dart-lang/sdk/blob/master/sdk/lib/_internal/vm/bin/vmservice_io.dart#L240 + # + # Also, this define excludes debugging and profiling code from Flutter. + args += [ "-Ddart.vm.product=true" ] + } else { + if (!is_debug) { + # The following define excludes debugging code from Flutter. + args += [ "-Ddart.vm.profile=true" ] + } + } + + args += [ rebase_path(main_dart) ] + } + + compiled_action(snapshot_target) { + if (target_cpu == "x86" && host_os == "linux") { + # By default Dart will create a 32-bit gen_snapshot host binary if the target + # platform is 32-bit. Override this to create a 64-bit gen_snapshot for x86 + # targets because some host platforms may not support 32-bit binaries. + tool = "//third_party/dart/runtime/bin:gen_snapshot_host_targeting_host" + toolchain = "//build/toolchain/$host_os:clang_x64" + } else { + tool = "//third_party/dart/runtime/bin:gen_snapshot" + } + + inputs = [ kernel_output ] + deps = [ ":$kernel_target" ] + outputs = [] + + args = [ "--lazy_async_stacks" ] + + if (is_debug && flutter_runtime_mode != "profile" && + flutter_runtime_mode != "release" && + flutter_runtime_mode != "jit_release") { + args += [ "--enable_asserts" ] + } + + if (is_aot) { + args += [ "--deterministic" ] + if (is_ios) { + snapshot_assembly = "$target_gen_dir/ios/snapshot_assembly.S" + outputs += [ snapshot_assembly ] + args += [ + "--snapshot_kind=app-aot-assembly", + "--assembly=" + rebase_path(snapshot_assembly), + ] + } else if (is_android) { + libapp = "$target_gen_dir/android/libs/$android_app_abi/libapp.so" + outputs += [ libapp ] + args += [ + "--snapshot_kind=app-aot-elf", + "--elf=" + rebase_path(libapp), + ] + } else { + assert(false) + } + } else { + deps += [ "//flutter/lib/snapshot:generate_snapshot_bin" ] + vm_snapshot_data = + "$root_gen_dir/flutter/lib/snapshot/vm_isolate_snapshot.bin" + snapshot_data = "$root_gen_dir/flutter/lib/snapshot/isolate_snapshot.bin" + isolate_snapshot_data = "$target_gen_dir/isolate_snapshot_data" + isolate_snapshot_instructions = "$target_gen_dir/isolate_snapshot_instr" + + inputs += [ + vm_snapshot_data, + snapshot_data, + ] + + outputs += [ + isolate_snapshot_data, + isolate_snapshot_instructions, + ] + args += [ + "--snapshot_kind=app-jit", + "--load_vm_snapshot_data=" + rebase_path(vm_snapshot_data), + "--load_isolate_snapshot_data=" + rebase_path(snapshot_data), + "--isolate_snapshot_data=" + rebase_path(isolate_snapshot_data), + "--isolate_snapshot_instructions=" + + rebase_path(isolate_snapshot_instructions), + ] + } + + args += [ rebase_path(kernel_output) ] + } + + group(target_name) { + public_deps = [ + ":$kernel_target", + ":$snapshot_target", + ] + } +} template("dart_pkg_helper") { assert(defined(invoker.package_name)) diff --git a/build/dart/tools/dart_package_name.py b/build/dart/tools/dart_package_name.py index 255ef5be7a13a..db8c9e93193ed 100755 --- a/build/dart/tools/dart_package_name.py +++ b/build/dart/tools/dart_package_name.py @@ -1,4 +1,5 @@ -#!/usr/bin/python +#!/usr/bin/env python3 +# # 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. diff --git a/build/dart/tools/dart_pkg.py b/build/dart/tools/dart_pkg.py index 18d2e6951faf8..09c7403eab757 100755 --- a/build/dart/tools/dart_pkg.py +++ b/build/dart/tools/dart_pkg.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -81,7 +81,7 @@ def copy(from_root, to_root, filter_func=None): os.makedirs(to_dir) shutil.copy(from_path, to_path) - dirs[:] = filter(wrapped_filter, dirs) + dirs[:] = list(filter(wrapped_filter, dirs)) def copy_or_link(from_root, to_root, filter_func=None): @@ -114,7 +114,7 @@ def list_files(from_root, filter_func=None): for name in filter(wrapped_filter, files): path = os.path.join(root, name) file_list.append(path) - dirs[:] = filter(wrapped_filter, dirs) + dirs[:] = list(filter(wrapped_filter, dirs)) return file_list diff --git a/build/generate_coverage.py b/build/generate_coverage.py index 6286d30a43612..4beecddb068f5 100755 --- a/build/generate_coverage.py +++ b/build/generate_coverage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -49,6 +49,8 @@ def main(): required=True, help='The output directory for coverage results.') parser.add_argument('-f', '--format', type=str, choices=['all', 'html', 'summary', 'lcov'], required=True, help='The type of coverage information to be displayed.') + parser.add_argument('-a', '--args', nargs='+', dest='test_args', + required=False, help='The arguments to pass to the unit test executable being run.') args = parser.parse_args() @@ -64,20 +66,34 @@ def main(): # Run all unit tests and collect raw profiles. for test in args.tests: absolute_test_path = os.path.abspath(test) + absolute_test_dir = os.path.dirname(absolute_test_path) + test_name = os.path.basename(absolute_test_path) if not os.path.exists(absolute_test_path): print("Path %s does not exist." % absolute_test_path) return -1 - binaries.append(absolute_test_path) + unstripped_test_path = os.path.join(absolute_test_dir, "exe.unstripped", test_name) + + if os.path.exists(unstripped_test_path): + binaries.append(unstripped_test_path) + else: + binaries.append(absolute_test_path) raw_profile = absolute_test_path + ".rawprofile" RemoveIfExists(raw_profile) - print "Running test %s to gather profile." % os.path.basename(absolute_test_path) + print("Running test %s to gather profile." % os.path.basename(absolute_test_path)) + + test_command = [absolute_test_path] + + test_args = ' '.join(args.test_args).split() + + if test_args is not None: + test_command += test_args - subprocess.check_call([absolute_test_path], env={ + subprocess.check_call(test_command, env={ "LLVM_PROFILE_FILE": raw_profile }) diff --git a/build/get_concurrent_jobs.py b/build/get_concurrent_jobs.py new file mode 100755 index 0000000000000..589937caa716a --- /dev/null +++ b/build/get_concurrent_jobs.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# Copyright 2019 The Fuchsia Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This script computes the number of concurrent jobs that can run in the +# build as a function of the machine. It accepts a set of key value pairs +# given by repeated --memory-per-job arguments. For example: +# +# $ get_concurrent_jobs.py --memory-per-job dart=1GB +# +# The result is a json map printed to stdout that gives the number of +# concurrent jobs allowed of each kind. For example: +# +# {"dart": 8} +# +# Some memory can be held out of the calculation with the --reserve-memory flag. + +import argparse +import ctypes +import json +import multiprocessing +import os +import re +import subprocess +import sys + +UNITS = {'B': 1, 'KB': 2**10, 'MB': 2**20, 'GB': 2**30, 'TB': 2**40} + + +# See https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex +# and https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex +class MEMORYSTATUSEX(ctypes.Structure): + _fields_ = [ + ("dwLength", ctypes.c_ulong), + ("dwMemoryLoad", ctypes.c_ulong), + ("ullTotalPhys", ctypes.c_ulonglong), + ("ullAvailPhys", ctypes.c_ulonglong), + ("ullTotalPageFile", ctypes.c_ulonglong), + ("ullAvailPageFile", ctypes.c_ulonglong), + ("ullTotalVirtual", ctypes.c_ulonglong), + ("ullAvailVirtual", ctypes.c_ulonglong), + ("sullAvailExtendedVirtual", ctypes.c_ulonglong), + ] + + +def GetTotalMemory(): + if sys.platform in ('win32', 'cygwin'): + stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX)) + success = ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)) + return stat.ullTotalPhys if success else 0 + elif sys.platform.startswith('linux'): + if os.path.exists("/proc/meminfo"): + with open("/proc/meminfo") as meminfo: + memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB') + for line in meminfo: + match = memtotal_re.match(line) + if match: + return float(match.group(1)) * 2**10 + elif sys.platform == 'darwin': + try: + return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize'])) + except Exception: + return 0 + else: + return 0 + + +def ParseSize(string): + i = next(i for (i, c) in enumerate(string) if not c.isdigit()) + number = string[:i].strip() + unit = string[i:].strip() + return int(float(number) * UNITS[unit]) + + +class ParseSizeAction(argparse.Action): + + def __call__(self, parser, args, values, option_string=None): + sizes = getattr(args, self.dest, []) + for value in values: + (k, v) = value.split('=', 1) + sizes.append((k, ParseSize(v))) + setattr(args, self.dest, sizes) + + +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--memory-per-job', + action=ParseSizeAction, + default=[], + nargs='*', + help='Key value pairings (dart=1GB) giving an estimate of the amount of ' + 'memory needed for the class of job.') + parser.add_argument( + '--reserve-memory', + type=ParseSize, + default=0, + help='The amount of memory to be held out of the amount for jobs to use.') + args = parser.parse_args() + + total_memory = GetTotalMemory() + + # Ensure the total memory used in the calculation below is at least 0 + mem_total_bytes = max(0, total_memory - args.reserve_memory) + + # Ensure the number of cpus used in the calculation below is at least 1 + try: + cpu_cap = multiprocessing.cpu_count() + except: + cpu_cap = 1 + + concurrent_jobs = {} + for job, memory_per_job in args.memory_per_job: + # Calculate the number of jobs that will fit in memory. Ensure the + # value is at least 1. + num_concurrent_jobs = int(max(1, mem_total_bytes / memory_per_job)) + # Cap the number of jobs by the number of cpus available. + concurrent_jobs[job] = min(num_concurrent_jobs, cpu_cap) + + print(json.dumps(concurrent_jobs)) + + return 0 + + +if __name__ == '__main__': + sys.exit(Main()) diff --git a/build/git_revision.py b/build/git_revision.py index 5ee807c3abad5..cbc82d7de898c 100755 --- a/build/git_revision.py +++ b/build/git_revision.py @@ -1,13 +1,13 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # 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. """Get the Git HEAD revision of a specified Git repository.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function + + + import sys import subprocess @@ -27,7 +27,7 @@ def GetRepositoryVersion(repository): 'HEAD', ]) - return version.strip() + return str(version.strip(), 'utf-8') def main(): parser = argparse.ArgumentParser() diff --git a/build/zip.py b/build/zip.py index b257a7ad3c53c..0bcbc02fc9006 100755 --- a/build/zip.py +++ b/build/zip.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# # 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. diff --git a/build/zip_bundle.gni b/build/zip_bundle.gni index 26fae8c96bf9a..2405e4b02b5e3 100644 --- a/build/zip_bundle.gni +++ b/build/zip_bundle.gni @@ -8,6 +8,9 @@ if (flutter_runtime_mode == "jit_release") { android_zip_archive_dir = "android-$target_cpu-jit-release" } else { android_zip_archive_dir = "android-$target_cpu" + if (flutter_runtime_mode != "debug") { + android_zip_archive_dir += "-$flutter_runtime_mode" + } } # Creates a zip file in the $root_build_dir/zip_archives folder. @@ -38,7 +41,7 @@ template("zip_bundle") { action(target_name) { script = "//flutter/build/zip.py" outputs = [ "$root_build_dir/zip_archives/${invoker.output}" ] - inputs = [] + sources = [] deps = invoker.deps args = [ @@ -49,9 +52,9 @@ template("zip_bundle") { args += [ "-i", rebase_path(input.source), - rebase_path(input.destination), + input.destination, ] - inputs += [ input.source ] + sources += [ input.source ] } } } diff --git a/ci/analyze.sh b/ci/analyze.sh index 6eb2a529c949a..aaadc0e68ba15 100755 --- a/ci/analyze.sh +++ b/ci/analyze.sh @@ -30,65 +30,51 @@ function follow_links() ( SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")") SRC_DIR="$(cd "$SCRIPT_DIR/../.."; pwd -P)" FLUTTER_DIR="$SRC_DIR/flutter" -DART_BIN="$SRC_DIR/third_party/dart/tools/sdks/dart-sdk/bin" -PUB="$DART_BIN/pub" -DART_ANALYZER="$DART_BIN/dartanalyzer" - -echo "Using analyzer from $DART_ANALYZER" - -"$DART_ANALYZER" --version - -function analyze() ( - local last_arg="${!#}" - local results - # Grep sets its return status to non-zero if it doesn't find what it's - # looking for. - set +e - results="$("$DART_ANALYZER" "$@" 2>&1 | - grep -Ev "No issues found!" | - grep -Ev "Analyzing.+$last_arg")" - set -e - echo "$results" - if [ -n "$results" ]; then - echo "Failed analysis of $last_arg" - return 1 - else - echo "Success: no issues found in $last_arg" - fi - return 0 -) +DART_BIN="$SRC_DIR/out/host_debug_unopt/dart-sdk/bin" +DART="$DART_BIN/dart" + +if [[ ! -f "$DART" ]]; then + echo "'$DART' not found" + echo "" + echo "To build the Dart SDK, run:" + echo " flutter/tools/gn --unoptimized --runtime-mode=debug" + echo " ninja -C out/host_debug_unopt" + exit 1 +fi + +echo "Using dart from $DART_BIN" +"$DART" --version +echo "" + +"$DART" analyze "$FLUTTER_DIR/lib/ui" + +"$DART" analyze "$FLUTTER_DIR/lib/spirv" + +"$DART" analyze "$FLUTTER_DIR/ci" + +"$DART" analyze "$FLUTTER_DIR/flutter_frontend_server" + +"$DART" analyze "$FLUTTER_DIR/tools/licenses" + +"$DART" analyze "$FLUTTER_DIR/testing/litetest" + +"$DART" analyze "$FLUTTER_DIR/testing/benchmark" + +"$DART" analyze "$FLUTTER_DIR/testing/smoke_test_failure" + +"$DART" analyze "$FLUTTER_DIR/testing/dart" + +"$DART" analyze "$FLUTTER_DIR/testing/scenario_app" + +"$DART" analyze "$FLUTTER_DIR/testing/symbols" + +"$DART" analyze "$FLUTTER_DIR/tools/githooks" + +"$DART" analyze "$FLUTTER_DIR/tools/clang_tidy" + +echo "" -echo "Analyzing dart:ui library..." -analyze \ - --options "$FLUTTER_DIR/analysis_options.yaml" \ - --enable-experiment=non-nullable \ - "$SRC_DIR/out/host_debug_unopt/gen/sky/bindings/dart_ui/ui.dart" - -echo "Analyzing flutter_frontend_server..." -analyze \ - --packages="$FLUTTER_DIR/flutter_frontend_server/.dart_tool/package_config.json" \ - --options "$FLUTTER_DIR/analysis_options.yaml" \ - "$FLUTTER_DIR/flutter_frontend_server" - -echo "Analyzing tools/licenses..." -(cd "$FLUTTER_DIR/tools/licenses" && "$PUB" get) -analyze \ - --packages="$FLUTTER_DIR/tools/licenses/.dart_tool/package_config.json" \ - --options "$FLUTTER_DIR/tools/licenses/analysis_options.yaml" \ - "$FLUTTER_DIR/tools/licenses" - -echo "Analyzing testing/dart..." -"$FLUTTER_DIR/tools/gn" --unoptimized -ninja -C "$SRC_DIR/out/host_debug_unopt" sky_engine sky_services -(cd "$FLUTTER_DIR/testing/dart" && "$PUB" get) -analyze \ - --packages="$FLUTTER_DIR/testing/dart/.dart_tool/package_config.json" \ - --options "$FLUTTER_DIR/analysis_options.yaml" \ - "$FLUTTER_DIR/testing/dart" - -echo "Analyzing testing/scenario_app..." -(cd "$FLUTTER_DIR/testing/scenario_app" && "$PUB" get) -analyze \ - --packages="$FLUTTER_DIR/testing/scenario_app/.dart_tool/package_config.json" \ - --options "$FLUTTER_DIR/analysis_options.yaml" \ - "$FLUTTER_DIR/testing/scenario_app" +# Check that dart libraries conform. +echo "Checking web_ui api conformance..." +(cd "$FLUTTER_DIR/web_sdk"; "$DART" pub get) +(cd "$FLUTTER_DIR"; "$DART" "web_sdk/test/api_conform_test.dart") diff --git a/ci/bin/format.dart b/ci/bin/format.dart index 1426edd54f97b..700995ea5529a 100644 --- a/ci/bin/format.dart +++ b/ci/bin/format.dart @@ -8,22 +8,20 @@ // TODO(gspencergoog): Support clang formatting on Windows. // TODO(gspencergoog): Support Java formatting on Windows. -// TODO(gspencergoog): Convert to null safety. import 'dart:io'; import 'package:args/args.dart'; -import 'package:isolate/isolate.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:process_runner/process_runner.dart'; import 'package:process/process.dart'; +import 'package:process_runner/process_runner.dart'; class FormattingException implements Exception { FormattingException(this.message, [this.result]); final String message; - final ProcessResult /*?*/ result; + final ProcessResult? result; int get exitCode => result?.exitCode ?? -1; @@ -31,8 +29,8 @@ class FormattingException implements Exception { String toString() { final StringBuffer output = StringBuffer(runtimeType.toString()); output.write(': $message'); - final String stderr = result?.stderr as String ?? ''; - if (stderr.isNotEmpty) { + final String? stderr = result?.stderr as String?; + if (stderr?.isNotEmpty == true) { output.write(':\n$stderr'); } return output.toString(); @@ -62,9 +60,9 @@ FormatCheck nameToFormatCheck(String name) { return FormatCheck.whitespace; case 'gn': return FormatCheck.gn; + default: + throw FormattingException('Unknown FormatCheck type $name'); } - assert(false, 'Unknown FormatCheck type $name'); - return null; } String formatCheckToName(FormatCheck check) { @@ -78,8 +76,6 @@ String formatCheckToName(FormatCheck check) { case FormatCheck.gn: return 'GN'; } - assert(false, 'Unhandled FormatCheck type $check'); - return null; } List formatCheckNames() { @@ -106,33 +102,33 @@ Future _runGit( return result.stdout; } -typedef MessageCallback = Function(String message, {MessageType type}); +typedef MessageCallback = Function(String? message, {MessageType type}); /// Base class for format checkers. /// /// Provides services that all format checkers need. abstract class FormatChecker { FormatChecker({ - ProcessManager /*?*/ processManager, - @required this.baseGitRef, - @required this.repoDir, - @required this.srcDir, + ProcessManager processManager = const LocalProcessManager(), + required this.baseGitRef, + required this.repoDir, + required this.srcDir, this.allFiles = false, this.messageCallback, }) : _processRunner = ProcessRunner( defaultWorkingDirectory: repoDir, - processManager: processManager ?? const LocalProcessManager(), + processManager: processManager, ); /// Factory method that creates subclass format checkers based on the type of check. factory FormatChecker.ofType( FormatCheck check, { - ProcessManager /*?*/ processManager, - @required String baseGitRef, - @required Directory repoDir, - @required Directory srcDir, + ProcessManager processManager = const LocalProcessManager(), + required String baseGitRef, + required Directory repoDir, + required Directory srcDir, bool allFiles = false, - MessageCallback messageCallback, + MessageCallback? messageCallback, }) { switch (check) { case FormatCheck.clang: @@ -144,7 +140,6 @@ abstract class FormatChecker { allFiles: allFiles, messageCallback: messageCallback, ); - break; case FormatCheck.java: return JavaFormatChecker( processManager: processManager, @@ -154,7 +149,6 @@ abstract class FormatChecker { allFiles: allFiles, messageCallback: messageCallback, ); - break; case FormatCheck.whitespace: return WhitespaceFormatChecker( processManager: processManager, @@ -164,7 +158,6 @@ abstract class FormatChecker { allFiles: allFiles, messageCallback: messageCallback, ); - break; case FormatCheck.gn: return GnFormatChecker( processManager: processManager, @@ -174,17 +167,14 @@ abstract class FormatChecker { allFiles: allFiles, messageCallback: messageCallback, ); - break; } - assert(false, 'Unhandled FormatCheck type $check'); - return null; } final ProcessRunner _processRunner; final Directory srcDir; final Directory repoDir; final bool allFiles; - MessageCallback /*?*/ messageCallback; + MessageCallback? messageCallback; final String baseGitRef; /// Override to provide format checking for a specific type. @@ -194,7 +184,7 @@ abstract class FormatChecker { Future fixFormatting(); @protected - void message(String string) => messageCallback?.call(string, type: MessageType.message); + void message(String? string) => messageCallback?.call(string, type: MessageType.message); @protected void error(String string) => messageCallback?.call(string, type: MessageType.error); @@ -208,8 +198,10 @@ abstract class FormatChecker { /// Uses to convert the stdout of a previous command into an input stream for /// the next command. @protected - Stream> codeUnitsAsStream(List input) async* { - yield input; + Stream> codeUnitsAsStream(List? input) async* { + if (input != null) { + yield input; + } } @protected @@ -299,12 +291,12 @@ abstract class FormatChecker { /// Checks and formats C++/ObjC files using clang-format. class ClangFormatChecker extends FormatChecker { ClangFormatChecker({ - ProcessManager /*?*/ processManager, - @required String baseGitRef, - @required Directory repoDir, - @required Directory srcDir, + ProcessManager processManager = const LocalProcessManager(), + required String baseGitRef, + required Directory repoDir, + required Directory srcDir, bool allFiles = false, - MessageCallback messageCallback, + MessageCallback? messageCallback, }) : super( processManager: processManager, baseGitRef: baseGitRef, @@ -334,7 +326,7 @@ class ClangFormatChecker extends FormatChecker { ); } - /*late*/ File clangFormat; + late final File clangFormat; @override Future checkFormatting() async { @@ -350,7 +342,7 @@ class ClangFormatChecker extends FormatChecker { if (failures.isEmpty) { return true; } - return await applyPatch(failures); + return applyPatch(failures); } Future _getClangFormatVersion() async { @@ -379,7 +371,7 @@ class ClangFormatChecker extends FormatChecker { message('Using ${await _getClangFormatVersion()}'); } final List clangJobs = []; - for (String file in files) { + for (final String file in files) { if (file.trim().isEmpty) { continue; } @@ -392,7 +384,7 @@ class ClangFormatChecker extends FormatChecker { final Stream completedClangFormats = clangPool.startWorkers(clangJobs); final List diffJobs = []; await for (final WorkerJob completedJob in completedClangFormats) { - if (completedJob.result != null && completedJob.result.exitCode == 0) { + if (completedJob.result.exitCode == 0) { diffJobs.add( WorkerJob(['diff', '-u', completedJob.command.last, '-'], stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw), failOk: true), @@ -432,12 +424,12 @@ class ClangFormatChecker extends FormatChecker { /// Checks the format of Java files uing the Google Java format checker. class JavaFormatChecker extends FormatChecker { JavaFormatChecker({ - ProcessManager /*?*/ processManager, - @required String baseGitRef, - @required Directory repoDir, - @required Directory srcDir, + ProcessManager processManager = const LocalProcessManager(), + required String baseGitRef, + required Directory repoDir, + required Directory srcDir, bool allFiles = false, - MessageCallback messageCallback, + MessageCallback? messageCallback, }) : super( processManager: processManager, baseGitRef: baseGitRef, @@ -459,7 +451,7 @@ class JavaFormatChecker extends FormatChecker { ); } - /*late*/ File googleJavaFormatJar; + late final File googleJavaFormatJar; Future _getGoogleJavaFormatVersion() async { final ProcessRunnerResult result = await _processRunner @@ -481,7 +473,7 @@ class JavaFormatChecker extends FormatChecker { if (failures.isEmpty) { return true; } - return await applyPatch(failures); + return applyPatch(failures); } Future _getJavaVersion() async { @@ -515,7 +507,7 @@ class JavaFormatChecker extends FormatChecker { if (verbose) { message('Using $javaFormatVersion with Java $javaVersion'); } - for (String file in files) { + for (final String file in files) { if (file.trim().isEmpty) { continue; } @@ -532,7 +524,7 @@ class JavaFormatChecker extends FormatChecker { final Stream completedClangFormats = formatPool.startWorkers(formatJobs); final List diffJobs = []; await for (final WorkerJob completedJob in completedClangFormats) { - if (completedJob.result != null && completedJob.result.exitCode == 0) { + if (completedJob.result.exitCode == 0) { diffJobs.add( WorkerJob( ['diff', '-u', completedJob.command.last, '-'], @@ -575,12 +567,12 @@ class JavaFormatChecker extends FormatChecker { /// Checks the format of any BUILD.gn files using the "gn format" command. class GnFormatChecker extends FormatChecker { GnFormatChecker({ - ProcessManager /*?*/ processManager, - @required String baseGitRef, - @required Directory repoDir, - @required Directory srcDir, + ProcessManager processManager = const LocalProcessManager(), + required String baseGitRef, + required Directory repoDir, + required Directory srcDir, bool allFiles = false, - MessageCallback messageCallback, + MessageCallback? messageCallback, }) : super( processManager: processManager, baseGitRef: baseGitRef, @@ -599,7 +591,7 @@ class GnFormatChecker extends FormatChecker { ); } - /*late*/ File gnBinary; + late final File gnBinary; @override Future checkFormatting() async { @@ -615,7 +607,7 @@ class GnFormatChecker extends FormatChecker { return true; } - Future _runGnCheck({@required bool fixing}) async { + Future _runGnCheck({required bool fixing}) async { final List filesToCheck = await getFileList(['*.gn', '*.gni']); final List cmd = [ @@ -665,7 +657,8 @@ class GnFormatChecker extends FormatChecker { @immutable class _GrepResult { - const _GrepResult(this.file, this.hits, this.lineNumbers); + const _GrepResult(this.file, [this.hits = const [], this.lineNumbers = const []]); + bool get isEmpty => hits.isEmpty && lineNumbers.isEmpty; final File file; final List hits; final List lineNumbers; @@ -674,12 +667,12 @@ class _GrepResult { /// Checks for trailing whitspace in Dart files. class WhitespaceFormatChecker extends FormatChecker { WhitespaceFormatChecker({ - ProcessManager /*?*/ processManager, - @required String baseGitRef, - @required Directory repoDir, - @required Directory srcDir, + ProcessManager processManager = const LocalProcessManager(), + required String baseGitRef, + required Directory repoDir, + required Directory srcDir, bool allFiles = false, - MessageCallback messageCallback, + MessageCallback? messageCallback, }) : super( processManager: processManager, baseGitRef: baseGitRef, @@ -701,7 +694,7 @@ class WhitespaceFormatChecker extends FormatChecker { Future fixFormatting() async { final List failures = await _getWhitespaceFailures(); if (failures.isNotEmpty) { - for (File file in failures) { + for (final File file in failures) { stderr.writeln('Fixing $file'); String contents = file.readAsStringSync(); contents = contents.replaceAll(trailingWsRegEx, ''); @@ -711,7 +704,7 @@ class WhitespaceFormatChecker extends FormatChecker { return true; } - static Future<_GrepResult> _hasTrailingWhitespace(File file) async { + static _GrepResult _hasTrailingWhitespace(File file) { final List hits = []; final List lineNumbers = []; int lineNumber = 0; @@ -723,17 +716,13 @@ class WhitespaceFormatChecker extends FormatChecker { lineNumber++; } if (hits.isEmpty) { - return null; + return _GrepResult(file); } return _GrepResult(file, hits, lineNumbers); } - Stream<_GrepResult> _whereHasTrailingWhitespace(Iterable files) async* { - final LoadBalancer pool = - await LoadBalancer.create(Platform.numberOfProcessors, IsolateRunner.spawn); - for (final File file in files) { - yield await pool.run<_GrepResult, File>(_hasTrailingWhitespace, file); - } + Iterable<_GrepResult> _whereHasTrailingWhitespace(Iterable files) { + return files.map(_hasTrailingWhitespace); } Future> _getWhitespaceFailures() async { @@ -769,14 +758,14 @@ class WhitespaceFormatChecker extends FormatChecker { int inProgress = Platform.numberOfProcessors; int pending = total; int failed = 0; - await for (final _GrepResult result in _whereHasTrailingWhitespace( + for (final _GrepResult result in _whereHasTrailingWhitespace( files.map( (String file) => File( path.join(repoDir.absolute.path, file), ), ), )) { - if (result == null) { + if (result.isEmpty) { completed++; } else { failed++; @@ -804,7 +793,7 @@ class WhitespaceFormatChecker extends FormatChecker { Future _getDiffBaseRevision(ProcessManager processManager, Directory repoDir) async { final ProcessRunner processRunner = ProcessRunner( defaultWorkingDirectory: repoDir, - processManager: processManager ?? const LocalProcessManager(), + processManager: processManager, ); String upstream = 'upstream'; final String upstreamUrl = await _runGit( @@ -861,7 +850,7 @@ Future main(List arguments) async { 'On Windows, only whitespace and gn checks are currently supported.'); parser.addFlag('verbose', help: 'Print verbose output.', defaultsTo: verbose); - ArgResults options; + late final ArgResults options; try { options = parser.parse(arguments); } on FormatException catch (e) { @@ -883,7 +872,8 @@ Future main(List arguments) async { stderr.writeln('Src: $srcDir'); } - void message(String message, {MessageType type = MessageType.message}) { + void message(String? message, {MessageType type = MessageType.message}) { + message ??= ''; switch (type) { case MessageType.message: stderr.writeln(message); diff --git a/ci/bin/lint.dart b/ci/bin/lint.dart deleted file mode 100644 index 04ff295acd5de..0000000000000 --- a/ci/bin/lint.dart +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Runs clang-tidy on files with changes. -// -// usage: -// dart lint.dart [clang-tidy checks] -// -// User environment variable FLUTTER_LINT_ALL to run on all files. - -import 'dart:async' show Completer; -import 'dart:convert' show jsonDecode, utf8, LineSplitter; -import 'dart:io' show File, exit, Directory, FileSystemEntity, Platform, stderr; - -import 'package:args/args.dart'; -import 'package:path/path.dart' as path; -import 'package:process_runner/process_runner.dart'; - -String _linterOutputHeader = ''' -┌──────────────────────────┐ -│ Engine Clang Tidy Linter │ -└──────────────────────────┘ -The following errors have been reported by the Engine Clang Tidy Linter. For -more information on addressing these issues please see: -https://github.com/flutter/flutter/wiki/Engine-Clang-Tidy-Linter -'''; - -class Command { - Directory directory = Directory(''); - String command = ''; - File file = File(''); -} - -Command parseCommand(Map map) { - final Directory dir = Directory(map['directory'] as String).absolute; - return Command() - ..directory = dir - ..command = map['command'] as String - ..file = File(path.normalize(path.join(dir.path, map['file'] as String))); -} - -String calcTidyArgs(Command command) { - String result = command.command; - result = result.replaceAll(RegExp(r'\S*clang/bin/clang'), ''); - result = result.replaceAll(RegExp(r'-MF \S*'), ''); - return result; -} - -String calcTidyPath(Command command) { - final RegExp regex = RegExp(r'\S*clang/bin/clang'); - return regex - .stringMatch(command.command) - ?.replaceAll('clang/bin/clang', 'clang/bin/clang-tidy') ?? - ''; -} - -bool isNonEmptyString(String str) => str.isNotEmpty; - -bool containsAny(File file, Iterable queries) { - return queries.where((File query) => path.equals(query.path, file.path)).isNotEmpty; -} - -/// Returns a list of all non-deleted files which differ from the nearest -/// merge-base with `master`. If it can't find a fork point, uses the default -/// merge-base. -Future> getListOfChangedFiles(Directory repoPath) async { - final ProcessRunner processRunner = ProcessRunner(defaultWorkingDirectory: repoPath); - final ProcessRunnerResult fetchResult = await processRunner.runProcess( - ['git', 'fetch', 'upstream', 'master'], - failOk: true, - ); - if (fetchResult.exitCode != 0) { - await processRunner.runProcess(['git', 'fetch', 'origin', 'master']); - } - final Set result = {}; - ProcessRunnerResult mergeBaseResult = await processRunner.runProcess( - ['git', 'merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD'], - failOk: true); - if (mergeBaseResult.exitCode != 0) { - if (verbose) { - stderr.writeln("Didn't find a fork point, falling back to default merge base."); - } - mergeBaseResult = await processRunner - .runProcess(['git', 'merge-base', 'FETCH_HEAD', 'HEAD'], failOk: false); - } - final String mergeBase = mergeBaseResult.stdout.trim(); - final ProcessRunnerResult masterResult = await processRunner - .runProcess(['git', 'diff', '--name-only', '--diff-filter=ACMRT', mergeBase]); - result.addAll(masterResult.stdout.split('\n').where(isNonEmptyString)); - return result.map((String filePath) => File(path.join(repoPath.path, filePath))).toList(); -} - -Future> dirContents(Directory dir) { - final List files = []; - final Completer> completer = Completer>(); - final Stream lister = dir.list(recursive: true); - lister.listen((FileSystemEntity file) => file is File ? files.add(file) : null, - onError: (Object e) => completer.completeError(e), onDone: () => completer.complete(files)); - return completer.future; -} - -File buildFileAsRepoFile(String buildFile, Directory repoPath) { - // Removes the "../../flutter" from the build files to make it relative to the flutter - // dir. - final String relativeBuildFile = path.joinAll(path.split(buildFile).sublist(3)); - final File result = File(path.join(repoPath.absolute.path, relativeBuildFile)); - print('Build file: $buildFile => ${result.path}'); - return result; -} - -Future shouldIgnoreFile(File file) async { - if (path.split(file.path).contains('third_party')) { - return 'third_party'; - } else { - final RegExp exp = RegExp(r'//\s*FLUTTER_NOLINT'); - await for (String line - in file.openRead().transform(utf8.decoder).transform(const LineSplitter())) { - if (exp.hasMatch(line)) { - return 'FLUTTER_NOLINT'; - } else if (line.isNotEmpty && line[0] != '\n' && line[0] != '/') { - // Quick out once we find a line that isn't empty or a comment. The - // FLUTTER_NOLINT must show up before the first real code. - return ''; - } - } - return ''; - } -} - -void _usage(ArgParser parser, {int exitCode = 1}) { - stderr.writeln('lint.dart [--help] [--lint-all] [--verbose] [--diff-branch]'); - stderr.writeln(parser.usage); - exit(exitCode); -} - -bool verbose = false; - -void main(List arguments) async { - final ArgParser parser = ArgParser(); - parser.addFlag('help', help: 'Print help.'); - parser.addFlag('lint-all', - help: 'lint all of the sources, regardless of FLUTTER_NOLINT.', defaultsTo: false); - parser.addFlag('verbose', help: 'Print verbose output.', defaultsTo: verbose); - parser.addOption('repo', help: 'Use the given path as the repo path'); - parser.addOption('compile-commands', - help: 'Use the given path as the source of compile_commands.json. This ' - 'file is created by running tools/gn'); - parser.addOption('checks', - help: 'Perform the given checks on the code. Defaults to the empty ' - 'string, indicating all checks should be performed.', - defaultsTo: ''); - final ArgResults options = parser.parse(arguments); - - verbose = options['verbose'] as bool; - - if (options['help'] as bool) { - _usage(parser, exitCode: 0); - } - - if (!options.wasParsed('compile-commands')) { - stderr.writeln('ERROR: The --compile-commands argument is requried.'); - _usage(parser); - } - - if (!options.wasParsed('repo')) { - stderr.writeln('ERROR: The --repo argument is requried.'); - _usage(parser); - } - - final File buildCommandsPath = File(options['compile-commands'] as String); - if (!buildCommandsPath.existsSync()) { - stderr.writeln("ERROR: Build commands path ${buildCommandsPath.absolute.path} doesn't exist."); - _usage(parser); - } - - final Directory repoPath = Directory(options['repo'] as String); - if (!repoPath.existsSync()) { - stderr.writeln("ERROR: Repo path ${repoPath.absolute.path} doesn't exist."); - _usage(parser); - } - - print(_linterOutputHeader); - - final String checksArg = options.wasParsed('checks') ? options['checks'] as String : ''; - final String checks = checksArg.isNotEmpty ? '--checks=$checksArg' : '--config='; - final bool lintAll = - Platform.environment['FLUTTER_LINT_ALL'] != null || options['lint-all'] as bool; - final List changedFiles = - lintAll ? await dirContents(repoPath) : await getListOfChangedFiles(repoPath); - - if (verbose) { - print('Checking lint in repo at $repoPath.'); - if (checksArg.isNotEmpty) { - print('Checking for specific checks: $checks.'); - } - if (lintAll) { - print('Checking all ${changedFiles.length} files the repo dir.'); - } else { - print('Dectected ${changedFiles.length} files that have changed'); - } - } - - final List buildCommandMaps = - jsonDecode(await buildCommandsPath.readAsString()) as List; - final List buildCommands = buildCommandMaps - .map((dynamic x) => parseCommand(x as Map)) - .toList(); - final Command firstCommand = buildCommands[0]; - final String tidyPath = calcTidyPath(firstCommand); - assert(tidyPath.isNotEmpty); - final List changedFileBuildCommands = - buildCommands.where((Command x) => containsAny(x.file, changedFiles)).toList(); - - if (changedFileBuildCommands.isEmpty) { - print('No changed files that have build commands associated with them ' - 'were found.'); - exit(0); - } - - if (verbose) { - print('Found ${changedFileBuildCommands.length} files that have build ' - 'commands associated with them and can be lint checked.'); - } - - int exitCode = 0; - final List jobs = []; - for (Command command in changedFileBuildCommands) { - final String relativePath = path.relative(command.file.path, from: repoPath.parent.path); - final String ignoreReason = await shouldIgnoreFile(command.file); - if (ignoreReason.isEmpty) { - final String tidyArgs = calcTidyArgs(command); - final List args = [command.file.path, checks, '--']; - args.addAll(tidyArgs?.split(' ') ?? []); - print('🔶 linting $relativePath'); - jobs.add(WorkerJob( - [tidyPath, ...args], - workingDirectory: command.directory, - name: 'clang-tidy on ${command.file.path}', - )); - } else { - print('🔷 ignoring $relativePath ($ignoreReason)'); - } - } - final ProcessPool pool = ProcessPool(); - - await for (final WorkerJob job in pool.startWorkers(jobs)) { - if (job.result?.stdout.isEmpty ?? true) { - continue; - } - print('❌ Failures for ${job.name}:'); - print(job.result.stdout); - exitCode = 1; - } - print('\n'); - if (exitCode == 0) { - print('No lint problems found.'); - } - exit(exitCode); -} diff --git a/ci/build.sh b/ci/build.sh deleted file mode 100755 index 5555fc4efd5e3..0000000000000 --- a/ci/build.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# -# 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. - -set -e - -# Needed because if it is set, cd may print the path it changed to. -unset CDPATH - -# On Mac OS, readlink -f doesn't work, so follow_links traverses the path one -# link at a time, and then cds into the link destination and find out where it -# ends up. -# -# The function is enclosed in a subshell to avoid changing the working directory -# of the caller. -function follow_links() ( - cd -P "$(dirname -- "$1")" - file="$PWD/$(basename -- "$1")" - while [[ -h "$file" ]]; do - cd -P "$(dirname -- "$file")" - file="$(readlink -- "$file")" - cd -P "$(dirname -- "$file")" - file="$PWD/$(basename -- "$file")" - done - echo "$file" -) - -SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")") -SRC_DIR="$(cd "$SCRIPT_DIR/../.."; pwd -P)" -FLUTTER_DIR="$SRC_DIR/flutter" - -set -x - -PATH="$SRC_DIR/third_party/dart/tools/sdks/dart-sdk/bin:$HOME/depot_tools:$PATH" - -cd "$SRC_DIR" - -# Build the dart UI files -"$FLUTTER_DIR/tools/gn" --unoptimized -ninja -C "$SRC_DIR/out/host_debug_unopt" generate_dart_ui - -# Analyze the dart UI -"$FLUTTER_DIR/ci/analyze.sh" -"$FLUTTER_DIR/ci/licenses.sh" - -# Check that dart libraries conform -(cd "$FLUTTER_DIR/web_sdk"; pub get) -(cd "$FLUTTER_DIR"; dart "web_sdk/test/api_conform_test.dart") \ No newline at end of file diff --git a/ci/builders/mac_ios_engine.json b/ci/builders/mac_ios_engine.json new file mode 100644 index 0000000000000..24b78fbd802d2 --- /dev/null +++ b/ci/builders/mac_ios_engine.json @@ -0,0 +1,72 @@ +[ + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "gn": [ + "--ios", + "--runtime-mode", + "debug", + "--simulator", + "--no-lto" + ], + "name": "ios_debug_sym", + "ninja": { + "config": "ios_debug_sim", + "targets": [] + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "gn": [ + "--bitcode", + "--ios", + "--runtime-mode", + "debug" + ], + "name": "ios_debug", + "ninja": { + "config": "ios_debug", + "targets": [] + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "generators": { + "tasks": [ + { + "name": "BuildObjcDoc", + "scripts": ["flutter/tools/gen_objcdoc.sh"] + } + ] + }, + "gn": [ + "--bitcode", + "--ios", + "--runtime-mode", + "debug", + "--ios-cpu=arm" + ], + "name": "ios_debug_arm", + "ninja": { + "config": "ios_debug_arm", + "targets": [] + }, + "tests": [] + } +] diff --git a/ci/builders/mac_ios_engine_profile.json b/ci/builders/mac_ios_engine_profile.json new file mode 100644 index 0000000000000..dc4f2e25ab235 --- /dev/null +++ b/ci/builders/mac_ios_engine_profile.json @@ -0,0 +1,65 @@ +[ + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "gn": [ + "--ios", + "--runtime-mode", + "debug", + "--simulator", + "--no-lto" + ], + "name": "ios_debug_sym", + "ninja": { + "config": "ios_debug_sim", + "targets": [] + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "gn": [ + "--ios", + "--runtime-mode", + "profile", + "--bitcode" + ], + "name": "ios_profile", + "ninja": { + "config": "ios_profile", + "targets": [] + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "generators": {}, + "gn": [ + "--ios", + "--runtime-mode", + "profile", + "--bitcode", + "--ios-cpu=arm" + ], + "name": "ios_profile_arm", + "ninja": { + "config": "ios_profile_arm", + "targets": [] + }, + "tests": [] + } +] diff --git a/ci/builders/mac_ios_engine_release.json b/ci/builders/mac_ios_engine_release.json new file mode 100644 index 0000000000000..98c5b74160634 --- /dev/null +++ b/ci/builders/mac_ios_engine_release.json @@ -0,0 +1,69 @@ +[ + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "gn": [ + "--ios", + "--runtime-mode", + "debug", + "--simulator", + "--no-lto" + ], + "name": "ios_debug_sym", + "ninja": { + "config": "ios_debug_sim", + "targets": [] + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "gn": [ + "--ios", + "--runtime-mode", + "release", + "--bitcode", + "--no-goma" + ], + "name": "ios_release", + "ninja": { + "config": "ios_release", + "targets": [], + "tool": "autoninja" + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "mac_model=Macmini8,1", + "os=Mac" + ], + "generators": {}, + "gn": [ + "--ios", + "--runtime-mode", + "release", + "--bitcode", + "--no-goma", + "--ios-cpu=arm" + ], + "name": "ios_release_arm", + "ninja": { + "config": "ios_release_arm", + "targets": [], + "tool": "autoninja" + }, + "tests": [] + } +] diff --git a/ci/builders/web_engine.json b/ci/builders/web_engine.json new file mode 100644 index 0000000000000..f1d406f6f0104 --- /dev/null +++ b/ci/builders/web_engine.json @@ -0,0 +1,100 @@ +[ + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "os=Windows-10" + ], + "gn": [ + "--runtime-mode", + "debug", + "--unoptimized", + "--full-dart-sdk", + "--prebuilt-dart-sdk" + ], + "name": "windows_host_debug", + "ninja": { + "config": "host_debug_unopt", + "targets": [] + }, + "platform": "Windows", + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "os=Linux" + ], + "generators": { + "pub_dirs": [ + "flutter/lib/web_ui/", + "flutter/web_sdk/web_engine_tester/" + ], + "tasks": [ + { + "name": "compile web_tests", + "parameters": [ + "run", + "compile_tests" + ], + "scripts": [ + "out/host_debug_unopt/dart-sdk/bin/dart", + "flutter/lib/web_ui/dev/felt.dart" + ] + }, + { + "name": "check licenses", + "parameters": [ + "check-licenses" + ], + "scripts": [ + "out/host_debug_unopt/dart-sdk/bin/dart", + "flutter/lib/web_ui/dev/felt.dart" + ] + }, + { + "name": "web engine analysis", + "parameters": [], + "scripts": [ + "flutter/lib/web_ui/dev/web_engine_analysis.sh" + ] + } + ] + }, + "gn": [ + "--runtime-mode", + "debug", + "--unoptimized", + "--full-dart-sdk" + ], + "name": "linux_host_debug_unopt", + "ninja": { + "config": "host_debug_unopt", + "targets": [] + }, + "platform": "Linux", + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "os=Mac" + ], + "gn": [ + "--runtime-mode", + "debug", + "--unoptimized", + "--full-dart-sdk", + "--prebuilt-dart-sdk" + ], + "name": "mac_host_debug_unopt", + "ninja": { + "config": "host_debug_unopt", + "targets": [] + }, + "platform": "Mac", + "tests": [] + } +] diff --git a/ci/builders/windows_android_aot_engine.json b/ci/builders/windows_android_aot_engine.json new file mode 100644 index 0000000000000..7c237c7a890ed --- /dev/null +++ b/ci/builders/windows_android_aot_engine.json @@ -0,0 +1,102 @@ +[ + { + "archives": [], + "drone_dimensions": [], + "gn": [ + "--runtime-mode", + "profile", + "--android" + ], + "name": "android_profile", + "ninja": { + "config": "android_profile", + "targets": [ + "gen_snapshot" + ] + } + }, + { + "archives": [], + "drone_dimensions": [], + "gn": [ + "--runtime-mode", + "profile", + "--android", + "--android-cpu=arm64" + ], + "name": "android_profile_arm64", + "ninja": { + "config": "android_profile_arm64", + "targets": [ + "gen_snapshot" + ] + } + }, + { + "archives": [], + "drone_dimensions": [], + "gn": [ + "--runtime-mode", + "profile", + "--android", + "--android-cpu=x64" + ], + "name": "android_profile_x64", + "ninja": { + "config": "android_profile_x64", + "targets": [ + "gen_snapshot" + ] + } + }, + { + "archives": [], + "drone_dimensions": [], + "gn": [ + "--runtime-mode", + "release", + "--android" + ], + "name": "android_release", + "ninja": { + "config": "android_release", + "targets": [ + "gen_snapshot" + ] + } + }, + { + "archives": [], + "drone_dimensions": [], + "gn": [ + "--runtime-mode", + "release", + "--android", + "--android-cpu=arm64" + ], + "name": "android_release_arm64", + "ninja": { + "config": "android_release_arm64", + "targets": [ + "gen_snapshot" + ] + } + }, + { + "archives": [], + "drone_dimensions": [], + "gn": [ + "--runtime-mode", + "release", + "--android", + "--android-cpu=x64" + ], + "name": "android_release_x64", + "ninja": { + "config": "android_release_x64", + "targets": [ + "gen_snapshot" + ] + } + } +] diff --git a/ci/builders/windows_host_engine.json b/ci/builders/windows_host_engine.json new file mode 100644 index 0000000000000..460f994ef435d --- /dev/null +++ b/ci/builders/windows_host_engine.json @@ -0,0 +1,86 @@ +[ + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "os=Windows-10" + ], + "gclient_custom_vars": { + "download_android_deps": false + }, + "gn": [ + "--runtime-mode", + "debug", + "--full-dart-sdk", + "--no-lto" + ], + "name": "host_debug", + "ninja": { + "config": "host_debug", + "targets": [] + }, + "tests": [ + { + "language": "python", + "name": "Host Tests for host_debug", + "parameters": [ + "--variant", + "host_debug", + "--type", + "engine" + ], + "script": "flutter/testing/run_tests.py", + "type": "local" + } + ] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "os=Windows-10" + ], + "gclient_custom_vars": { + "download_android_deps": false + }, + "gn": [ + "--runtime-mode", + "profile", + "--no-lto" + ], + "name": "host_profile", + "ninja": { + "config": "host_profile", + "targets": [ + "windows", + "gen_snapshot" + ] + }, + "tests": [] + }, + { + "archives": [], + "drone_dimensions": [ + "device_type=none", + "os=Windows-10" + ], + "gclient_custom_vars": { + "download_android_deps": false + }, + "generators": {}, + "gn": [ + "--runtime-mode", + "release", + "--no-lto" + ], + "name": "host_release", + "ninja": { + "config": "host_release", + "targets": [ + "windows", + "gen_snapshot" + ] + }, + "tests": [] + } +] diff --git a/ci/dev/README.md b/ci/dev/README.md deleted file mode 100644 index 6f5701397bfe6..0000000000000 --- a/ci/dev/README.md +++ /dev/null @@ -1,32 +0,0 @@ -This directory contains resources that the Flutter team uses during -the development of engine. - -## Luci builder file -`try_builders.json` and `prod_builders.json` contains the -supported luci try/prod builders for engine. It follows format: -```json -{ - "builders":[ - { - "name":"yyy", - "repo":"engine", - "enabled":true - } - ] -} -``` -for `try_builders.json`, and follows format: -```json -{ - "builders":[ - { - "name":"yyy", - "repo":"engine" - } - ] -} -``` -for `prod_builders.json`. `try_builders.json` will be mainly used in -[`flutter/cocoon`](https://github.com/flutter/cocoon) to trigger/update pre-submit -engine luci tasks, whereas `prod_builders.json` will be mainly used in `flutter/cocoon` -to push luci task statuses to GitHub. \ No newline at end of file diff --git a/ci/dev/prod_builders.json b/ci/dev/prod_builders.json deleted file mode 100644 index 435046df1bcd8..0000000000000 --- a/ci/dev/prod_builders.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "builders":[ - { - "name":"Linux Android AOT Engine", - "repo":"engine" - }, - { - "name":"Linux Android Debug Engine", - "repo":"engine" - }, - { - "name":"Linux Host Engine", - "repo":"engine" - }, - { - "name":"Linux Fuchsia", - "repo":"engine" - }, - { - "name":"Linux Fuchsia FEMU", - "repo":"engine" - }, - { - "name":"Linux Web Engine", - "repo":"engine" - }, - { - "name":"Mac Android AOT Engine", - "repo":"engine" - }, - { - "name":"Mac Android Debug Engine", - "repo":"engine" - }, - { - "name":"Mac Host Engine", - "repo":"engine" - }, - { - "name":"Mac iOS Engine", - "repo":"engine" - }, - { - "name":"Mac iOS Engine Profile", - "repo":"engine" - }, - { - "name":"Mac iOS Engine Release", - "repo":"engine" - }, - { - "name":"Mac Web Engine", - "repo":"engine" - }, - { - "name":"Windows Android AOT Engine", - "repo":"engine" - }, - { - "name":"Windows Host Engine", - "repo":"engine" - }, - { - "name":"Windows Web Engine", - "repo":"engine" - } - ] -} diff --git a/ci/dev/try_builders.json b/ci/dev/try_builders.json deleted file mode 100644 index 9ca44f677c2d6..0000000000000 --- a/ci/dev/try_builders.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "builders":[ - { - "name":"Linux Android AOT Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Linux Android Debug Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Linux Android Scenarios", - "repo":"engine", - "enabled": true - }, - { - "name":"Linux Fuchsia", - "repo":"engine", - "enabled": true - }, - { - "name":"Linux Host Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Linux Web Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Linux Web Framework tests", - "repo":"engine", - "enabled": true, - "run_if": ["DEPS", "lib/web_ui/**", "web_sdk/**"] - }, - { - "name":"Mac Android AOT Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Mac Android Debug Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Mac Host Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Mac iOS Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Mac Web Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Windows Android AOT Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Windows Host Engine", - "repo":"engine", - "enabled": true - }, - { - "name":"Windows Web Engine", - "repo":"engine", - "enabled": true - } - ] -} diff --git a/ci/docker/build/Dockerfile b/ci/docker/build/Dockerfile index 2d96db361dce0..b1e2d9495a8be 100644 --- a/ci/docker/build/Dockerfile +++ b/ci/docker/build/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM debian:stretch ENV DEPOT_TOOLS_PATH $HOME/depot_tools ENV BUILDROOT_PATH $HOME/buildroot @@ -8,19 +8,97 @@ ENV PATH $PATH:$DEPOT_TOOLS_PATH # Notes: # - libx11-dev is used by Flutter for desktop linux (see also install-build-deps-linux-desktop.sh) # - chrome is used by Flutter for web. -RUN apt-get update && \ - apt-get install -y git wget curl unzip python lsb-release sudo apt-transport-https && \ - git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS_PATH && \ - mkdir --parents $ENGINE_PATH && \ - git clone https://github.com/flutter/buildroot.git $BUILDROOT_PATH && \ - cd $BUILDROOT_PATH && \ - ./build/install-build-deps.sh --no-prompt && \ - ./build/install-build-deps-android.sh --no-prompt && \ - (echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list) && \ - (curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -) && \ - (wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -) && \ - echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list && \ - apt-get update && apt-get install -y google-cloud-sdk google-chrome-stable libx11-dev && \ + +# Updates the distribution. +RUN apt-get update + +# Install generic tools. +RUN apt-get install -y ca-certificates gnupg wget curl lsb-release sudo apt-transport-https + +# Add additional repos. +# chrome stable +RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - +RUN echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list +# gcloud sdk +RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | \ + tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \ + apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - +RUN apt-get update + +# Install basic tools and libraries +RUN apt-get install -y \ + acpica-tools \ + autoconf \ + automake \ + bison \ + build-essential \ + dejagnu \ + dosfstools \ + flex \ + g++-multilib \ + gettext \ + git \ + gperf \ + groff \ + ifupdown \ + libblkid-dev \ + libc6-dev-i386 \ + libedit-dev \ + libfreetype6-dev \ + libglib2.0-dev \ + liblz4-tool \ + libncurses5-dev \ + libssl-dev \ + libtool \ + libxcursor-dev \ + libxinerama-dev \ + libxrandr-dev \ + libxxf86vm-dev \ + lsof \ + mtools \ + nasm \ + net-tools \ + openjdk-8-jdk \ + pkg-config \ + python \ + python-m2crypto \ + python2.7-dev \ + tcpdump \ + texinfo \ + unzip \ + uuid-dev \ + vim \ + xz-utils \ + zip \ + zlib1g-dev + +# Install x/gui deps +RUN apt-get install -y \ + libegl1-mesa \ + libgles2-mesa-dev \ + libglu1-mesa-dev \ + libgtk-3-dev \ + libx11-dev \ + mesa-common-dev \ + xvfb + +# Install browsers +RUN apt-get install -y \ + firefox-esr \ + google-chrome-stable + +# Install and config gcloud +RUN apt-get update && apt-get install -y google-cloud-sdk && \ gcloud config set core/disable_usage_reporting true && \ - gcloud config set component_manager/disable_update_check true && \ - apt-get clean + gcloud config set component_manager/disable_update_check true + +# Clone depot_tools +RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS_PATH + +# Clone buildroot +RUN git clone https://github.com/flutter/buildroot.git $BUILDROOT_PATH + +# Make engine path and start change directory to buildroot. +RUN mkdir --parents $ENGINE_PATH && \ + cd $BUILDROOT_PATH diff --git a/ci/docker/build/README.md b/ci/docker/build/README.md index 79f0d92708418..02a7fb345ed48 100644 --- a/ci/docker/build/README.md +++ b/ci/docker/build/README.md @@ -1,8 +1,8 @@ This directory includes scripts to build the docker container image used for building flutter/engine in our CI system (currently [Cirrus](cirrus-ci.org)). -In order to run the scripts, you have to setup `docker` and `gcloud`. Please -refer to internal doc go/installdocker for how to setup `docker` on gLinux. +In order to run the scripts, you have to set up `docker` and `gcloud`. Please +refer to internal doc go/installdocker for how to set up `docker` on gLinux. Cirrus will build (and cache) a Docker image based on this `Dockerfile` for Linux tasks using its diff --git a/ci/firebase_testlab.py b/ci/firebase_testlab.py new file mode 100755 index 0000000000000..effd543819ecd --- /dev/null +++ b/ci/firebase_testlab.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import glob +import re +import os +import subprocess +import sys + +script_dir = os.path.dirname(os.path.realpath(__file__)) +buildroot_dir = os.path.abspath(os.path.join(script_dir, '..', '..')) +out_dir = os.path.join(buildroot_dir, 'out') +bucket = 'gs://flutter_firebase_testlab' +error_re = re.compile(r'[EF]/flutter.+') + + +def RunFirebaseTest(apk, results_dir): + # game-loop tests are meant for OpenGL apps. + # This type of test will give the application a handle to a file, and + # we'll write the timeline JSON to that file. + # See https://firebase.google.com/docs/test-lab/android/game-loop + # Pixel 4. As of this commit, this is a highly available device in FTL. + process = subprocess.Popen( + [ + 'gcloud', + '--project', 'flutter-infra', + 'firebase', 'test', 'android', 'run', + '--type', 'game-loop', + '--app', apk, + '--timeout', '2m', + '--results-bucket', bucket, + '--results-dir', results_dir, + '--device', 'model=flame,version=29', + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) + return process + + +def CheckLogcat(results_dir): + logcat = subprocess.check_output([ + 'gsutil', 'cat', '%s/%s/*/logcat' % (bucket, results_dir) + ]) + if not logcat: + sys.exit(1) + + logcat_matches = error_re.findall(logcat) + if logcat_matches: + print('Errors in logcat:') + print(logcat_matches) + sys.exit(1) + + +def CheckTimeline(results_dir): + du = subprocess.check_output([ + 'gsutil', 'du', + '%s/%s/*/game_loop_results/results_scenario_0.json' % (bucket, results_dir) + ]).strip() + if du == '0': + print('Failed to produce a timeline.') + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--variant', dest='variant', action='store', + default='android_profile_arm64', help='The engine variant to run tests for.') + parser.add_argument('--build-id', + default=os.environ.get('SWARMING_TASK_ID', 'local_test'), + help='A unique build identifier for this test. Used to sort results in the GCS bucket.') + + args = parser.parse_args() + + apks_dir = os.path.join(out_dir, args.variant, 'firebase_apks') + apks = glob.glob('%s/*.apk' % apks_dir) + + if not apks: + print('No APKs found at %s' % apks_dir) + return 1 + + git_revision = subprocess.check_output( + ['git', 'rev-parse', 'HEAD'], cwd=script_dir).strip() + + results = [] + for apk in apks: + results_dir = '%s/%s/%s' % (os.path.basename(apk), git_revision, args.build_id) + process = RunFirebaseTest(apk, results_dir) + results.append((results_dir, process)) + + for results_dir, process in results: + for line in iter(process.stdout.readline, ""): + print(line.strip()) + return_code = process.wait() + if return_code != 0: + print('Firebase test failed ' + returncode) + sys.exit(process.returncode) + + print('Checking logcat for %s' % results_dir) + CheckLogcat(results_dir) + # scenario_app produces a timeline, but the android image test does not. + if 'scenario' in apk: + print('Checking timeline for %s' % results_dir) + CheckTimeline(results_dir) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/ci/firebase_testlab.sh b/ci/firebase_testlab.sh index 45631d9bfd025..38bb393626d22 100755 --- a/ci/firebase_testlab.sh +++ b/ci/firebase_testlab.sh @@ -4,46 +4,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -set -e - -APP="$1" -if [[ -z "$APP" ]]; then - echo "Application must be specified as the first argument to the script." - exit 255 -fi - -if [[ ! -f "$APP" ]]; then - echo "File '$APP' not found." - exit 255 -fi +# TODO(dnfield): delete this script once recipes point to the python version. -GIT_REVISION="${2:-$(git rev-parse HEAD)}" -BUILD_ID="${3:-$CIRRUS_BUILD_ID}" - -if [[ -n $GCLOUD_FIREBASE_TESTLAB_KEY ]]; then - # New contributors will not have permissions to run this test - they won't be - # able to access the service account information. We should just mark the test - # as passed - it will run fine on post submit, where it will still catch - # failures. - # We can also still make sure that building a release app bundle still works. - if [[ $GCLOUD_FIREBASE_TESTLAB_KEY == ENCRYPTED* ]]; then - echo "This user does not have permission to run this test." - exit 0 - fi +set -e - echo "$GCLOUD_FIREBASE_TESTLAB_KEY" > "${HOME}/gcloud-service-key.json" - gcloud auth activate-service-account --key-file="${HOME}/gcloud-service-key.json" -fi +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -# Run the test. -# game-loop tests are meant for OpenGL apps. -# This type of test will give the application a handle to a file, and -# we'll write the timeline JSON to that file. -# See https://firebase.google.com/docs/test-lab/android/game-loop -gcloud --project flutter-infra firebase test android run \ - --type game-loop \ - --app "$APP" \ - --timeout 2m \ - --results-bucket=gs://flutter_firebase_testlab \ - --results-dir="engine_scenario_test/$GIT_REVISION/$BUILD_ID" \ - --no-auto-google-login +python $CURRENT_DIR/firebase_testlab.py diff --git a/ci/format.bat b/ci/format.bat index 12a6bb8d7af89..d82d64cec0221 100644 --- a/ci/format.bat +++ b/ci/format.bat @@ -19,14 +19,14 @@ REM Test if Git is available on the Host where /q git || ECHO Error: Unable to find git in your PATH. && EXIT /B 1 SET repo_dir=%SRC_DIR%\flutter -SET ci_dir=%repo_dir%\flutter\ci +SET ci_dir=%repo_dir%\ci SET dart_sdk_path=%SRC_DIR%\third_party\dart\tools\sdks\dart-sdk SET dart=%dart_sdk_path%\bin\dart.exe -SET pub=%dart_sdk_path%\bin\pub.bat cd "%ci_dir%" REM Do not use the CALL command in the next line to execute Dart. CALL causes REM Windows to re-read the line from disk after the CALL command has finished REM regardless of the ampersand chain. -"%pub%" get & "%dart%" --disable-dart-dev bin\format.dart %* & exit /B !ERRORLEVEL! +REM TODO(gspencergoog): Remove --no-sound-null-safety once isolate package is null-safe. +"%dart%" --no-sound-null-safety --disable-dart-dev bin\format.dart %* & exit /B !ERRORLEVEL! diff --git a/ci/format.sh b/ci/format.sh index 6725635583f98..50475df4df335 100755 --- a/ci/format.sh +++ b/ci/format.sh @@ -27,15 +27,15 @@ function follow_links() ( echo "$file" ) - SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")") SRC_DIR="$(cd "$SCRIPT_DIR/../.."; pwd -P)" DART_SDK_DIR="${SRC_DIR}/third_party/dart/tools/sdks/dart-sdk" DART="${DART_SDK_DIR}/bin/dart" -PUB="${DART_SDK_DIR}/bin/pub" +# TODO(gspencergoog): Remove --no-sound-null-safety once isolate package is null-safe. cd "$SCRIPT_DIR" -"$PUB" get && "$DART" \ +"$DART" \ --disable-dart-dev \ + --no-sound-null-safety \ bin/format.dart \ "$@" diff --git a/ci/licenses.sh b/ci/licenses.sh index 48843bf58d761..15efdd80ca367 100755 --- a/ci/licenses.sh +++ b/ci/licenses.sh @@ -33,6 +33,14 @@ SRC_DIR="$(cd "$SCRIPT_DIR/../.."; pwd -P)" DART_BIN="$SRC_DIR/third_party/dart/tools/sdks/dart-sdk/bin" PATH="$DART_BIN:$PATH" +# Use: +# env VERBOSE=1 ./ci/licenses.sh +# to turn on verbose progress report printing. +QUIET="--quiet" +if [[ "${VERBOSE}" == "1" ]]; then + QUIET="" +fi + echo "Verifying license script is still happy..." echo "Using pub from $(command -v pub), dart from $(command -v dart)" @@ -54,11 +62,11 @@ dart --version # Runs in a subshell. function collect_licenses() ( cd "$SRC_DIR/flutter/tools/licenses" - pub get dart --enable-asserts lib/main.dart \ --src ../../.. \ --out ../../../out/license_script_output \ - --golden ../../ci/licenses_golden + --golden ../../ci/licenses_golden \ + "${QUIET}" ) # Verifies the licenses in the repo. @@ -113,7 +121,7 @@ function verify_licenses() ( local actualLicenseCount actualLicenseCount="$(tail -n 1 flutter/ci/licenses_golden/licenses_flutter | tr -dc '0-9')" - local expectedLicenseCount=2 # When changing this number: Update the error message below as well describing all expected license types. + local expectedLicenseCount=16 # When changing this number: Update the error message below as well describing all expected license types. if [[ $actualLicenseCount -ne $expectedLicenseCount ]]; then echo "=============================== ERROR ===============================" @@ -137,4 +145,4 @@ function verify_licenses() ( return $exitStatus ) -verify_licenses \ No newline at end of file +verify_licenses diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8b6299b8900f8..0080651086c37 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5,6 +5,7 @@ UNUSED LICENSES: USED LICENSES: ==================================================================================================== +LIBRARY: accessibility LIBRARY: engine LIBRARY: tonic LIBRARY: txt @@ -21,20 +22,37 @@ FILE: ../../../flutter/benchmarking/benchmarking.cc FILE: ../../../flutter/benchmarking/benchmarking.h FILE: ../../../flutter/common/constants.h FILE: ../../../flutter/common/exported_symbols.sym +FILE: ../../../flutter/common/graphics/gl_context_switch.cc +FILE: ../../../flutter/common/graphics/gl_context_switch.h +FILE: ../../../flutter/common/graphics/persistent_cache.cc +FILE: ../../../flutter/common/graphics/persistent_cache.h +FILE: ../../../flutter/common/graphics/texture.cc +FILE: ../../../flutter/common/graphics/texture.h FILE: ../../../flutter/common/settings.cc FILE: ../../../flutter/common/settings.h FILE: ../../../flutter/common/task_runners.cc FILE: ../../../flutter/common/task_runners.h FILE: ../../../flutter/flow/compositor_context.cc FILE: ../../../flutter/flow/compositor_context.h +FILE: ../../../flutter/flow/diff_context.cc +FILE: ../../../flutter/flow/diff_context.h +FILE: ../../../flutter/flow/display_list.cc +FILE: ../../../flutter/flow/display_list.h +FILE: ../../../flutter/flow/display_list_canvas.cc +FILE: ../../../flutter/flow/display_list_canvas.h +FILE: ../../../flutter/flow/display_list_canvas_unittests.cc +FILE: ../../../flutter/flow/display_list_unittests.cc +FILE: ../../../flutter/flow/display_list_utils.cc +FILE: ../../../flutter/flow/display_list_utils.h FILE: ../../../flutter/flow/embedded_view_params_unittests.cc FILE: ../../../flutter/flow/embedded_views.cc FILE: ../../../flutter/flow/embedded_views.h FILE: ../../../flutter/flow/flow_run_all_unittests.cc FILE: ../../../flutter/flow/flow_test_utils.cc FILE: ../../../flutter/flow/flow_test_utils.h -FILE: ../../../flutter/flow/gl_context_switch.cc -FILE: ../../../flutter/flow/gl_context_switch.h +FILE: ../../../flutter/flow/frame_timings.cc +FILE: ../../../flutter/flow/frame_timings.h +FILE: ../../../flutter/flow/frame_timings_recorder_unittests.cc FILE: ../../../flutter/flow/gl_context_switch_unittests.cc FILE: ../../../flutter/flow/instrumentation.cc FILE: ../../../flutter/flow/instrumentation.h @@ -42,8 +60,6 @@ FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/checkerboard_layertree_unittests.cc -FILE: ../../../flutter/flow/layers/child_scene_layer.cc -FILE: ../../../flutter/flow/layers/child_scene_layer.h FILE: ../../../flutter/flow/layers/clip_path_layer.cc FILE: ../../../flutter/flow/layers/clip_path_layer.h FILE: ../../../flutter/flow/layers/clip_path_layer_unittests.cc @@ -59,7 +75,9 @@ FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h FILE: ../../../flutter/flow/layers/container_layer_unittests.cc -FILE: ../../../flutter/flow/layers/fuchsia_layer_unittests.cc +FILE: ../../../flutter/flow/layers/display_list_layer.cc +FILE: ../../../flutter/flow/layers/display_list_layer.h +FILE: ../../../flutter/flow/layers/display_list_layer_unittests.cc FILE: ../../../flutter/flow/layers/image_filter_layer.cc FILE: ../../../flutter/flow/layers/image_filter_layer.h FILE: ../../../flutter/flow/layers/image_filter_layer_unittests.cc @@ -96,6 +114,8 @@ FILE: ../../../flutter/flow/matrix_decomposition.cc FILE: ../../../flutter/flow/matrix_decomposition.h FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc FILE: ../../../flutter/flow/mutators_stack_unittests.cc +FILE: ../../../flutter/flow/paint_region.cc +FILE: ../../../flutter/flow/paint_region.h FILE: ../../../flutter/flow/paint_utils.cc FILE: ../../../flutter/flow/paint_utils.h FILE: ../../../flutter/flow/raster_cache.cc @@ -106,8 +126,6 @@ FILE: ../../../flutter/flow/raster_cache_unittests.cc FILE: ../../../flutter/flow/rtree.cc FILE: ../../../flutter/flow/rtree.h FILE: ../../../flutter/flow/rtree_unittests.cc -FILE: ../../../flutter/flow/scene_update_context.cc -FILE: ../../../flutter/flow/scene_update_context.h FILE: ../../../flutter/flow/skia_gpu_object.cc FILE: ../../../flutter/flow/skia_gpu_object.h FILE: ../../../flutter/flow/skia_gpu_object_unittests.cc @@ -115,13 +133,7 @@ FILE: ../../../flutter/flow/surface.cc FILE: ../../../flutter/flow/surface.h FILE: ../../../flutter/flow/surface_frame.cc FILE: ../../../flutter/flow/surface_frame.h -FILE: ../../../flutter/flow/texture.cc -FILE: ../../../flutter/flow/texture.h FILE: ../../../flutter/flow/texture_unittests.cc -FILE: ../../../flutter/flow/view_holder.cc -FILE: ../../../flutter/flow/view_holder.h -FILE: ../../../flutter/flutter_frontend_server/bin/starter.dart -FILE: ../../../flutter/flutter_frontend_server/lib/server.dart FILE: ../../../flutter/fml/ascii_trie.cc FILE: ../../../flutter/fml/ascii_trie.h FILE: ../../../flutter/fml/ascii_trie_unittests.cc @@ -158,10 +170,12 @@ FILE: ../../../flutter/fml/log_settings.h FILE: ../../../flutter/fml/log_settings_state.cc FILE: ../../../flutter/fml/logging.cc FILE: ../../../flutter/fml/logging.h +FILE: ../../../flutter/fml/logging_unittests.cc FILE: ../../../flutter/fml/macros.h FILE: ../../../flutter/fml/make_copyable.h FILE: ../../../flutter/fml/mapping.cc FILE: ../../../flutter/fml/mapping.h +FILE: ../../../flutter/fml/mapping_unittests.cc FILE: ../../../flutter/fml/memory/ref_counted.h FILE: ../../../flutter/fml/memory/ref_counted_internal.h FILE: ../../../flutter/fml/memory/ref_counted_unittest.cc @@ -175,8 +189,6 @@ FILE: ../../../flutter/fml/memory/weak_ptr.h FILE: ../../../flutter/fml/memory/weak_ptr_internal.cc FILE: ../../../flutter/fml/memory/weak_ptr_internal.h FILE: ../../../flutter/fml/memory/weak_ptr_unittest.cc -FILE: ../../../flutter/fml/message.cc -FILE: ../../../flutter/fml/message.h FILE: ../../../flutter/fml/message_loop.cc FILE: ../../../flutter/fml/message_loop.h FILE: ../../../flutter/fml/message_loop_impl.cc @@ -187,7 +199,6 @@ FILE: ../../../flutter/fml/message_loop_task_queues_benchmark.cc FILE: ../../../flutter/fml/message_loop_task_queues_merge_unmerge_unittests.cc FILE: ../../../flutter/fml/message_loop_task_queues_unittests.cc FILE: ../../../flutter/fml/message_loop_unittests.cc -FILE: ../../../flutter/fml/message_unittests.cc FILE: ../../../flutter/fml/native_library.h FILE: ../../../flutter/fml/paths.cc FILE: ../../../flutter/fml/paths.h @@ -220,6 +231,8 @@ FILE: ../../../flutter/fml/platform/darwin/string_range_sanitization_unittests.m FILE: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.cc FILE: ../../../flutter/fml/platform/fuchsia/message_loop_fuchsia.h FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc +FILE: ../../../flutter/fml/platform/fuchsia/task_observers.cc +FILE: ../../../flutter/fml/platform/fuchsia/task_observers.h FILE: ../../../flutter/fml/platform/linux/message_loop_linux.cc FILE: ../../../flutter/fml/platform/linux/message_loop_linux.h FILE: ../../../flutter/fml/platform/linux/paths_linux.cc @@ -246,6 +259,8 @@ FILE: ../../../flutter/fml/posix_wrappers.h FILE: ../../../flutter/fml/raster_thread_merger.cc FILE: ../../../flutter/fml/raster_thread_merger.h FILE: ../../../flutter/fml/raster_thread_merger_unittests.cc +FILE: ../../../flutter/fml/shared_thread_merger.cc +FILE: ../../../flutter/fml/shared_thread_merger.h FILE: ../../../flutter/fml/size.h FILE: ../../../flutter/fml/status.h FILE: ../../../flutter/fml/synchronization/atomic_object.h @@ -264,20 +279,30 @@ FILE: ../../../flutter/fml/synchronization/sync_switch_unittest.cc FILE: ../../../flutter/fml/synchronization/waitable_event.cc FILE: ../../../flutter/fml/synchronization/waitable_event.h FILE: ../../../flutter/fml/synchronization/waitable_event_unittest.cc +FILE: ../../../flutter/fml/task_queue_id.h FILE: ../../../flutter/fml/task_runner.cc FILE: ../../../flutter/fml/task_runner.h +FILE: ../../../flutter/fml/task_source.cc +FILE: ../../../flutter/fml/task_source.h +FILE: ../../../flutter/fml/task_source_grade.h +FILE: ../../../flutter/fml/task_source_unittests.cc FILE: ../../../flutter/fml/thread.cc FILE: ../../../flutter/fml/thread.h FILE: ../../../flutter/fml/thread_local.cc FILE: ../../../flutter/fml/thread_local.h FILE: ../../../flutter/fml/thread_local_unittests.cc FILE: ../../../flutter/fml/thread_unittests.cc +FILE: ../../../flutter/fml/time/chrono_timestamp_provider.cc +FILE: ../../../flutter/fml/time/chrono_timestamp_provider.h +FILE: ../../../flutter/fml/time/dart_timestamp_provider.cc +FILE: ../../../flutter/fml/time/dart_timestamp_provider.h FILE: ../../../flutter/fml/time/time_delta.h FILE: ../../../flutter/fml/time/time_delta_unittest.cc FILE: ../../../flutter/fml/time/time_point.cc FILE: ../../../flutter/fml/time/time_point.h FILE: ../../../flutter/fml/time/time_point_unittest.cc FILE: ../../../flutter/fml/time/time_unittest.cc +FILE: ../../../flutter/fml/time/timestamp_provider.h FILE: ../../../flutter/fml/trace_event.cc FILE: ../../../flutter/fml/trace_event.h FILE: ../../../flutter/fml/unique_fd.cc @@ -288,6 +313,10 @@ FILE: ../../../flutter/lib/io/dart_io.cc FILE: ../../../flutter/lib/io/dart_io.h FILE: ../../../flutter/lib/snapshot/libraries.json FILE: ../../../flutter/lib/snapshot/snapshot.h +FILE: ../../../flutter/lib/spirv/lib/spirv.dart +FILE: ../../../flutter/lib/spirv/lib/src/constants.dart +FILE: ../../../flutter/lib/spirv/lib/src/transpiler.dart +FILE: ../../../flutter/lib/spirv/lib/src/types.dart FILE: ../../../flutter/lib/ui/annotations.dart FILE: ../../../flutter/lib/ui/channel_buffers.dart FILE: ../../../flutter/lib/ui/compositing.dart @@ -295,8 +324,7 @@ FILE: ../../../flutter/lib/ui/compositing/scene.cc FILE: ../../../flutter/lib/ui/compositing/scene.h FILE: ../../../flutter/lib/ui/compositing/scene_builder.cc FILE: ../../../flutter/lib/ui/compositing/scene_builder.h -FILE: ../../../flutter/lib/ui/compositing/scene_host.cc -FILE: ../../../flutter/lib/ui/compositing/scene_host.h +FILE: ../../../flutter/lib/ui/compositing/scene_builder_unittests.cc FILE: ../../../flutter/lib/ui/dart_runtime_hooks.cc FILE: ../../../flutter/lib/ui/dart_runtime_hooks.h FILE: ../../../flutter/lib/ui/dart_ui.cc @@ -310,14 +338,15 @@ FILE: ../../../flutter/lib/ui/fixtures/hello_loop_2.webp FILE: ../../../flutter/lib/ui/fixtures/ui_test.dart FILE: ../../../flutter/lib/ui/geometry.dart FILE: ../../../flutter/lib/ui/hash_codes.dart -FILE: ../../../flutter/lib/ui/hint_freed_delegate.h FILE: ../../../flutter/lib/ui/hooks.dart +FILE: ../../../flutter/lib/ui/hooks_unittests.cc FILE: ../../../flutter/lib/ui/io_manager.h FILE: ../../../flutter/lib/ui/isolate_name_server.dart FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h +FILE: ../../../flutter/lib/ui/key.dart FILE: ../../../flutter/lib/ui/lerp.dart FILE: ../../../flutter/lib/ui/natives.dart FILE: ../../../flutter/lib/ui/painting.dart @@ -344,6 +373,11 @@ FILE: ../../../flutter/lib/ui/painting/image_encoding.h FILE: ../../../flutter/lib/ui/painting/image_encoding_unittests.cc FILE: ../../../flutter/lib/ui/painting/image_filter.cc FILE: ../../../flutter/lib/ui/painting/image_filter.h +FILE: ../../../flutter/lib/ui/painting/image_generator.cc +FILE: ../../../flutter/lib/ui/painting/image_generator.h +FILE: ../../../flutter/lib/ui/painting/image_generator_registry.cc +FILE: ../../../flutter/lib/ui/painting/image_generator_registry.h +FILE: ../../../flutter/lib/ui/painting/image_generator_registry_unittests.cc FILE: ../../../flutter/lib/ui/painting/image_shader.cc FILE: ../../../flutter/lib/ui/painting/image_shader.h FILE: ../../../flutter/lib/ui/painting/immutable_buffer.cc @@ -358,6 +392,7 @@ FILE: ../../../flutter/lib/ui/painting/path.cc FILE: ../../../flutter/lib/ui/painting/path.h FILE: ../../../flutter/lib/ui/painting/path_measure.cc FILE: ../../../flutter/lib/ui/painting/path_measure.h +FILE: ../../../flutter/lib/ui/painting/path_unittests.cc FILE: ../../../flutter/lib/ui/painting/picture.cc FILE: ../../../flutter/lib/ui/painting/picture.h FILE: ../../../flutter/lib/ui/painting/picture_recorder.cc @@ -368,9 +403,11 @@ FILE: ../../../flutter/lib/ui/painting/shader.cc FILE: ../../../flutter/lib/ui/painting/shader.h FILE: ../../../flutter/lib/ui/painting/single_frame_codec.cc FILE: ../../../flutter/lib/ui/painting/single_frame_codec.h +FILE: ../../../flutter/lib/ui/painting/single_frame_codec_unittests.cc FILE: ../../../flutter/lib/ui/painting/vertices.cc FILE: ../../../flutter/lib/ui/painting/vertices.h FILE: ../../../flutter/lib/ui/painting/vertices_unittests.cc +FILE: ../../../flutter/lib/ui/platform_dispatcher.dart FILE: ../../../flutter/lib/ui/plugins.dart FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc FILE: ../../../flutter/lib/ui/plugins/callback_cache.h @@ -384,6 +421,9 @@ FILE: ../../../flutter/lib/ui/semantics/semantics_update.cc FILE: ../../../flutter/lib/ui/semantics/semantics_update.h FILE: ../../../flutter/lib/ui/semantics/semantics_update_builder.cc FILE: ../../../flutter/lib/ui/semantics/semantics_update_builder.h +FILE: ../../../flutter/lib/ui/semantics/semantics_update_builder_unittests.cc +FILE: ../../../flutter/lib/ui/semantics/string_attribute.cc +FILE: ../../../flutter/lib/ui/semantics/string_attribute.h FILE: ../../../flutter/lib/ui/snapshot_delegate.h FILE: ../../../flutter/lib/ui/text.dart FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.cc @@ -400,7 +440,13 @@ FILE: ../../../flutter/lib/ui/ui.dart FILE: ../../../flutter/lib/ui/ui_benchmarks.cc FILE: ../../../flutter/lib/ui/ui_dart_state.cc FILE: ../../../flutter/lib/ui/ui_dart_state.h +FILE: ../../../flutter/lib/ui/volatile_path_tracker.cc +FILE: ../../../flutter/lib/ui/volatile_path_tracker.h FILE: ../../../flutter/lib/ui/window.dart +FILE: ../../../flutter/lib/ui/window/key_data.cc +FILE: ../../../flutter/lib/ui/window/key_data.h +FILE: ../../../flutter/lib/ui/window/key_data_packet.cc +FILE: ../../../flutter/lib/ui/window/key_data_packet.h FILE: ../../../flutter/lib/ui/window/platform_configuration.cc FILE: ../../../flutter/lib/ui/window/platform_configuration.h FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc @@ -424,19 +470,19 @@ FILE: ../../../flutter/lib/ui/window/window.h FILE: ../../../flutter/lib/web_ui/lib/src/engine.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/alarm_clock.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/assets.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/bitmap_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_detection.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_location.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvas_pool.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/fonts.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/initialization.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/interval_tree.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart @@ -453,56 +499,79 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/shader.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/vertices.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/viewport_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/clipboard.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/color_filter.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_renderer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/font_change_util.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/history.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/host_node.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/clip.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/dom_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/image_filter.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/offscreen_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/offset.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/opacity.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/painting.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/conic.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/cubic.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path_iterator.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path_metrics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path_ref.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path_utils.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/path_windings.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path/tangent.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/picture.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/platform_view.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/recording_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/render_vertices.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/scene.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shader.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shader_mask.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/shaders/webgl_context.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/key_map.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/url_strategy.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/onscreen_logging.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/picture.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views/content_manager.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views/message_handler.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views/slots.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_converter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/profiler.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/image.dart @@ -514,18 +583,23 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/tappable.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/text_field.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/services.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/buffers.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codec.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/message_codecs.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/services/serialization.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/test_embedding.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/font_collection.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/layout_service.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/line_break_properties.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/line_breaker.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/measurement.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/paint_service.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/paragraph.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/ruler.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/text_direction.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/unicode_range.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/word_break_properties.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/text/word_breaker.dart @@ -546,11 +620,13 @@ FILE: ../../../flutter/lib/web_ui/lib/src/ui/compositing.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/geometry.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/hash_codes.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/initialization.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/key.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/lerp.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/natives.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/painting.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/path.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/path_metrics.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/platform_dispatcher.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/pointer.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/semantics.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/test_embedding.dart @@ -574,12 +650,19 @@ FILE: ../../../flutter/runtime/dart_vm.cc FILE: ../../../flutter/runtime/dart_vm.h FILE: ../../../flutter/runtime/dart_vm_data.cc FILE: ../../../flutter/runtime/dart_vm_data.h +FILE: ../../../flutter/runtime/dart_vm_initializer.cc +FILE: ../../../flutter/runtime/dart_vm_initializer.h FILE: ../../../flutter/runtime/dart_vm_lifecycle.cc FILE: ../../../flutter/runtime/dart_vm_lifecycle.h FILE: ../../../flutter/runtime/dart_vm_unittests.cc FILE: ../../../flutter/runtime/embedder_resources.cc FILE: ../../../flutter/runtime/embedder_resources.h +FILE: ../../../flutter/runtime/fixtures/no_dart_plugin_registrant_test.dart FILE: ../../../flutter/runtime/fixtures/runtime_test.dart +FILE: ../../../flutter/runtime/fixtures/split_lib_test.dart +FILE: ../../../flutter/runtime/isolate_configuration.cc +FILE: ../../../flutter/runtime/isolate_configuration.h +FILE: ../../../flutter/runtime/no_dart_plugin_registrant_unittests.cc FILE: ../../../flutter/runtime/platform_data.cc FILE: ../../../flutter/runtime/platform_data.h FILE: ../../../flutter/runtime/ptrace_check.cc @@ -594,12 +677,15 @@ FILE: ../../../flutter/runtime/skia_concurrent_executor.cc FILE: ../../../flutter/runtime/skia_concurrent_executor.h FILE: ../../../flutter/runtime/test_font_data.cc FILE: ../../../flutter/runtime/test_font_data.h +FILE: ../../../flutter/runtime/type_conversions_unittests.cc FILE: ../../../flutter/shell/common/animator.cc FILE: ../../../flutter/shell/common/animator.h FILE: ../../../flutter/shell/common/animator_unittests.cc FILE: ../../../flutter/shell/common/canvas_spy.cc FILE: ../../../flutter/shell/common/canvas_spy.h FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc +FILE: ../../../flutter/shell/common/context_options.cc +FILE: ../../../flutter/shell/common/context_options.h FILE: ../../../flutter/shell/common/display.h FILE: ../../../flutter/shell/common/display_manager.cc FILE: ../../../flutter/shell/common/display_manager.h @@ -609,10 +695,6 @@ FILE: ../../../flutter/shell/common/engine_unittests.cc FILE: ../../../flutter/shell/common/fixtures/shell_test.dart FILE: ../../../flutter/shell/common/fixtures/shelltest_screenshot.png FILE: ../../../flutter/shell/common/input_events_unittests.cc -FILE: ../../../flutter/shell/common/isolate_configuration.cc -FILE: ../../../flutter/shell/common/isolate_configuration.h -FILE: ../../../flutter/shell/common/persistent_cache.cc -FILE: ../../../flutter/shell/common/persistent_cache.h FILE: ../../../flutter/shell/common/persistent_cache_unittests.cc FILE: ../../../flutter/shell/common/pipeline.cc FILE: ../../../flutter/shell/common/pipeline.h @@ -623,6 +705,7 @@ FILE: ../../../flutter/shell/common/pointer_data_dispatcher.cc FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h FILE: ../../../flutter/shell/common/rasterizer.cc FILE: ../../../flutter/shell/common/rasterizer.h +FILE: ../../../flutter/shell/common/rasterizer_unittests.cc FILE: ../../../flutter/shell/common/run_configuration.cc FILE: ../../../flutter/shell/common/run_configuration.h FILE: ../../../flutter/shell/common/serialization_callbacks.cc @@ -630,6 +713,7 @@ FILE: ../../../flutter/shell/common/serialization_callbacks.h FILE: ../../../flutter/shell/common/shell.cc FILE: ../../../flutter/shell/common/shell.h FILE: ../../../flutter/shell/common/shell_benchmarks.cc +FILE: ../../../flutter/shell/common/shell_fuchsia_unittests.cc FILE: ../../../flutter/shell/common/shell_io_manager.cc FILE: ../../../flutter/shell/common/shell_io_manager.h FILE: ../../../flutter/shell/common/shell_test.cc @@ -640,14 +724,18 @@ FILE: ../../../flutter/shell/common/shell_test_platform_view.cc FILE: ../../../flutter/shell/common/shell_test_platform_view.h FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.cc FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.h +FILE: ../../../flutter/shell/common/shell_test_platform_view_metal.h +FILE: ../../../flutter/shell/common/shell_test_platform_view_metal.mm FILE: ../../../flutter/shell/common/shell_test_platform_view_vulkan.cc FILE: ../../../flutter/shell/common/shell_test_platform_view_vulkan.h FILE: ../../../flutter/shell/common/shell_unittests.cc FILE: ../../../flutter/shell/common/skia_event_tracer_impl.cc FILE: ../../../flutter/shell/common/skia_event_tracer_impl.h FILE: ../../../flutter/shell/common/skp_shader_warmup_unittests.cc +FILE: ../../../flutter/shell/common/snapshot_surface_producer.h FILE: ../../../flutter/shell/common/switches.cc FILE: ../../../flutter/shell/common/switches.h +FILE: ../../../flutter/shell/common/switches_unittests.cc FILE: ../../../flutter/shell/common/thread_host.cc FILE: ../../../flutter/shell/common/thread_host.h FILE: ../../../flutter/shell/common/vsync_waiter.cc @@ -656,13 +744,14 @@ FILE: ../../../flutter/shell/common/vsync_waiter_fallback.cc FILE: ../../../flutter/shell/common/vsync_waiter_fallback.h FILE: ../../../flutter/shell/common/vsync_waiters_test.cc FILE: ../../../flutter/shell/common/vsync_waiters_test.h -FILE: ../../../flutter/shell/gpu/gpu_surface_delegate.h FILE: ../../../flutter/shell/gpu/gpu_surface_gl.cc FILE: ../../../flutter/shell/gpu/gpu_surface_gl.h FILE: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.cc FILE: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.h FILE: ../../../flutter/shell/gpu/gpu_surface_metal.h FILE: ../../../flutter/shell/gpu/gpu_surface_metal.mm +FILE: ../../../flutter/shell/gpu/gpu_surface_metal_delegate.cc +FILE: ../../../flutter/shell/gpu/gpu_surface_metal_delegate.h FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc FILE: ../../../flutter/shell/gpu/gpu_surface_software.h FILE: ../../../flutter/shell/gpu/gpu_surface_software_delegate.cc @@ -679,6 +768,8 @@ FILE: ../../../flutter/shell/platform/android/android_environment_gl.h FILE: ../../../flutter/shell/platform/android/android_exports.lst FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.cc FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.h +FILE: ../../../flutter/shell/platform/android/android_image_generator.cc +FILE: ../../../flutter/shell/platform/android/android_image_generator.h FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc FILE: ../../../flutter/shell/platform/android/android_shell_holder.h FILE: ../../../flutter/shell/platform/android/android_surface_gl.cc @@ -706,8 +797,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDele FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEvents.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterApplication.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java +FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/DrawableSplashScreen.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java @@ -723,6 +814,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyChannelResponder.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyboardManager.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/MotionEventTracker.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/RenderMode.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/SplashScreen.java @@ -731,12 +824,15 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Trans FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineCache.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroup.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterOverlaySurface.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterApplicationInfo.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -764,7 +860,9 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java @@ -795,7 +893,9 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardM FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringCodec.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ListenableEditingState.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/mouse/MouseCursorPlugin.java @@ -812,6 +912,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Virtual FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/Predicate.java +FILE: ../../../flutter/shell/platform/android/io/flutter/util/ViewUtils.java FILE: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java FILE: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java @@ -842,63 +943,81 @@ FILE: ../../../flutter/shell/platform/android/surface/android_surface.cc FILE: ../../../flutter/shell/platform/android/surface/android_surface.h FILE: ../../../flutter/shell/platform/android/surface/android_surface_mock.cc FILE: ../../../flutter/shell/platform/android/surface/android_surface_mock.h +FILE: ../../../flutter/shell/platform/android/surface/snapshot_surface_producer.cc +FILE: ../../../flutter/shell/platform/android/surface/snapshot_surface_producer.h FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.cc FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/binary_messenger_impl.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/byte_buffer_streams.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/core_implementations.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/encodable_value_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/engine_method_result.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/event_channel_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/byte_streams.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/event_channel.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/event_sink.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/event_stream_handler.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/event_stream_handler_functions.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registry.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/engine_switches.cc -FILE: ../../../flutter/shell/platform/common/cpp/engine_switches.h -FILE: ../../../flutter/shell/platform/common/cpp/engine_switches_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.cc -FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.h -FILE: ../../../flutter/shell/platform/common/cpp/json_message_codec.cc -FILE: ../../../flutter/shell/platform/common/cpp/json_message_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/json_message_codec_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/json_method_codec.cc -FILE: ../../../flutter/shell/platform/common/cpp/json_method_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/json_method_codec_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/path_utils.cc -FILE: ../../../flutter/shell/platform/common/cpp/path_utils.h -FILE: ../../../flutter/shell/platform/common/cpp/path_utils_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_export.h -FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_messenger.h -FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_plugin_registrar.h -FILE: ../../../flutter/shell/platform/common/cpp/text_input_model.cc -FILE: ../../../flutter/shell/platform/common/cpp/text_input_model.h -FILE: ../../../flutter/shell/platform/common/cpp/text_input_model_unittests.cc +FILE: ../../../flutter/shell/platform/common/accessibility_bridge.cc +FILE: ../../../flutter/shell/platform/common/accessibility_bridge.h +FILE: ../../../flutter/shell/platform/common/accessibility_bridge_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/basic_message_channel_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/encodable_value_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/engine_method_result.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/event_channel_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/byte_streams.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/engine_method_result.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/event_channel.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/event_sink.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/event_stream_handler.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/event_stream_handler_functions.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/message_codec.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/method_call.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/method_channel.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/method_codec.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/method_result.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registrar.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/plugin_registry.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/standard_codec_serializer.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/include/flutter/texture_registrar.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/method_call_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/method_channel_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/method_result_functions_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/plugin_registrar.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/plugin_registrar_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/standard_codec.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/standard_message_codec_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/standard_method_codec_unittests.cc +FILE: ../../../flutter/shell/platform/common/client_wrapper/texture_registrar_impl.h +FILE: ../../../flutter/shell/platform/common/client_wrapper/texture_registrar_unittests.cc +FILE: ../../../flutter/shell/platform/common/engine_switches.cc +FILE: ../../../flutter/shell/platform/common/engine_switches.h +FILE: ../../../flutter/shell/platform/common/engine_switches_unittests.cc +FILE: ../../../flutter/shell/platform/common/flutter_platform_node_delegate.cc +FILE: ../../../flutter/shell/platform/common/flutter_platform_node_delegate.h +FILE: ../../../flutter/shell/platform/common/flutter_platform_node_delegate_unittests.cc +FILE: ../../../flutter/shell/platform/common/geometry.h +FILE: ../../../flutter/shell/platform/common/geometry_unittests.cc +FILE: ../../../flutter/shell/platform/common/incoming_message_dispatcher.cc +FILE: ../../../flutter/shell/platform/common/incoming_message_dispatcher.h +FILE: ../../../flutter/shell/platform/common/json_message_codec.cc +FILE: ../../../flutter/shell/platform/common/json_message_codec.h +FILE: ../../../flutter/shell/platform/common/json_message_codec_unittests.cc +FILE: ../../../flutter/shell/platform/common/json_method_codec.cc +FILE: ../../../flutter/shell/platform/common/json_method_codec.h +FILE: ../../../flutter/shell/platform/common/json_method_codec_unittests.cc +FILE: ../../../flutter/shell/platform/common/path_utils.cc +FILE: ../../../flutter/shell/platform/common/path_utils.h +FILE: ../../../flutter/shell/platform/common/path_utils_unittests.cc +FILE: ../../../flutter/shell/platform/common/public/flutter_export.h +FILE: ../../../flutter/shell/platform/common/public/flutter_messenger.h +FILE: ../../../flutter/shell/platform/common/public/flutter_plugin_registrar.h +FILE: ../../../flutter/shell/platform/common/public/flutter_texture_registrar.h +FILE: ../../../flutter/shell/platform/common/test_accessibility_bridge.cc +FILE: ../../../flutter/shell/platform/common/test_accessibility_bridge.h +FILE: ../../../flutter/shell/platform/common/text_input_model.cc +FILE: ../../../flutter/shell/platform/common/text_input_model.h +FILE: ../../../flutter/shell/platform/common/text_input_model_unittests.cc +FILE: ../../../flutter/shell/platform/common/text_range.h +FILE: ../../../flutter/shell/platform/common/text_range_unittests.cc FILE: ../../../flutter/shell/platform/darwin/common/buffer_conversions.h FILE: ../../../flutter/shell/platform/darwin/common/buffer_conversions.mm FILE: ../../../flutter/shell/platform/darwin/common/command_line.h @@ -915,12 +1034,17 @@ FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStan FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm +FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.h +FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetal.mm +FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h +FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Flutter.podspec FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/Flutter.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -928,20 +1052,38 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Info.plist FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegateTest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Test.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelayTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterChannelKeyResponder.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterChannelKeyResponder.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterChannelKeyResponderTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponderTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest_mrc.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyPrimaryResponder.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeySecondaryResponder.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManagerTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h @@ -954,19 +1096,27 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatfor FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegateTest.m +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegateTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPluginTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/IOKit.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/KeyCodeMap.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/KeyCodeMap_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm @@ -1000,6 +1150,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_metal.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_view_embedder.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_view_embedder.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_render_target_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_render_target_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h @@ -1025,25 +1177,91 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlug FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Info.plist +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegateTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponderUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderExternalTextureUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponderUnittests.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm -FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureMetal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureMetal.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProviderUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMacOSExternalTexture.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRendererTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalSurfaceManagerTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm -FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.h -FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart FILE: ../../../flutter/shell/platform/darwin/macos/framework/module.modulemap FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist @@ -1054,6 +1272,10 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_engine.cc FILE: ../../../flutter/shell/platform/embedder/embedder_engine.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.cc FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.h +FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_metal.h +FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_metal.mm +FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_resolver.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_resolver.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_view.cc FILE: ../../../flutter/shell/platform/embedder/embedder_external_view.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.cc @@ -1068,11 +1290,13 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.cc FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.h FILE: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.cc FILE: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.h -FILE: ../../../flutter/shell/platform/embedder/embedder_safe_access.h +FILE: ../../../flutter/shell/platform/embedder/embedder_struct_macros.h FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.h +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_metal.h +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_metal.mm FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.cc FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.cc @@ -1087,32 +1311,55 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_platform FILE: ../../../flutter/shell/platform/embedder/fixtures/compositor_with_root_layer_only.png FILE: ../../../flutter/shell/platform/embedder/fixtures/dpr_noxform.png FILE: ../../../flutter/shell/platform/embedder/fixtures/dpr_xform.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/external_texture_metal.png FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_metal.png FILE: ../../../flutter/shell/platform/embedder/fixtures/gradient_xform.png FILE: ../../../flutter/shell/platform/embedder/fixtures/main.dart FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor.png FILE: ../../../flutter/shell/platform/embedder/fixtures/scene_without_custom_compositor_with_xform.png +FILE: ../../../flutter/shell/platform/embedder/fixtures/snapshot_large_scene.png FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703.png FILE: ../../../flutter/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h +FILE: ../../../flutter/shell/platform/embedder/test_utils/key_codes.h +FILE: ../../../flutter/shell/platform/embedder/test_utils/proc_table_replacement.h FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.cc FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.h FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/fuchsia/lib/fuchsia.dart FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/fuchsia/sdk_ext/fuchsia.cc FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/fuchsia/sdk_ext/fuchsia.h FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_disposition.dart FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle_waiter.dart +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/init.dart FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_channel.dart +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_handle.dart FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/lib/zircon.dart FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/handle.cc FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/handle.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/handle_disposition.cc +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/handle_disposition.h FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/handle_waiter.cc FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/handle_waiter.h FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/natives.cc FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/natives.h FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/system.cc FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon/sdk_ext/system.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/basic_types.cc +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/basic_types.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/channel.cc +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/channel.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/clock.cc +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/clock.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/dart_dl.cc +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/dart_dl.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/handle.cc +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/handle.h +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/lib/zircon_ffi.dart +FILE: ../../../flutter/shell/platform/fuchsia/dart-pkg/zircon_ffi/macros.h FILE: ../../../flutter/shell/platform/fuchsia/dart/compiler.dart FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/builtin_libraries.cc FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/builtin_libraries.h @@ -1126,24 +1373,6 @@ FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/embedder/shim.dart FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/embedder/snapshot.cc.tmpl FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/embedder/snapshot.dart FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/embedder/snapshot.h -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/goodbye_dart.dart -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/goodbye_dart_test -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/meta/goodbye_dart_aot.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/meta/goodbye_dart_jit.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/greeting/greeting.dart -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_app_dart/interfaces/hello.fidl -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_app_dart/main.dart -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_app_dart/meta/hello_app_dart_aot.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_app_dart/meta/hello_app_dart_jit.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_dart/bin/hello_dart.dart -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_dart/meta/hello_dart_aot.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_dart/meta/hello_dart_aot_product.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_dart/meta/hello_dart_debug.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_dart/meta/hello_dart_jit.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/examples/hello_dart/meta/hello_dart_jit_product.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/integration/main.dart -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/integration/meta/dart_aot_runner_test.cmx -FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/integration/meta/dart_jit_runner_test.cmx FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/kernel/libraries.json FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/logging.h FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/main.cc @@ -1163,30 +1392,33 @@ FILE: ../../../flutter/shell/platform/fuchsia/dart_runner/vmservice/meta/vmservi FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/collect_traces.dart FILE: ../../../flutter/shell/platform/fuchsia/flutter/component.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/component.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_unittest.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/compositor_context.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/compositor_context.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_fakes.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate_unittests.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl_unittest.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/gfx_session_connection.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/gfx_session_connection.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/isolate_configurator.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/isolate_configurator.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/extract_far.dart FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/framework_shim.dart FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/libraries.json +FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/logging.h -FILE: ../../../flutter/shell/platform/fuchsia/flutter/loop.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/loop.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/main.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/aot_product_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/aot_runtime @@ -1194,9 +1426,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_product_r FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx -FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_scenic_tests.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx -FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_product_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_runtime FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view.cc @@ -1206,22 +1436,13 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_unittest.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/session_connection.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/session_connection.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.h -FILE: ../../../flutter/shell/platform/fuchsia/flutter/task_observers.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/task_observers.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/task_runner_adapter.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/task_runner_adapter.h -FILE: ../../../flutter/shell/platform/fuchsia/flutter/thread.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/thread.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/unique_fdio_ns.h -FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_recorder.cc -FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_recorder.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_waiter.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_waiter.h -FILE: ../../../flutter/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc @@ -1229,6 +1450,9 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart +FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/build_info.h +FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/build_info_in.cc +FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/build_info_unittests.cc FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/files.cc FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/files.h FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc @@ -1237,6 +1461,8 @@ FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/inlines.h FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/logging.h FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/mapped_resource.cc FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/mapped_resource.h +FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/root_inspect_node.cc +FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/root_inspect_node.h FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/tempfs.cc FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/tempfs.h FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/vmo.cc @@ -1252,6 +1478,7 @@ FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutte FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window.h FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/plugin_registrar_glfw.h +FILE: ../../../flutter/shell/platform/glfw/client_wrapper/plugin_registrar_glfw_unittests.cc FILE: ../../../flutter/shell/platform/glfw/event_loop.cc FILE: ../../../flutter/shell/platform/glfw/event_loop.h FILE: ../../../flutter/shell/platform/glfw/flutter_glfw.cc @@ -1265,11 +1492,18 @@ FILE: ../../../flutter/shell/platform/glfw/keyboard_hook_handler.h FILE: ../../../flutter/shell/platform/glfw/platform_handler.cc FILE: ../../../flutter/shell/platform/glfw/platform_handler.h FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h +FILE: ../../../flutter/shell/platform/glfw/system_utils.cc +FILE: ../../../flutter/shell/platform/glfw/system_utils.h +FILE: ../../../flutter/shell/platform/glfw/system_utils_test.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h -FILE: ../../../flutter/shell/platform/linux/egl_utils.cc -FILE: ../../../flutter/shell/platform/linux/egl_utils.h -FILE: ../../../flutter/shell/platform/linux/egl_utils_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_accessibility_plugin.cc +FILE: ../../../flutter/shell/platform/linux/fl_accessibility_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_accessible_node.cc +FILE: ../../../flutter/shell/platform/linux/fl_accessible_node.h +FILE: ../../../flutter/shell/platform/linux/fl_accessible_node_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_backing_store_provider.cc +FILE: ../../../flutter/shell/platform/linux/fl_backing_store_provider.h FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel_test.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec.cc @@ -1278,16 +1512,33 @@ FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger_private.h FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger_test.cc FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc +FILE: ../../../flutter/shell/platform/linux/fl_dart_project_private.h FILE: ../../../flutter/shell/platform/linux/fl_dart_project_test.cc FILE: ../../../flutter/shell/platform/linux/fl_engine.cc FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h +FILE: ../../../flutter/shell/platform/linux/fl_engine_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_event_channel.cc +FILE: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_gl_area.cc +FILE: ../../../flutter/shell/platform/linux/fl_gl_area.h FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec_test.cc -FILE: ../../../flutter/shell/platform/linux/fl_key_event_plugin.cc -FILE: ../../../flutter/shell/platform/linux/fl_key_event_plugin.h -FILE: ../../../flutter/shell/platform/linux/fl_key_event_plugin_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_channel_responder.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_channel_responder.h +FILE: ../../../flutter/shell/platform/linux/fl_key_channel_responder_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder.h +FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_private.h +FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_event.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_event.h +FILE: ../../../flutter/shell/platform/linux/fl_key_responder.cc +FILE: ../../../flutter/shell/platform/linux/fl_key_responder.h +FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager.cc +FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager.h +FILE: ../../../flutter/shell/platform/linux/fl_keyboard_manager_test.cc FILE: ../../../flutter/shell/platform/linux/fl_message_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_message_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_method_call.cc @@ -1309,12 +1560,12 @@ FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h FILE: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.h +FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.h FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.h -FILE: ../../../flutter/shell/platform/linux/fl_renderer_wayland.cc -FILE: ../../../flutter/shell/platform/linux/fl_renderer_wayland.h -FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc -FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.h +FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.cc +FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc @@ -1322,16 +1573,24 @@ FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc +FILE: ../../../flutter/shell/platform/linux/fl_task_runner.cc +FILE: ../../../flutter/shell/platform/linux/fl_task_runner.h FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.cc FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_value.cc FILE: ../../../flutter/shell/platform/linux/fl_value_test.cc FILE: ../../../flutter/shell/platform/linux/fl_view.cc +FILE: ../../../flutter/shell/platform/linux/fl_view_accessible.cc +FILE: ../../../flutter/shell/platform/linux/fl_view_accessible.h +FILE: ../../../flutter/shell/platform/linux/fl_view_private.h +FILE: ../../../flutter/shell/platform/linux/key_mapping.cc +FILE: ../../../flutter/shell/platform/linux/key_mapping.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_engine.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_event_channel.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h @@ -1363,51 +1622,119 @@ FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plu FILE: ../../../flutter/shell/platform/windows/client_wrapper/plugin_registrar_windows_unittests.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.h +FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.h +FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.cc +FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.h +FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32_unittests.cc +FILE: ../../../flutter/shell/platform/windows/external_texture_gl.cc +FILE: ../../../flutter/shell/platform/windows/external_texture_gl.h +FILE: ../../../flutter/shell/platform/windows/flutter_key_map.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.cc FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc +FILE: ../../../flutter/shell/platform/windows/flutter_window_win32.cc +FILE: ../../../flutter/shell/platform/windows/flutter_window_win32.h +FILE: ../../../flutter/shell/platform/windows/flutter_window_win32_unittests.cc +FILE: ../../../flutter/shell/platform/windows/flutter_window_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/flutter_window_winuwp.h FILE: ../../../flutter/shell/platform/windows/flutter_windows.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.h +FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine_unittests.cc +FILE: ../../../flutter/shell/platform/windows/flutter_windows_texture_registrar.cc +FILE: ../../../flutter/shell/platform/windows/flutter_windows_texture_registrar.h +FILE: ../../../flutter/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_view.cc FILE: ../../../flutter/shell/platform/windows/flutter_windows_view.h -FILE: ../../../flutter/shell/platform/windows/key_event_handler.cc -FILE: ../../../flutter/shell/platform/windows/key_event_handler.h -FILE: ../../../flutter/shell/platform/windows/keyboard_hook_handler.h +FILE: ../../../flutter/shell/platform/windows/flutter_windows_view_unittests.cc +FILE: ../../../flutter/shell/platform/windows/flutter_windows_win32.cc +FILE: ../../../flutter/shell/platform/windows/flutter_windows_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/game_pad_cursor_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/game_pad_cursor_winuwp.h +FILE: ../../../flutter/shell/platform/windows/game_pad_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/game_pad_winuwp.h +FILE: ../../../flutter/shell/platform/windows/keyboard_handler_base.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_channel_handler.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_channel_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_channel_handler_unittests.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_embedder_handler_unittests.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler.h +FILE: ../../../flutter/shell/platform/windows/keyboard_key_handler_unittests.cc +FILE: ../../../flutter/shell/platform/windows/keyboard_unittests.cc +FILE: ../../../flutter/shell/platform/windows/platform_handler.cc +FILE: ../../../flutter/shell/platform/windows/platform_handler.h +FILE: ../../../flutter/shell/platform/windows/platform_handler_unittests.cc +FILE: ../../../flutter/shell/platform/windows/platform_handler_win32.cc +FILE: ../../../flutter/shell/platform/windows/platform_handler_win32.h +FILE: ../../../flutter/shell/platform/windows/platform_handler_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/platform_handler_winuwp.h FILE: ../../../flutter/shell/platform/windows/public/flutter_windows.h +FILE: ../../../flutter/shell/platform/windows/sequential_id_generator.cc +FILE: ../../../flutter/shell/platform/windows/sequential_id_generator.h +FILE: ../../../flutter/shell/platform/windows/sequential_id_generator_unittests.cc 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/system_utils.h FILE: ../../../flutter/shell/platform/windows/system_utils_unittests.cc FILE: ../../../flutter/shell/platform/windows/system_utils_win32.cc +FILE: ../../../flutter/shell/platform/windows/system_utils_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/task_runner.h +FILE: ../../../flutter/shell/platform/windows/task_runner_win32.cc +FILE: ../../../flutter/shell/platform/windows/task_runner_win32.h +FILE: ../../../flutter/shell/platform/windows/task_runner_win32_window.cc +FILE: ../../../flutter/shell/platform/windows/task_runner_win32_window.h +FILE: ../../../flutter/shell/platform/windows/task_runner_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/task_runner_winuwp.h +FILE: ../../../flutter/shell/platform/windows/text_input_manager_win32.cc +FILE: ../../../flutter/shell/platform/windows/text_input_manager_win32.h FILE: ../../../flutter/shell/platform/windows/text_input_plugin.cc FILE: ../../../flutter/shell/platform/windows/text_input_plugin.h -FILE: ../../../flutter/shell/platform/windows/win32_dpi_utils.cc -FILE: ../../../flutter/shell/platform/windows/win32_dpi_utils.h -FILE: ../../../flutter/shell/platform/windows/win32_dpi_utils_unittests.cc -FILE: ../../../flutter/shell/platform/windows/win32_flutter_window.cc -FILE: ../../../flutter/shell/platform/windows/win32_flutter_window.h -FILE: ../../../flutter/shell/platform/windows/win32_flutter_window_unittests.cc -FILE: ../../../flutter/shell/platform/windows/win32_platform_handler.cc -FILE: ../../../flutter/shell/platform/windows/win32_platform_handler.h -FILE: ../../../flutter/shell/platform/windows/win32_task_runner.cc -FILE: ../../../flutter/shell/platform/windows/win32_task_runner.h -FILE: ../../../flutter/shell/platform/windows/win32_window.cc -FILE: ../../../flutter/shell/platform/windows/win32_window.h -FILE: ../../../flutter/shell/platform/windows/win32_window_proc_delegate_manager.cc -FILE: ../../../flutter/shell/platform/windows/win32_window_proc_delegate_manager.h -FILE: ../../../flutter/shell/platform/windows/win32_window_proc_delegate_manager_unittests.cc -FILE: ../../../flutter/shell/platform/windows/win32_window_unittests.cc +FILE: ../../../flutter/shell/platform/windows/text_input_plugin_delegate.h +FILE: ../../../flutter/shell/platform/windows/text_input_plugin_unittest.cc +FILE: ../../../flutter/shell/platform/windows/uwptool_commands.cc +FILE: ../../../flutter/shell/platform/windows/uwptool_commands.h +FILE: ../../../flutter/shell/platform/windows/uwptool_main.cc +FILE: ../../../flutter/shell/platform/windows/uwptool_utils.cc +FILE: ../../../flutter/shell/platform/windows/uwptool_utils.h +FILE: ../../../flutter/shell/platform/windows/uwptool_utils_unittests.cc FILE: ../../../flutter/shell/platform/windows/window_binding_handler.h FILE: ../../../flutter/shell/platform/windows/window_binding_handler_delegate.h +FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager_win32.cc +FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager_win32.h +FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager_win32_unittests.cc FILE: ../../../flutter/shell/platform/windows/window_state.h +FILE: ../../../flutter/shell/platform/windows/window_win32.cc +FILE: ../../../flutter/shell/platform/windows/window_win32.h +FILE: ../../../flutter/shell/platform/windows/window_win32_unittests.cc FILE: ../../../flutter/shell/profiling/sampling_profiler.cc FILE: ../../../flutter/shell/profiling/sampling_profiler.h +FILE: ../../../flutter/shell/profiling/sampling_profiler_unittest.cc FILE: ../../../flutter/shell/version/version.cc FILE: ../../../flutter/shell/version/version.h -FILE: ../../../flutter/sky/packages/flutter_services/lib/empty.dart FILE: ../../../flutter/sky/tools/roll/patches/chromium/android_build.patch +FILE: ../../../flutter/third_party/accessibility/base/color_utils.h +FILE: ../../../flutter/third_party/accessibility/base/compiler_specific.h +FILE: ../../../flutter/third_party/accessibility/base/container_utils.h +FILE: ../../../flutter/third_party/accessibility/base/logging.cc +FILE: ../../../flutter/third_party/accessibility/base/logging.h +FILE: ../../../flutter/third_party/accessibility/base/logging_unittests.cc +FILE: ../../../flutter/third_party/accessibility/base/macros.h +FILE: ../../../flutter/third_party/accessibility/base/platform/darwin/scoped_nsobject.h +FILE: ../../../flutter/third_party/accessibility/base/platform/darwin/scoped_nsobject.mm +FILE: ../../../flutter/third_party/accessibility/base/simple_token.cc +FILE: ../../../flutter/third_party/accessibility/base/simple_token.h +FILE: ../../../flutter/third_party/accessibility/base/string_utils.cc +FILE: ../../../flutter/third_party/accessibility/base/string_utils.h +FILE: ../../../flutter/third_party/accessibility/base/string_utils_unittest.cc +FILE: ../../../flutter/third_party/accessibility/base/win/string_conversion.cc +FILE: ../../../flutter/third_party/accessibility/base/win/string_conversion.h +FILE: ../../../flutter/third_party/accessibility/gfx/transform.cc +FILE: ../../../flutter/third_party/accessibility/gfx/transform.h FILE: ../../../flutter/third_party/tonic/common/build_config.h FILE: ../../../flutter/third_party/tonic/common/log.cc FILE: ../../../flutter/third_party/tonic/common/log.h @@ -1432,6 +1759,8 @@ FILE: ../../../flutter/third_party/tonic/dart_persistent_value.cc FILE: ../../../flutter/third_party/tonic/dart_persistent_value.h FILE: ../../../flutter/third_party/tonic/dart_state.cc FILE: ../../../flutter/third_party/tonic/dart_state.h +FILE: ../../../flutter/third_party/tonic/dart_weak_persistent_value.cc +FILE: ../../../flutter/third_party/tonic/dart_weak_persistent_value.h FILE: ../../../flutter/third_party/tonic/dart_wrappable.cc FILE: ../../../flutter/third_party/tonic/dart_wrappable.h FILE: ../../../flutter/third_party/tonic/dart_wrapper_info.h @@ -1505,8 +1834,6 @@ FILE: ../../../flutter/vulkan/vulkan_utilities.cc FILE: ../../../flutter/vulkan/vulkan_utilities.h FILE: ../../../flutter/vulkan/vulkan_window.cc FILE: ../../../flutter/vulkan/vulkan_window.h -FILE: ../../../flutter/web_sdk/libraries.json -FILE: ../../../flutter/web_sdk/sdk_rewriter.dart ---------------------------------------------------------------------------------------------------- Copyright 2013 The Flutter Authors. All rights reserved. @@ -1535,6 +1862,659 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: accessibility +ORIGIN: ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_id_registry.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_id_registry.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_base.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_base.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate_base.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_mac.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_mac.mm +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_conversions.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_conversions_impl.h +FILE: ../../../flutter/third_party/accessibility/gfx/mac/coordinate_conversion.h +FILE: ../../../flutter/third_party/accessibility/gfx/mac/coordinate_conversion.mm +---------------------------------------------------------------------------------------------------- +Copyright 2014 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax/ax_action_data.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_action_data.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_action_data.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_position.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_position.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_position_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_position.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_range.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_relative_bounds.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_relative_bounds.h +FILE: ../../../flutter/third_party/accessibility/gfx/range/gfx_range_export.h +---------------------------------------------------------------------------------------------------- +Copyright 2016 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax/ax_export.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_export.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_node.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_node.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_data.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_data.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_update.h +---------------------------------------------------------------------------------------------------- +Copyright 2013 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_active_popup.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_active_popup.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_clipping_behavior.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_constants.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_coordinate_system.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_enum_util_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_mode.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_text_styles.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_text_styles.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_offscreen_result.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_range_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_role_properties_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_manager.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_manager_map.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_manager_map.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_fragment_root_win.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_fragment_root_win.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_fragment_root_win_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_base_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate_utils_win.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate_utils_win.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_win_unittest.h +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_safearray.h +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_safearray_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright 2019 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate_base.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_enum_util.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_enum_util.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_enums.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_node_data_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_table_info.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_table_info.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_table_info_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_id.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_id.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_observer.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_observer.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate_base.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/compute_attributes.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/compute_attributes.h +FILE: ../../../flutter/third_party/accessibility/base/no_destructor.h +FILE: ../../../flutter/third_party/accessibility/base/win/atl.h +---------------------------------------------------------------------------------------------------- +Copyright 2018 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_unittest.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_action_handler.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_action_handler.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_event_generator.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_event_generator.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_event_generator_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_mode.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_mode_observer.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_role_properties.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_role_properties.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_unittest.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_relation_win.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_relation_win.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_unique_id.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_unique_id.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/checked_math.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/checked_math_impl.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/clamped_math.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/clamped_math_impl.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/math_constants.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/ranges.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_conversions_arm_impl.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_math.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_math_arm_impl.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_math_clang_gcc_impl.h +FILE: ../../../flutter/third_party/accessibility/base/numerics/safe_math_shared_impl.h +---------------------------------------------------------------------------------------------------- +Copyright 2017 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax/platform/uia_registrar_win.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_action_handler_base.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_action_handler_base.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_base_export.h +FILE: ../../../flutter/third_party/accessibility/ax/ax_event_intent.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_event_intent.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/uia_registrar_win.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/uia_registrar_win.h +FILE: ../../../flutter/third_party/accessibility/ax/test_ax_node_helper.cc +FILE: ../../../flutter/third_party/accessibility/ax/test_ax_node_helper.h +FILE: ../../../flutter/third_party/accessibility/ax/test_ax_tree_manager.cc +FILE: ../../../flutter/third_party/accessibility/ax/test_ax_tree_manager.h +FILE: ../../../flutter/third_party/accessibility/base/win/dispatch_stub.cc +FILE: ../../../flutter/third_party/accessibility/base/win/dispatch_stub.h +FILE: ../../../flutter/third_party/accessibility/base/win/variant_util.h +FILE: ../../../flutter/third_party/accessibility/base/win/variant_vector.cc +FILE: ../../../flutter/third_party/accessibility/base/win/variant_vector.h +FILE: ../../../flutter/third_party/accessibility/base/win/variant_vector_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright 2020 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/ax_build/build_config.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax_build/build_config.h +FILE: ../../../flutter/third_party/accessibility/base/base_export.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/insets.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/insets_f.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/insets_f.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point_conversions.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point_conversions.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point_f.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point_f.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/point_unittest.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect_conversions.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect_conversions.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect_f.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect_f.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size_conversions.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size_conversions.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size_f.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size_f.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/size_unittest.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d_conversions.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d_conversions.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d_f.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d_f.h +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/vector2d_unittest.cc +FILE: ../../../flutter/third_party/accessibility/gfx/native_widget_types.h +FILE: ../../../flutter/third_party/accessibility/gfx/range/range.cc +FILE: ../../../flutter/third_party/accessibility/gfx/range/range.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2012 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/base/win/atl_module.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/base/auto_reset.h +FILE: ../../../flutter/third_party/accessibility/base/win/atl_module.h +FILE: ../../../flutter/third_party/accessibility/base/win/enum_variant.cc +FILE: ../../../flutter/third_party/accessibility/base/win/enum_variant.h +FILE: ../../../flutter/third_party/accessibility/base/win/enum_variant_unittest.cc +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_bstr.h +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_variant.h +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_variant_unittest.cc +FILE: ../../../flutter/third_party/accessibility/gfx/range/range_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2011 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/base/win/scoped_bstr.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_bstr.cc +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_bstr_unittest.cc +FILE: ../../../flutter/third_party/accessibility/base/win/scoped_variant.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2010 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/base/win/windows_types.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/base/win/windows_types.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/gfx/geometry/insets.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/insets.cc +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/insets_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009 The Chromium 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: accessibility +ORIGIN: ../../../flutter/third_party/accessibility/gfx/geometry/rect_unittest.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/gfx/geometry/rect_unittest.cc +FILE: ../../../flutter/third_party/accessibility/gfx/gfx_export.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013 The Chromium 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: accessibility +ORIGIN: ../../../third_party/icu/scripts/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_data.cc +FILE: ../../../flutter/third_party/accessibility/ax/ax_tree_data.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_win.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_platform_node_win_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/ax_unique_id_unittest.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/test_ax_node_wrapper.cc +FILE: ../../../flutter/third_party/accessibility/ax/platform/test_ax_node_wrapper.h +---------------------------------------------------------------------------------------------------- +Copyright 2015 The Chromium 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: txt ORIGIN: ../../../flutter/third_party/txt/LICENSE @@ -1543,7 +2523,6 @@ FILE: ../../../flutter/third_party/txt/benchmarks/paint_record_benchmarks.cc FILE: ../../../flutter/third_party/txt/benchmarks/paragraph_benchmarks.cc FILE: ../../../flutter/third_party/txt/benchmarks/paragraph_builder_benchmarks.cc FILE: ../../../flutter/third_party/txt/benchmarks/skparagraph_benchmarks.cc -FILE: ../../../flutter/third_party/txt/benchmarks/styled_runs_benchmarks.cc FILE: ../../../flutter/third_party/txt/benchmarks/txt_run_all_benchmarks.cc FILE: ../../../flutter/third_party/txt/src/log/log.cc FILE: ../../../flutter/third_party/txt/src/log/log.h @@ -1837,4 +2816,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==================================================================================================== -Total license count: 2 +Total license count: 16 diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index c36b232a57040..77baa72440c6c 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: dbb5ba7d07e0a2359f59cdfa309e5708 +Signature: 52e42b70d2fe31a1eeff2deffce4c792 UNUSED LICENSES: @@ -11,6 +11,7 @@ LIBRARY: fuchsia_sdk ORIGIN: ../../../fuchsia/sdk/linux/COPYRIGHT.musl TYPE: LicenseType.mit FILE: ../../../fuchsia/sdk/linux/.versions/core.cipd_version +FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_compact_image.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so @@ -214,6 +215,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libm.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libzircon.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_compact_image.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so @@ -417,8 +419,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libm.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libzircon.so -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/_layout_logic.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fidl/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/meta.json @@ -432,20 +432,19 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/meta.json FILE: ../../../fuchsia/sdk/linux/dart/sl4f/meta.json FILE: ../../../fuchsia/sdk/linux/dart/zircon/meta.json -FILE: ../../../fuchsia/sdk/linux/device/generic-arm64.json -FILE: ../../../fuchsia/sdk/linux/device/generic-x64.json -FILE: ../../../fuchsia/sdk/linux/device/qemu-arm64.json -FILE: ../../../fuchsia/sdk/linux/device/qemu-x64.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.gesture/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.virtualkeyboard/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth.oldtokens/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.a2dp/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.hfp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.buildinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2.hal/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/meta.json @@ -462,13 +461,14 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.data/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.deprecatedtimezone/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.developer.tiles/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics.types/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory.wlan/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.light/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/meta.json @@ -476,10 +476,13 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/meta.j FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.virtualkeyboard/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/meta.json @@ -487,6 +490,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.position/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.sensor/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.lowpan.bootstrap/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/meta.json @@ -498,20 +502,18 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mediacodec/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mem/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.metrics/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.migration/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.testing/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.dhcp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.http/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.interfaces/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.mdns/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.oldhttp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.routes/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.netstack/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix.socket/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.power.profile/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.process/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/meta.json @@ -522,16 +524,19 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysmem/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.thermal/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.tracing.provider/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity.control/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.composition/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer.augment/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json @@ -545,25 +550,26 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedclient/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedconfiguration/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.service/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.stats/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/zx/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-default/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-default/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fdio/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl-async/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json @@ -574,19 +580,28 @@ FILE: ../../../fuchsia/sdk/linux/pkg/memfs/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/svc/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sync/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/client.shard.cml +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/client.shard.cmx +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/elf_stdio.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/syslog/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/syslog.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog_structured_backend/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sysroot/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace-engine/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace-provider-so/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/client.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/vulkan.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_compact_image.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_khronos_validation.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/meta.json @@ -726,6 +741,7 @@ LIBRARY: fuchsia_sdk ORIGIN: ../../../fuchsia/sdk/linux/LICENSE TYPE: LicenseType.bsd FILE: ../../../fuchsia/sdk/linux/.versions/core.cipd_version +FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_compact_image.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so @@ -922,10 +938,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wait.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wchar.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wctype.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/wordexp.h -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/exception.h -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/clock.h -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/internal/cdecls.inc -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/scheduler.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/Scrt1.o FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libc.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libdl.so @@ -933,6 +945,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libm.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libzircon.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_compact_image.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so @@ -1129,10 +1142,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wait.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wchar.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wctype.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/wordexp.h -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/exception.h -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/clock.h -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/internal/cdecls.inc -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/scheduler.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/Scrt1.o FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libc.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libdl.so @@ -1140,502 +1149,195 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libm.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libzircon.so -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/composition_delegate.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/composition_delegate/composition_delegate.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/_layout_logic.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/_layout_strategy.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/archetype_strategy/archetype_strategy.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/copresent_strategy/copresent_strategy.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/split_evenly_strategy/split_evenly_strategy.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/stack_strategy/stack_strategy.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/tree/_surface_node.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/tree/_surface_tree.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/layout/layout_types.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/surface/surface_relation.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/bits.dart -FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/xunion.dart FILE: ../../../fuchsia/sdk/linux/dart/fidl/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/inspect.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/inspect.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/internal/_inspect_impl.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/node.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/property.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/bitfield64.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/block.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/heap.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/little_big_slab.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/util.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_fields.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_holder.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_writer.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/testing.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_logger/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/agent_interceptor.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/test_harness_fixtures.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/test_harness_spec_builder.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/test.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic/lib/src/view_token_pair.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic/lib/views.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/incoming.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/outgoing.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/composed_pseudo_dir.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/internal/_flags.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/pseudo_vmo_file.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/vmo_file.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/fuchsia_web_services.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/fuchsia_webview.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/fuchsia_webview_platform_controller.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/utils.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/webview.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/sl4f.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/audio.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device_log.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/dump.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/exceptions.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/factory_store.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/input.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/inspect.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/modular.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/performance.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/scenic.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/setui.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/sl4f_client.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/ssh.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/storage.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/common.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/cpu_metrics.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/drm_fps.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/flutter_frame_stats.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/input_latency.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/memory_metrics.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/scenic_frame_stats.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/temperature_metrics.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_results.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_spec.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_delta.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_point.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/trace_importing.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/trace_model.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/webdriver.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/trace_processing.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/meta.json FILE: ../../../fuchsia/sdk/linux/dart/zircon/meta.json -FILE: ../../../fuchsia/sdk/linux/device/generic-arm64.json -FILE: ../../../fuchsia/sdk/linux/device/generic-x64.json -FILE: ../../../fuchsia/sdk/linux/device/qemu-arm64.json -FILE: ../../../fuchsia/sdk/linux/device/qemu-x64.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.gesture/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/node.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/semantics_manager.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth.oldtokens/credentials_producer.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.virtualkeyboard/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth.oldtokens/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.a2dp/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/pairing_options.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/constants.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/advertising_data.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.hfp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/peer.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/access.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/bootstrap.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/host_watcher.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/identity.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/pairing_delegate.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/peer.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/address.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/appearance.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/connection_role.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/device_class.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/id.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/uuid.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.buildinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2.hal/hal.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2.hal/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/manager.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/stream.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/cast_auth.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/cast_config.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/cast_setup.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/cast_system_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.runner/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.data/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.deprecatedtimezone/deprecated_time_zone.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.deprecatedtimezone/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.developer.tiles/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/format.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics.types/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/reader.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/selector.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory.wlan/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory/factory.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/annotation.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/attachment.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/crash_reporter.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/data_provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/device_id_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/events.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/styles.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/audio.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/ring_buffer.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/stream.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_address_space.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_control.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_pipe.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.light/light.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.light/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/admin.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/hwinfo.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/image_pipe2.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/descriptor.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/device.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/keyboard.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/led.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/mouse.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/report.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/sensor.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/touch.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/units.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.virtualkeyboard/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/tree.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/property_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/namedplace.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.position/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.sensor/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.lowpan.bootstrap/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/volume_control.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/content_decryption.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/error.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/license_session.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/provisioning.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/services.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.playback/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/discovery.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/images.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/player.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/publisher.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sounds/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sounds/sound_player.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.target/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_consumer.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_core.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/usage_reporter.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mediacodec/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mem/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mem/range.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.metrics/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.migration/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.migration/migration.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/modular_config.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.testing/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.testing/test_harness.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/annotation.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story_shell_factory.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.dhcp/client.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.dhcp/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.dhcp/options.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.dhcp/server.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.http/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.interfaces/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.mdns/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.oldhttp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.routes/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/namelookup.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.netstack/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix.socket/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix.socket/socket.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.power.profile/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.process/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/countdown.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/factory_reset.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.scenic.scheduling/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.scenic.scheduling/prediction_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings.policy/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/accessibility.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/audio.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/device.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/display.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/do_not_disturb.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/intl.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/privacy.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/settings.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/setup.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/types.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysmem/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysmem/secure_mem.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.thermal/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.tracing.provider/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity.control/control.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity.control/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/activity.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/state.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/tracker.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/view.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/view_config.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/brightness.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/color_adjustment.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.composition/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/tokens.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/pointer_capture.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/events.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/keyboard.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/keys.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/layout.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/modifiers.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/semantic_keys.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/lifecycle_controller.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer.augment/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/device_listener.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/types.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/focuser.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_token.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ultrasound/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/channel.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/channelcontrol.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/constants.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/context.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/cookie.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/debug.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/frame.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/navigation.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/url_request_rewrite_rules.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/access_point_provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/client_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedclient/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedconfiguration/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.service/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.stats/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/zx/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/executor.cc -FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/executor.h -FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/irq.h -FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/paged_vmo.h -FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/irq.cc FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/paged_vmo.cc FILE: ../../../fuchsia/sdk/linux/pkg/async-default/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-default/include/lib/async-loop/default.h FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-default/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-testing/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/async/include/lib/async/paged_vmo.h FILE: ../../../fuchsia/sdk/linux/pkg/async/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/directory.h -FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/fd.h FILE: ../../../fuchsia/sdk/linux/pkg/fdio/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl-async/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/internal_callable_traits.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/txn_header.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/visitor.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/internal.c FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/txn_header.c -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/event_sender.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/member_connector.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/service_connector.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/service_handler_base.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/transition.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/message_sender.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/internal/message_sender.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fit/barrier.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/barrier.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/constructors_internal.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/in_place_internal.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/storage_internal.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/string_view.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/utility_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/health.cc -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/hierarchy.cc -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/health.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/hierarchy.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/inspect.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/reader.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/value_list.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/block.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/scanner.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/snapshot.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/reader.cc -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/scanner.cc -FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/snapshot.cc -FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/include/lib/inspect/service/cpp/reader.h -FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/include/lib/inspect/service/cpp/service.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/reader.cc -FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/service.cc FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/memfs/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/agent.cc -FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/include/lib/modular/cpp/agent.h FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/fake_agent.cc -FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/fake_component.cc FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/test_harness_builder.cc -FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/test_harness_launcher.cc -FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/view_ref_pair.h -FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/view_token_pair.h FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/view_ref_pair.cc +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/svc/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/sync/include/lib/sync/internal/mutex-internal.h FILE: ../../../fuchsia/sdk/linux/pkg/sync/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/component_context.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/file_descriptor.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/component_context.h -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/file_descriptor.h -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/outgoing_directory.h -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/service_directory.h FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/outgoing_directory.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/service_directory.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/termination_reason.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/component_context_provider.cc FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/service_directory_provider.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/component.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/include/lib/sys/inspect/cpp/component.h FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service.h -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service_aggregate.h -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service_handler.h -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service_watcher.h FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service_aggregate.cc -FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service_watcher.cc +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/client.shard.cml +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/client.shard.cmx +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/elf_stdio.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/syslog/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/syslog.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog_structured_backend/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sysroot/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace-engine/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace-provider-so/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/flags.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/connection.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/directory.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/directory_connection.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/dirent_filler.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/file.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/file_connection.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/node.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/node_connection.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/lazy_dir.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/node_kind.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/pseudo_dir.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/pseudo_file.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/remote_dir.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/service.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/vmo_file.h -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/connection.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/directory.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/directory_connection.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/dirent_filler.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/file.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/file_connection.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/node.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/node_connection.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/lazy_dir.cc FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/pseudo_dir.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/pseudo_file.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/remote_dir.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/service.cc -FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/vmo_file.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/client.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/vulkan.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_compact_image.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_khronos_validation.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/clock.h -FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/exception.h -FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/pager.h FILE: ../../../fuchsia/sdk/linux/pkg/zx/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/zx/pager.cc ---------------------------------------------------------------------------------------------------- -Copyright 2019 The Fuchsia Authors. All rights reserved. +Copyright 2019 The Fuchsia Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1647,9 +1349,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -1669,6 +1368,7 @@ LIBRARY: fuchsia_sdk ORIGIN: ../../../fuchsia/sdk/linux/LICENSE.vulkan TYPE: LicenseType.apache FILE: ../../../fuchsia/sdk/linux/.versions/core.cipd_version +FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_compact_image.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/dist/libasync-default.so @@ -1872,6 +1572,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libm.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/lib/libzircon.so +FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_compact_image.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_image_pipe_swapchain.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/VkLayer_khronos_validation.so FILE: ../../../fuchsia/sdk/linux/arch/x64/dist/libasync-default.so @@ -2075,8 +1776,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libm.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libpthread.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/librt.so FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/lib/libzircon.so -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/internal/layout_logic/_layout_logic.dart -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fidl/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/meta.json @@ -2090,20 +1789,19 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/meta.json FILE: ../../../fuchsia/sdk/linux/dart/sl4f/meta.json FILE: ../../../fuchsia/sdk/linux/dart/zircon/meta.json -FILE: ../../../fuchsia/sdk/linux/device/generic-arm64.json -FILE: ../../../fuchsia/sdk/linux/device/generic-x64.json -FILE: ../../../fuchsia/sdk/linux/device/qemu-arm64.json -FILE: ../../../fuchsia/sdk/linux/device/qemu-x64.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.gesture/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.virtualkeyboard/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth.oldtokens/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.a2dp/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.hfp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.buildinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2.hal/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/meta.json @@ -2120,13 +1818,14 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.data/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.deprecatedtimezone/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.developer.tiles/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics.types/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory.wlan/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.light/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/meta.json @@ -2134,10 +1833,13 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/meta.j FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.virtualkeyboard/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ldsvc/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/meta.json @@ -2145,6 +1847,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.position/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.sensor/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.logger/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.lowpan.bootstrap/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.math/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/meta.json @@ -2156,20 +1859,18 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mediacodec/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mem/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.metrics/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.migration/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.testing/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.dhcp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.http/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.interfaces/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.mdns/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.oldhttp/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.routes/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.netstack/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix.socket/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.power.profile/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.process/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/meta.json @@ -2180,16 +1881,19 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysinfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysmem/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.thermal/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.tracing.provider/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity.control/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.composition/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input2/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer.augment/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/meta.json @@ -2203,25 +1907,26 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedclient/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedconfiguration/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.service/meta.json -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.stats/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/zx/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-default/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-default/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-loop/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async-testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/async/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fdio/meta.json -FILE: ../../../fuchsia/sdk/linux/pkg/fidl-async/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/fit/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/inspect/meta.json @@ -2232,19 +1937,28 @@ FILE: ../../../fuchsia/sdk/linux/pkg/memfs/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/svc/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sync/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/client.shard.cml +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/client.shard.cmx +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/elf_stdio.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/syslog/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog/syslog.json +FILE: ../../../fuchsia/sdk/linux/pkg/syslog_structured_backend/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/sysroot/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace-engine/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace-provider-so/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/trace/meta.json FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/client.shard.cml FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/meta.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan/vulkan.json +FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_compact_image.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_khronos_validation.json FILE: ../../../fuchsia/sdk/linux/pkg/vulkan_layers/meta.json @@ -2475,8 +2189,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ==================================================================================================== LIBRARY: fuchsia_sdk -ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/driver-config.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/bootfs.h + ../../../fuchsia/sdk/linux/LICENSE TYPE: LicenseType.bsd +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/bootfs.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/driver-config.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/image.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/sysconfig.h @@ -2488,6 +2203,7 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/limits.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/smc.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/threads.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/time.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/bootfs.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/driver-config.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/image.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/sysconfig.h @@ -2508,6 +2224,7 @@ FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/message.dart FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/struct.dart FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/table.dart FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/types.dart +FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/unknown_data.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia/lib/src/fakes/fuchsia_fakes.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_logger/lib/logger.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_logger/lib/src/internal/_fuchsia_log_writer.dart @@ -2533,11 +2250,6 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_m FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/module.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/module_state_exception.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/service_connection/agent_service_connection.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/child_view.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/child_view_connection.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/child_view.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/child_view_connection.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/child_view_render_box.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/services.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/service_provider_impl.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/internal/_error_node.dart @@ -2560,18 +2272,13 @@ FILE: ../../../fuchsia/sdk/linux/dart/zircon/lib/src/socket_reader.dart FILE: ../../../fuchsia/sdk/linux/dart/zircon/lib/src/vmo.dart FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/attestation_signer.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/common.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/bonding.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/pairing_delegate.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/remote_device.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/types.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/central.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/nullables.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera/camera.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera/manager.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/cobalt.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.data/data.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.developer.tiles/tiles.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.ethernet/ethernet.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/encoded_image.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/image_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/image_pipe.fidl @@ -2622,8 +2329,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/scenic.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/session.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.scenic/snapshot.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/commands.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/wlan_common.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.stats/wlan_stats.fidl FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/time.h FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/trap.h FILE: ../../../fuchsia/sdk/linux/pkg/async-testing/include/lib/async-testing/test_loop.h @@ -2634,25 +2339,12 @@ FILE: ../../../fuchsia/sdk/linux/pkg/async-testing/test_loop_dispatcher.cc FILE: ../../../fuchsia/sdk/linux/pkg/async/include/lib/async/time.h FILE: ../../../fuchsia/sdk/linux/pkg/async/include/lib/async/trap.h FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/spawn.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl-async/bind.c -FILE: ../../../fuchsia/sdk/linux/pkg/fidl-async/include/lib/fidl-async/bind.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl/epitaph.c FILE: ../../../fuchsia/sdk/linux/pkg/fidl/handle_closing.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl/include/lib/fidl/epitaph.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/builder.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/cpp/builder.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/cpp/message.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/cpp/message_buffer.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/cpp/message_builder.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/cpp/message_part.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/walker.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/message.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/message_buffer.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/message_builder.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/walker.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/binding.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/binding_set.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/enum.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr_set.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/internal/header.h @@ -2676,46 +2368,54 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/internal/proxy_controller.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/internal/stub.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/internal/stub_controller.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/internal/weak_stub_controller.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/builder.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/clone.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/decoder.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/encoder.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/builder.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/clone.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/coding_traits.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/comparison.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/decoder.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/encoder.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/logging.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/enum.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/interface_handle.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/interface_request.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/message.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/message_buffer.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/message_builder.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/message_part.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/object_coding.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/string.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/traits.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/vector.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/internal/logging.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_handle.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_request.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/message.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/message_buffer.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/message_builder.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/logging.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/synchronous_proxy.h FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/synchronous_interface_ptr.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/internal/logging.cc FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/internal/synchronous_proxy.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/bridge.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/bridge_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/bridge.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/bridge_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/promise.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/promise_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/result.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/scheduler.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/scope.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/sequencer.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/single_threaded_executor.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/promise.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/scheduler.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/scope.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/sequencer.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/single_threaded_executor.cc FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/defer.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/function_traits.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/nullable.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/optional.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/promise.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/promise_internal.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/result.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/scheduler.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/scope.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/sequencer.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/single_threaded_executor.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/thread_safety.h FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/traits.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/variant.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/promise.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fit/scheduler.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fit/scope.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fit/sequencer.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fit/single_threaded_executor.cc FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/images.cc FILE: ../../../fuchsia/sdk/linux/pkg/images_cpp/include/lib/images/cpp/images.h FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/inspector.h @@ -2731,12 +2431,18 @@ FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/include/lib/media/cpp/type_conver FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp/type_converters.cc FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/id.h FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/view_token_pair.cc +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/optional.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/type_traits.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/variant.h FILE: ../../../fuchsia/sdk/linux/pkg/svc/include/lib/svc/dir.h FILE: ../../../fuchsia/sdk/linux/pkg/sync/include/lib/sync/condition.h FILE: ../../../fuchsia/sdk/linux/pkg/sync/include/lib/sync/internal/condition-template.h FILE: ../../../fuchsia/sdk/linux/pkg/sync/include/lib/sync/mutex.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/enclosing_environment.cc FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/fake_component.cc FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/fake_launcher.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/launcher_impl.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/test_with_environment.cc FILE: ../../../fuchsia/sdk/linux/pkg/syslog/include/lib/syslog/global.h FILE: ../../../fuchsia/sdk/linux/pkg/syslog/include/lib/syslog/logger.h FILE: ../../../fuchsia/sdk/linux/pkg/syslog/include/lib/syslog/wire_format.h @@ -2776,9 +2482,182 @@ notice, this list of conditions and the following disclaimer. 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: fuchsia_sdk +ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/crash-reason.h + ../../../fuchsia/sdk/linux/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/analyzer.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/boot/crash-reason.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/string_view.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/testonly-syscalls.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/analyzer.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/boot/crash-reason.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/string_view.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/testonly-syscalls.h +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/fuchsia_view.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/fuchsia_view.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/fuchsia_view_connection.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/fuchsia_view_controller.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/pointer_injector.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/component.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/diagnostics.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/feedback_data_provider.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/power.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/proxy.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/repository_manager.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/tiles.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/time.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/gpu_metrics.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/total_trace_wall_time.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/update.dart +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.gesture/gesture_listener.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.a2dp/audio_mode.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.hfp/hfp.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/connection_options.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/configuration.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/pairing_options.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/security_mode.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/deprecated.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/device_name.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.buildinfo/buildinfo.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device_watcher.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/stream.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/remote_control.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/server.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/window.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/constants.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/error.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/interest.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/severity.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/graphical_presenter.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory.wlan/iovar.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/crash_register.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/data_register.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/last_reboot_info.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/codec.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/dai.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/dai_format.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/device.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/frames.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/instance.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/mac.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/session.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/reboot_reason.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/consumer_control.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/device_ids.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input/keys.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/locking.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/debug-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/hypervisor-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/info-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/ioport-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/irq-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/mmio-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/smc-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/vmex-resource.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/event.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/metrics_recorder.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.position/position.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.sensor/sensor.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.lowpan.bootstrap/thread.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/effects_controller.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/properties.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.target/target_discovery.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/activity_reporter.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/profile_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/memorypressure.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.metrics/metric_event_logger.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/launcher.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/session_restart_controller.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.interfaces/interfaces.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.routes/routes.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/socket.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings.policy/error.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings.policy/volume_policy.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/factory_reset.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/input.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/light.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 +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/modifiers.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer.augment/augment.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/mouse.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/state.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/touch.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointer/view.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/config.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/device.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/event.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/display_backlight.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_ref.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_ref_focused.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_ref_installed.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ultrasound/factory.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/commit.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/update.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/url.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/auth.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/bootstrap.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/common.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/weavestack.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/reason_code.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/status_code.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedclient/wlan_deprecated_client.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedconfiguration/wlan_deprecated_configuration.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/zx/zx_common.fidl +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/handle_close_many.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/trace.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/validate_string.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/bitset.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/natural_types_header.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/natural_types_implementation.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/types.h +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/commands_sizing.cc +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands_sizing.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/array.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/bit.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/array.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/bit.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/memory.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/source_location.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/version.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/msi.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/stream.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/msi.cc +FILE: ../../../fuchsia/sdk/linux/pkg/zx/stream.cc +---------------------------------------------------------------------------------------------------- +Copyright 2020 The Fuchsia 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -2825,15 +2704,12 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/iommu. FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/policy.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/system.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/tls.h -FILE: ../../../fuchsia/sdk/linux/dart/composition_delegate/lib/src/surface/surface.dart FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/auth_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth/token_manager.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.control/control.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/client.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/server.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/peripheral.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/types_deprecated.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/status.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/cobalt_controller.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/presentation_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_capturer.fidl @@ -2853,7 +2729,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/flat_namespace.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/commands.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/display_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/events.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/hit.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/nodes.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/renderer.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/resources.fidl @@ -2866,7 +2741,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/text_editing.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/text_input.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/usages.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/presentation.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.service/wlan_service.fidl FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/receiver.h FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/task.h FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/wait.h @@ -2877,6 +2751,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/wait.cc FILE: ../../../fuchsia/sdk/linux/pkg/async-default/include/lib/async/default.h FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-cpp/include/lib/async-loop/cpp/loop.h FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-cpp/loop_wrapper.cc +FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-testing/real_loop.cc FILE: ../../../fuchsia/sdk/linux/pkg/async-loop/include/lib/async-loop/loop.h FILE: ../../../fuchsia/sdk/linux/pkg/async-loop/loop.c FILE: ../../../fuchsia/sdk/linux/pkg/async-testing/dispatcher_stub.cc @@ -2938,9 +2813,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -2975,9 +2847,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -3004,7 +2873,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/i2c.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/usb.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/usb/audio.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/usb/hid.h -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/usb/hub.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/usb/ums.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/listnode.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/pixelformat.h @@ -3029,7 +2897,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/i2c.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/usb.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/usb/audio.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/usb/hid.h -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/usb/hub.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/usb/ums.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/listnode.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/pixelformat.h @@ -3071,6 +2938,7 @@ FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/fdio.h FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/io.h FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/vfs.h FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/watcher.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/thread_checker.h FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/include/lib/media/cpp/timeline_function.h FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/include/lib/media/cpp/timeline_rate.h FILE: ../../../fuchsia/sdk/linux/pkg/media_cpp_no_converters/timeline_function.cc @@ -3114,9 +2982,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -3133,108 +2998,351 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: fuchsia_sdk -ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/string_view.h + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/debug/arm64.h + ../../../fuchsia/sdk/linux/LICENSE TYPE: LicenseType.bsd -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/string_view.h -FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/testonly-syscalls.h -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/string_view.h -FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/testonly-syscalls.h -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/child_view_render_box_2.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/component.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/diagnostics.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/feedback_data_provider.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/power.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/proxy.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/repository_manager.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/tiles.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/time.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/gpu_metrics.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/total_trace_wall_time.dart -FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/update.dart -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.gesture/gesture_listener.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.a2dp/audio_mode.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/connection_options.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/configuration.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/pairing_options.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/security_mode.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device_watcher.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/stream.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/remote_control.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/server.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/window.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/constants.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/error.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/types.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/interest.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/severity.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory.wlan/iovar.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/crash_register.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/data_register.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/last_reboot_info.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/device.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/frames.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/instance.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/mac.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/session.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/consumer_control.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/device_ids.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input/keys.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/event.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/metrics_recorder.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.position/position.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.sensor/sensor.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location/types.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/effects_controller.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/properties.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/types.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.target/target_discovery.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/activity_reporter.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/profile_provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/memorypressure.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/launcher.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/session_restart_controller.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.interfaces/interfaces.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.routes/routes.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/socket.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.posix/error.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings.policy/error.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings.policy/volume_policy.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/input.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/light.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 -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/modifiers.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/config.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/device.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.pointerinjector/event.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/display_backlight.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_ref.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_ref_focused.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_ref_installed.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ultrasound/factory.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/update.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/url.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/auth.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/common.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/weavestack.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.product.deprecatedconfiguration/wlan_deprecated_configuration.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/zx/zx_common.fidl -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/trace.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/validate_string.cc -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/internal/bitset.h -FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_base/include/lib/fidl/cpp/types.h -FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/source_location.h -FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/commands_sizing.cc -FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands_sizing.h -FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/msi.h -FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/stream.h -FILE: ../../../fuchsia/sdk/linux/pkg/zx/msi.cc -FILE: ../../../fuchsia/sdk/linux/pkg/zx/stream.cc +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/exception.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/debug/arm64.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/hw/debug/x86.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/lookup.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/clock.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/internal/cdecls.inc +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/syscalls/scheduler.h +FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/utc.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/exception.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/debug/arm64.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/hw/debug/x86.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/lookup.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/clock.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/internal/cdecls.inc +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/syscalls/scheduler.h +FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/utc.h +FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/bits.dart +FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/xunion.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/inspect.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/inspect.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/internal/_inspect_impl.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/node.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/inspect/property.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/bitfield64.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/block.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/heap.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/little_big_slab.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/util.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_fields.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_holder.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_writer.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/testing.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/agent_interceptor.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/test_harness_fixtures.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/test_harness_spec_builder.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/test.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic/lib/src/view_token_pair.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic/lib/views.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/incoming.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/outgoing.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/composed_pseudo_dir.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/internal/_flags.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/pseudo_vmo_file.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_vfs/lib/src/vmo_file.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/fuchsia_web_services.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/fuchsia_webview.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/fuchsia_webview_platform_controller.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/src/utils.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_webview_flutter/lib/webview.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/sl4f.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/audio.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device_log.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/dump.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/exceptions.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/factory_store.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/input.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/inspect.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/modular.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/performance.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/scenic.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/setui.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/sl4f_client.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/ssh.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/storage.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/common.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/cpu_metrics.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/drm_fps.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/flutter_frame_stats.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/input_latency.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/memory_metrics.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/scenic_frame_stats.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/temperature_metrics.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_results.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics_spec.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_delta.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/time_point.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/trace_importing.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/trace_model.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/webdriver.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/trace_processing.dart +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/node.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.semantics/semantics_manager.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.auth.oldtokens/credentials_producer.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt/constants.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/advertising_data.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/peer.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/access.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/bootstrap.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/host_watcher.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/identity.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/pairing_delegate.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.sys/peer.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/address.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/appearance.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/connection_role.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/device_class.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/id.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth/uuid.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2.hal/hal.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/manager.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/stream.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/cast_auth.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/cast_config.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/cast_setup.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/cast_system_info.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.deprecatedtimezone/deprecated_time_zone.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/format.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/reader.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics/selector.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/annotations.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.element/element_manager.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.factory/factory.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/annotation.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/attachment.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/crash_reporter.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/data_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/device_id_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/events.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.fonts/styles.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/audio.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/ring_buffer.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/stream.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_address_space.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_control.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_pipe.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.light/light.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.power.statecontrol/admin.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hwinfo/hwinfo.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.images/image_pipe2.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/descriptor.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/device.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/keyboard.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/led.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/mouse.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/report.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/sensor.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/touch.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.report/units.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.inspect/tree.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/property_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/connection-info.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/connection-options.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/connector.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/debuglog.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/deprecated.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/directory-entry.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/directory-iterator.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/directory-watcher.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/directory.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/file.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/io2.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/memory.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/node-attributes.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/node.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/pipe.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/posix-socket.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/rights-abilities.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/kernel-counter.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/kernel-debug.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/kernel-stats.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.kernel/root-job.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.location.namedplace/namedplace.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.audio/volume_control.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/content_decryption.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/error.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/license_session.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/provisioning.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.drm/services.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/discovery.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/images.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/player.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sessions2/publisher.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.sounds/sound_player.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_consumer.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/audio_core.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/usage_reporter.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mem/range.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.migration/migration.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.session/modular_config.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular.testing/test_harness.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/annotation.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story_shell_factory.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/namelookup.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery.ui/countdown.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.recovery/factory_reset.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.scenic.scheduling/prediction_info.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/accessibility.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/audio.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/device.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/display.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/do_not_disturb.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/intl.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/privacy.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/settings.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/setup.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sysmem/secure_mem.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.thermal/thermal.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity.control/control.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/activity.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/state.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.activity/tracker.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/view.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.app/view_config.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/brightness.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.brightness/color_adjustment.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.gfx/tokens.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input/pointer_capture.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.lifecycle/lifecycle_controller.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.policy/device_listener.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/focuser.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/view_token.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/channel.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/channelcontrol.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/constants.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/context.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/cookie.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/debug.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/frame.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/navigation.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/url_request_rewrite_rules.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/access_point_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/client_provider.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/zx/rights.fidl +FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/executor.cc +FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/executor.h +FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/irq.h +FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/include/lib/async/cpp/paged_vmo.h +FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/irq.cc +FILE: ../../../fuchsia/sdk/linux/pkg/async-cpp/paged_vmo.cc +FILE: ../../../fuchsia/sdk/linux/pkg/async-loop-default/include/lib/async-loop/default.h +FILE: ../../../fuchsia/sdk/linux/pkg/async/include/lib/async/paged_vmo.h +FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/directory.h +FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/fd.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/internal_callable_traits.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/txn_header.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/visitor.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/internal.c +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/txn_header.c +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/event_sender.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/member_connector.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/service_connector.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp/include/lib/fidl/cpp/service_handler_base.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/message_sender.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_cpp_sync/internal/message_sender.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/barrier.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/barrier.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fpromise/in_place_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/constructors_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/storage_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit/include/lib/fit/utility_internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/health.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/hierarchy.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/health.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/hierarchy.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/inspect.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/reader.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/value_list.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/block.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/scanner.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/include/lib/inspect/cpp/vmo/snapshot.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/reader.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/scanner.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect/vmo/snapshot.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/include/lib/inspect/service/cpp/reader.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/include/lib/inspect/service/cpp/service.h +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/reader.cc +FILE: ../../../fuchsia/sdk/linux/pkg/inspect_service_cpp/service.cc +FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/agent.cc +FILE: ../../../fuchsia/sdk/linux/pkg/modular_cpp/include/lib/modular/cpp/agent.h +FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/fake_agent.cc +FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/fake_component.cc +FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/test_harness_builder.cc +FILE: ../../../fuchsia/sdk/linux/pkg/modular_testing_cpp/test_harness_launcher.cc +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/view_ref_pair.h +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/view_token_pair.h +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/view_ref_pair.cc +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/constructors.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/storage.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/utility.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/string_view.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/utility.h +FILE: ../../../fuchsia/sdk/linux/pkg/sync/include/lib/sync/internal/mutex-internal.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/component_context.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/file_descriptor.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/component_context.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/file_descriptor.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/outgoing_directory.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/include/lib/sys/cpp/service_directory.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/outgoing_directory.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/service_directory.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp/termination_reason.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/component_context_provider.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_cpp_testing/service_directory_provider.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/component.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_inspect_cpp/include/lib/sys/inspect/cpp/component.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service_aggregate.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service_handler.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/include/lib/sys/service/cpp/service_watcher.h +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service_aggregate.cc +FILE: ../../../fuchsia/sdk/linux/pkg/sys_service_cpp/service_watcher.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/flags.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/connection.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/directory.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/directory_connection.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/dirent_filler.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/file.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/file_connection.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/node.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/internal/node_connection.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/lazy_dir.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/node_kind.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/pseudo_dir.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/pseudo_file.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/remote_dir.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/service.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/include/lib/vfs/cpp/vmo_file.h +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/connection.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/directory.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/directory_connection.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/dirent_filler.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/file.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/file_connection.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/node.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/internal/node_connection.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/lazy_dir.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/pseudo_dir.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/pseudo_file.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/remote_dir.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/service.cc +FILE: ../../../fuchsia/sdk/linux/pkg/vfs_cpp/vmo_file.cc +FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/clock.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/exception.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/pager.h +FILE: ../../../fuchsia/sdk/linux/pkg/zx/pager.cc ---------------------------------------------------------------------------------------------------- -Copyright 2020 The Fuchsia Authors. All rights reserved. +Copyright 2019 The Fuchsia Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -3246,9 +3354,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -3269,7 +3374,6 @@ ORIGIN: ../../../fuchsia/sdk/linux/dart/fidl/lib/fidl.dart + ../../../LICENSE TYPE: LicenseType.bsd FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/fidl.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia/lib/fuchsia.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/startup_context.dart FILE: ../../../fuchsia/sdk/linux/dart/zircon/lib/src/fakes/zircon_fakes.dart FILE: ../../../fuchsia/sdk/linux/dart/zircon/lib/zircon.dart ---------------------------------------------------------------------------------------------------- @@ -3304,12 +3408,103 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: fuchsia_sdk -ORIGIN: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/internal/_startup_context_impl.dart + ../../../LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/codegen_common.dart + ../../../fuchsia/sdk/linux/LICENSE TYPE: LicenseType.bsd -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/internal/_startup_context_impl.dart -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/cache.fidl +FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/codegen_common.dart +FILE: ../../../fuchsia/sdk/linux/dart/fidl/lib/src/wire_format.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/reader.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/reader/diagnostic_config.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/reader/diagnostic_data.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/reader/reader.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic/lib/src/scenic_context.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/focus_state.dart +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/src/fuchsia_views_service.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/trace_processing/metrics/camera_metrics.dart +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.accessibility.virtualkeyboard/virtual_keyboard.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt2/client.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt2/constants.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.gatt2/types.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/binder.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.diagnostics.types/component.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.feedback/data_provider_controller.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.audio/dai_connect.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.goldfish/goldfish_sync.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.hardware.network/port.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.input.virtualkeyboard/virtual_keyboard.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/calendar.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.intl/time_zones.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.io2/inotify.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.power.profile/profile.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.composition/allocator.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.composition/flatland.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.composition/screenshot.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/flatland_tokens.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/wlan_common.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/constants.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.ieee80211/rsn.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/types.fidl +FILE: ../../../fuchsia/sdk/linux/pkg/fdio/include/lib/fdio/inotify.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/include/lib/fidl/transformer.h +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/transformer.cc +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/barrier.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/bridge.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/promise.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/result.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/scheduler.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/scope.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/sequencer.h +FILE: ../../../fuchsia/sdk/linux/pkg/fit-promise/include/lib/fit/single_threaded_executor.h +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/view_identity.h +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/view_identity.cc +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/algorithm.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/cstddef.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/functional.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/algorithm.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/exception.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/functional.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/span.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/tuple.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/internal/type_traits.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/iterator.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/span.h +FILE: ../../../fuchsia/sdk/linux/pkg/stdcompat/include/lib/stdcompat/tuple.h +FILE: ../../../fuchsia/sdk/linux/pkg/syslog_structured_backend/fuchsia_syslog.cc +FILE: ../../../fuchsia/sdk/linux/pkg/syslog_structured_backend/include/lib/syslog/structured_backend/cpp/fuchsia_syslog.h +FILE: ../../../fuchsia/sdk/linux/pkg/syslog_structured_backend/include/lib/syslog/structured_backend/fuchsia_syslog.h ---------------------------------------------------------------------------------------------------- -Copyright 2019 The Chromium Authors. All rights reserved. +Copyright 2021 The Fuchsia 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. + +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: fuchsia_sdk +ORIGIN: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/component_context.dart + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_services/lib/src/component_context.dart +---------------------------------------------------------------------------------------------------- +Copyright 2021 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -3338,6 +3533,39 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: fuchsia_sdk +ORIGIN: ../../../fuchsia/sdk/linux/dart/zircon/lib/src/fakes/handle_disposition.dart + ../../../flutter/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../fuchsia/sdk/linux/dart/zircon/lib/src/fakes/handle_disposition.dart +---------------------------------------------------------------------------------------------------- +Copyright 2021 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + ==================================================================================================== LIBRARY: fuchsia_sdk ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.net.oldhttp/http_error.fidl + ../../../fuchsia/sdk/linux/LICENSE @@ -3361,9 +3589,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -3380,11 +3605,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: fuchsia_sdk -ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.netstack/netstack.fidl + ../../../fuchsia/sdk/linux/LICENSE +ORIGIN: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/cache.fidl + ../../../LICENSE TYPE: LicenseType.bsd -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.netstack/netstack.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.sys.test/cache.fidl ---------------------------------------------------------------------------------------------------- -Copyright 2013 The Fuchsia Authors. All rights reserved. +Copyright 2019 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -3433,9 +3658,6 @@ notice, this list of conditions and the following disclaimer. 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 @@ -3449,4 +3671,4 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -Total license count: 14 +Total license count: 17 diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 99f99dc1b2569..6b2c0f7df3d96 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c8b43171afd115a8618c41b931c64abd +Signature: cd6984449e3f9843d9d94629e27424c2 UNUSED LICENSES: @@ -416,440 +416,158 @@ limitations under the License. ==================================================================================================== ==================================================================================================== -LIBRARY: libsdl +LIBRARY: expat +ORIGIN: ../../../third_party/skia/third_party/expat/include/expat_config/expat_config.h + ../../../third_party/skia/third_party/expat/include/expat_config/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/third_party/expat/include/expat_config/expat_config.h +---------------------------------------------------------------------------------------------------- +Copyright 2021 Google Inc. + +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: harfbuzz +ORIGIN: ../../../third_party/skia/third_party/expat/include/expat_config/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/third_party/harfbuzz/config-override.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2021 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: skcms +LIBRARY: vulkanmemoryallocator +ORIGIN: ../../../third_party/skia/include/third_party/skcms/skcms.h + ../../../third_party/skia/include/third_party/skcms/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/include/third_party/skcms/skcms.h +FILE: ../../../third_party/skia/third_party/skcms/skcms.cc +FILE: ../../../third_party/skia/third_party/skcms/skcms_internal.h +FILE: ../../../third_party/skia/third_party/skcms/src/Transform_inl.h +FILE: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.cpp +FILE: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.h +---------------------------------------------------------------------------------------------------- +Copyright 2018 Google Inc. + +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: skcms +ORIGIN: ../../../third_party/skia/include/third_party/skcms/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/third_party/skcms/version.sha1 +---------------------------------------------------------------------------------------------------- +Copyright (c) 2018 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: skia -ORIGIN: ../../../third_party/skia/bench/AndroidCodecBench.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/icu/scripts/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/bench/AndroidCodecBench.cpp -FILE: ../../../third_party/skia/bench/AndroidCodecBench.h -FILE: ../../../third_party/skia/bench/EncodeBench.cpp -FILE: ../../../third_party/skia/bench/GrMipmapBench.cpp -FILE: ../../../third_party/skia/bench/HardStopGradientBench_ScaleNumColors.cpp -FILE: ../../../third_party/skia/bench/HardStopGradientBench_ScaleNumHardStops.cpp -FILE: ../../../third_party/skia/bench/HardStopGradientBench_SpecialHardStops.cpp -FILE: ../../../third_party/skia/bench/ImageCacheBudgetBench.cpp -FILE: ../../../third_party/skia/bench/PDFBench.cpp -FILE: ../../../third_party/skia/bench/QuickRejectBench.cpp -FILE: ../../../third_party/skia/bench/ShapesBench.cpp -FILE: ../../../third_party/skia/bench/StreamBench.cpp -FILE: ../../../third_party/skia/bench/SwizzleBench.cpp -FILE: ../../../third_party/skia/bench/TileImageFilterBench.cpp -FILE: ../../../third_party/skia/bench/VertexColorSpaceBench.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkPEG.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGAttribute.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGAttribute.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGAttributeParser.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGAttributeParser.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGCircle.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGCircle.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGClipPath.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGClipPath.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGContainer.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGContainer.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGDOM.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGDOM.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGDefs.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGEllipse.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGEllipse.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGG.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGHiddenContainer.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGIDMapper.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGLine.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGLine.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGLinearGradient.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGLinearGradient.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGNode.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGNode.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGPath.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGPath.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGPoly.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGPoly.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGRect.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGRect.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGRenderContext.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGRenderContext.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGSVG.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGSVG.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGShape.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGShape.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGStop.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGStop.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGTransformableNode.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGTransformableNode.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGTypes.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGValue.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGValue.h -FILE: ../../../third_party/skia/experimental/xps_to_png/xps_to_png.cs -FILE: ../../../third_party/skia/fuzz/Fuzz.cpp -FILE: ../../../third_party/skia/fuzz/Fuzz.h -FILE: ../../../third_party/skia/fuzz/FuzzGradients.cpp -FILE: ../../../third_party/skia/fuzz/FuzzMain.cpp -FILE: ../../../third_party/skia/fuzz/FuzzParsePath.cpp -FILE: ../../../third_party/skia/fuzz/FuzzPathop.cpp -FILE: ../../../third_party/skia/gm/animated_gif.cpp -FILE: ../../../third_party/skia/gm/animatedimageblurs.cpp -FILE: ../../../third_party/skia/gm/arcto.cpp -FILE: ../../../third_party/skia/gm/bigrect.cpp -FILE: ../../../third_party/skia/gm/bitmapimage.cpp -FILE: ../../../third_party/skia/gm/blurcircles2.cpp -FILE: ../../../third_party/skia/gm/bug5252.cpp -FILE: ../../../third_party/skia/gm/bug530095.cpp -FILE: ../../../third_party/skia/gm/bug615686.cpp -FILE: ../../../third_party/skia/gm/circulararcs.cpp -FILE: ../../../third_party/skia/gm/clip_error.cpp -FILE: ../../../third_party/skia/gm/colorfilteralpha8.cpp -FILE: ../../../third_party/skia/gm/complexclip4.cpp -FILE: ../../../third_party/skia/gm/complexclip_blur_tiled.cpp -FILE: ../../../third_party/skia/gm/croppedrects.cpp -FILE: ../../../third_party/skia/gm/dashcircle.cpp -FILE: ../../../third_party/skia/gm/drawregion.cpp -FILE: ../../../third_party/skia/gm/drawregionmodes.cpp -FILE: ../../../third_party/skia/gm/encode_platform.cpp -FILE: ../../../third_party/skia/gm/encode_srgb.cpp -FILE: ../../../third_party/skia/gm/filterbug.cpp -FILE: ../../../third_party/skia/gm/hardstop_gradients.cpp -FILE: ../../../third_party/skia/gm/imagemakewithfilter.cpp -FILE: ../../../third_party/skia/gm/imagemasksubset.cpp -FILE: ../../../third_party/skia/gm/lattice.cpp -FILE: ../../../third_party/skia/gm/overdrawcolorfilter.cpp -FILE: ../../../third_party/skia/gm/overstroke.cpp -FILE: ../../../third_party/skia/gm/pathmaskcache.cpp -FILE: ../../../third_party/skia/gm/readpixels.cpp -FILE: ../../../third_party/skia/gm/rectangletexture.cpp -FILE: ../../../third_party/skia/gm/rrectclipdrawpaint.cpp -FILE: ../../../third_party/skia/gm/shapes.cpp -FILE: ../../../third_party/skia/gm/showmiplevels.cpp -FILE: ../../../third_party/skia/gm/simplerect.cpp -FILE: ../../../third_party/skia/gm/skbug_4868.cpp -FILE: ../../../third_party/skia/gm/skbug_5321.cpp -FILE: ../../../third_party/skia/gm/stroke_rect_shader.cpp -FILE: ../../../third_party/skia/gm/strokedlines.cpp -FILE: ../../../third_party/skia/gm/subsetshader.cpp -FILE: ../../../third_party/skia/gm/textblobblockreordering.cpp -FILE: ../../../third_party/skia/gm/windowrectangles.cpp -FILE: ../../../third_party/skia/include/codec/SkCodecAnimation.h -FILE: ../../../third_party/skia/include/core/SkBlendMode.h -FILE: ../../../third_party/skia/include/core/SkClipOp.h -FILE: ../../../third_party/skia/include/core/SkColorSpace.h -FILE: ../../../third_party/skia/include/core/SkICC.h -FILE: ../../../third_party/skia/include/core/SkMilestone.h -FILE: ../../../third_party/skia/include/core/SkOverdrawCanvas.h -FILE: ../../../third_party/skia/include/core/SkRasterHandleAllocator.h -FILE: ../../../third_party/skia/include/core/SkSwizzle.h -FILE: ../../../third_party/skia/include/core/SkYUVASizeInfo.h -FILE: ../../../third_party/skia/include/effects/SkArithmeticImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkOverdrawColorFilter.h -FILE: ../../../third_party/skia/include/effects/SkPaintImageFilter.h -FILE: ../../../third_party/skia/include/gpu/vk/GrVkBackendContext.h -FILE: ../../../third_party/skia/include/gpu/vk/GrVkExtensions.h -FILE: ../../../third_party/skia/include/gpu/vk/GrVkTypes.h -FILE: ../../../third_party/skia/include/ports/SkFontMgr_FontConfigInterface.h -FILE: ../../../third_party/skia/include/ports/SkImageGeneratorCG.h -FILE: ../../../third_party/skia/include/ports/SkImageGeneratorWIC.h -FILE: ../../../third_party/skia/include/private/GrSingleOwner.h -FILE: ../../../third_party/skia/include/private/SkBitmaskEnum.h -FILE: ../../../third_party/skia/include/private/SkEncodedInfo.h -FILE: ../../../third_party/skia/include/private/SkSafe_math.h -FILE: ../../../third_party/skia/include/utils/SkNoDrawCanvas.h -FILE: ../../../third_party/skia/modules/sksg/samples/SampleSVGPong.cpp -FILE: ../../../third_party/skia/modules/skshaper/include/SkShaper.h -FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp -FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_primitive.cpp -FILE: ../../../third_party/skia/samplecode/DecodeFile.h -FILE: ../../../third_party/skia/samplecode/Sample.cpp -FILE: ../../../third_party/skia/samplecode/SampleAndroidShadows.cpp -FILE: ../../../third_party/skia/samplecode/SampleMegaStroke.cpp -FILE: ../../../third_party/skia/samplecode/SamplePathOverstroke.cpp -FILE: ../../../third_party/skia/samplecode/SampleSVGFile.cpp -FILE: ../../../third_party/skia/src/codec/SkCodecAnimationPriv.h -FILE: ../../../third_party/skia/src/codec/SkRawCodec.cpp -FILE: ../../../third_party/skia/src/codec/SkRawCodec.h -FILE: ../../../third_party/skia/src/codec/SkStreamBuffer.cpp -FILE: ../../../third_party/skia/src/codec/SkStreamBuffer.h -FILE: ../../../third_party/skia/src/core/SkATrace.cpp -FILE: ../../../third_party/skia/src/core/SkATrace.h -FILE: ../../../third_party/skia/src/core/SkAnnotationKeys.h -FILE: ../../../third_party/skia/src/core/SkArenaAlloc.cpp -FILE: ../../../third_party/skia/src/core/SkArenaAlloc.h -FILE: ../../../third_party/skia/src/core/SkAutoMalloc.h -FILE: ../../../third_party/skia/src/core/SkAutoPixmapStorage.cpp -FILE: ../../../third_party/skia/src/core/SkAutoPixmapStorage.h -FILE: ../../../third_party/skia/src/core/SkBlendModePriv.h -FILE: ../../../third_party/skia/src/core/SkClipOpPriv.h -FILE: ../../../third_party/skia/src/core/SkColorFilter_Matrix.h -FILE: ../../../third_party/skia/src/core/SkColorSpace.cpp -FILE: ../../../third_party/skia/src/core/SkColorSpacePriv.h -FILE: ../../../third_party/skia/src/core/SkCpu.cpp -FILE: ../../../third_party/skia/src/core/SkCpu.h -FILE: ../../../third_party/skia/src/core/SkFixed15.h -FILE: ../../../third_party/skia/src/core/SkFuzzLogging.h -FILE: ../../../third_party/skia/src/core/SkGlobalInitialization_core.cpp -FILE: ../../../third_party/skia/src/core/SkICC.cpp -FILE: ../../../third_party/skia/src/core/SkICCPriv.h -FILE: ../../../third_party/skia/src/core/SkImageFilterCache.cpp -FILE: ../../../third_party/skia/src/core/SkImageFilterCache.h -FILE: ../../../third_party/skia/src/core/SkLRUCache.h -FILE: ../../../third_party/skia/src/core/SkLeanWindows.h -FILE: ../../../third_party/skia/src/core/SkMSAN.h -FILE: ../../../third_party/skia/src/core/SkMatrixPriv.h -FILE: ../../../third_party/skia/src/core/SkModeColorFilter.h -FILE: ../../../third_party/skia/src/core/SkOverdrawCanvas.cpp -FILE: ../../../third_party/skia/src/core/SkPathMeasurePriv.h -FILE: ../../../third_party/skia/src/core/SkRasterPipeline.cpp -FILE: ../../../third_party/skia/src/core/SkRasterPipeline.h -FILE: ../../../third_party/skia/src/core/SkRasterPipelineBlitter.cpp -FILE: ../../../third_party/skia/src/core/SkRecordedDrawable.cpp -FILE: ../../../third_party/skia/src/core/SkRecordedDrawable.h -FILE: ../../../third_party/skia/src/core/SkScaleToSides.h -FILE: ../../../third_party/skia/src/core/SkScopeExit.h -FILE: ../../../third_party/skia/src/core/SkSpecialImage.cpp -FILE: ../../../third_party/skia/src/core/SkSpecialImage.h -FILE: ../../../third_party/skia/src/core/SkSpecialSurface.cpp -FILE: ../../../third_party/skia/src/core/SkSpecialSurface.h -FILE: ../../../third_party/skia/src/core/SkSwizzle.cpp -FILE: ../../../third_party/skia/src/effects/SkOverdrawColorFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkArithmeticImageFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkPaintImageFilter.cpp -FILE: ../../../third_party/skia/src/gpu/GrAppliedClip.h -FILE: ../../../third_party/skia/src/gpu/GrAuditTrail.cpp -FILE: ../../../third_party/skia/src/gpu/GrAuditTrail.h -FILE: ../../../third_party/skia/src/gpu/GrBitmapTextureMaker.cpp -FILE: ../../../third_party/skia/src/gpu/GrBitmapTextureMaker.h -FILE: ../../../third_party/skia/src/gpu/GrClipStackClip.cpp -FILE: ../../../third_party/skia/src/gpu/GrClipStackClip.h -FILE: ../../../third_party/skia/src/gpu/GrColorSpaceXform.cpp -FILE: ../../../third_party/skia/src/gpu/GrColorSpaceXform.h -FILE: ../../../third_party/skia/src/gpu/GrContextPriv.h -FILE: ../../../third_party/skia/src/gpu/GrFixedClip.h -FILE: ../../../third_party/skia/src/gpu/GrImageTextureMaker.cpp -FILE: ../../../third_party/skia/src/gpu/GrImageTextureMaker.h -FILE: ../../../third_party/skia/src/gpu/GrOpsRenderPass.cpp -FILE: ../../../third_party/skia/src/gpu/GrOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/GrProgramDesc.cpp -FILE: ../../../third_party/skia/src/gpu/GrReducedClip.cpp -FILE: ../../../third_party/skia/src/gpu/GrReducedClip.h -FILE: ../../../third_party/skia/src/gpu/GrRenderTargetContextPriv.h -FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxy.cpp -FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxy.h -FILE: ../../../third_party/skia/src/gpu/GrResourceHandle.h -FILE: ../../../third_party/skia/src/gpu/GrScissorState.h -FILE: ../../../third_party/skia/src/gpu/GrShaderVar.cpp -FILE: ../../../third_party/skia/src/gpu/GrShaderVar.h -FILE: ../../../third_party/skia/src/gpu/GrStencilSettings.h -FILE: ../../../third_party/skia/src/gpu/GrStyle.cpp -FILE: ../../../third_party/skia/src/gpu/GrStyle.h -FILE: ../../../third_party/skia/src/gpu/GrSurfaceContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrSurfaceContext.h -FILE: ../../../third_party/skia/src/gpu/GrSurfaceContextPriv.h -FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxy.cpp -FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxy.h -FILE: ../../../third_party/skia/src/gpu/GrSwizzle.h -FILE: ../../../third_party/skia/src/gpu/GrTextureAdjuster.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureAdjuster.h -FILE: ../../../third_party/skia/src/gpu/GrTextureMaker.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureMaker.h -FILE: ../../../third_party/skia/src/gpu/GrTextureProducer.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureProducer.h -FILE: ../../../third_party/skia/src/gpu/GrTextureProxy.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureProxy.h -FILE: ../../../third_party/skia/src/gpu/GrTextureRenderTargetProxy.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureRenderTargetProxy.h -FILE: ../../../third_party/skia/src/gpu/GrUserStencilSettings.h -FILE: ../../../third_party/skia/src/gpu/GrWindowRectangles.h -FILE: ../../../third_party/skia/src/gpu/GrWindowRectsState.h -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineStateDataManager.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrShadowGeoProc.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrShadowGeoProc.h -FILE: ../../../third_party/skia/src/gpu/geometry/GrStyledShape.cpp -FILE: ../../../third_party/skia/src/gpu/geometry/GrStyledShape.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLBuffer.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/gl/glfw/GrGLMakeNativeInterface_glfw.cpp -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramDataManager.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrPathStencilSettings.h -FILE: ../../../third_party/skia/src/gpu/ops/GrRegionOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrRegionOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrShadowRRectOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrShadowRRectOp.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorPool.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorPool.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSet.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSet.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSetManager.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSetManager.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkExtensions.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkFramebuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkFramebuffer.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageView.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageView.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkOpsRenderPass.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipeline.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipeline.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineState.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineState.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateBuilder.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateBuilder.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateCache.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateDataManager.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateDataManager.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkResourceProvider.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkResourceProvider.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkSampler.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkSampler.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformHandler.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformHandler.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkVaryingHandler.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkVaryingHandler.h -FILE: ../../../third_party/skia/src/images/SkImageEncoderPriv.h -FILE: ../../../third_party/skia/src/opts/SkChecksum_opts.h -FILE: ../../../third_party/skia/src/opts/SkOpts_avx.cpp -FILE: ../../../third_party/skia/src/opts/SkOpts_crc32.cpp -FILE: ../../../third_party/skia/src/opts/SkOpts_sse42.cpp -FILE: ../../../third_party/skia/src/opts/SkSwizzler_opts.h -FILE: ../../../third_party/skia/src/pdf/SkBitmapKey.h -FILE: ../../../third_party/skia/src/pdf/SkPDFDocumentPriv.h -FILE: ../../../third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp -FILE: ../../../third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h -FILE: ../../../third_party/skia/src/pdf/SkPDFMakeToUnicodeCmap.h -FILE: ../../../third_party/skia/src/ports/SkFontConfigInterface.cpp -FILE: ../../../third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp -FILE: ../../../third_party/skia/src/ports/SkImageGeneratorCG.cpp -FILE: ../../../third_party/skia/src/ports/SkImageGeneratorWIC.cpp -FILE: ../../../third_party/skia/src/shaders/SkColorFilterShader.h -FILE: ../../../third_party/skia/src/shaders/SkColorShader.cpp -FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.cpp -FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.h -FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientPriv.h -FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fLinearGradient.cpp -FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fLinearGradient.h -FILE: ../../../third_party/skia/src/sksl/SkSLCFGGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCFGGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLCPPCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCodeGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLCompiler.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCompiler.h -FILE: ../../../third_party/skia/src/sksl/SkSLContext.h -FILE: ../../../third_party/skia/src/sksl/SkSLErrorReporter.h -FILE: ../../../third_party/skia/src/sksl/SkSLGLSLCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLGLSLCodeGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLHCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLIRGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLIRGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLMain.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLMemoryLayout.h -FILE: ../../../third_party/skia/src/sksl/SkSLMetalCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLMetalCodeGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLParser.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLParser.h -FILE: ../../../third_party/skia/src/sksl/SkSLPosition.h -FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVCodeGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLUtil.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLUtil.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLBinaryExpression.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLBlock.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLBoolLiteral.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLBreakStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructor.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLContinueStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLDiscardStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLDoStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLExpression.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLExpressionStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLExtension.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLField.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLFieldAccess.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLFloatLiteral.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLForStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionCall.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionDeclaration.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionDefinition.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionReference.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLIRNode.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLIfStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLIndexExpression.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLIntLiteral.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLInterfaceBlock.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLLayout.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLModifiers.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLModifiersDeclaration.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLNop.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLNullLiteral.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLPostfixExpression.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLPrefixExpression.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLProgram.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLProgramElement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLReturnStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLSection.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLSetting.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLSwizzle.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLSymbol.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLSymbolTable.cpp -FILE: ../../../third_party/skia/src/sksl/ir/SkSLSymbolTable.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLTernaryExpression.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLType.cpp -FILE: ../../../third_party/skia/src/sksl/ir/SkSLType.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLTypeReference.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLUnresolvedFunction.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLVarDeclarations.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLVarDeclarationsStatement.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariable.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariableReference.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLWhileStatement.h -FILE: ../../../third_party/skia/src/utils/SkMultiPictureDocument.cpp -FILE: ../../../third_party/skia/src/utils/SkMultiPictureDocument.h -FILE: ../../../third_party/skia/src/utils/SkMultiPictureDocumentPriv.h -FILE: ../../../third_party/skia/src/utils/SkOSPath.h -FILE: ../../../third_party/skia/third_party/libsdl/SDL_config_premake.h ----------------------------------------------------------------------------------------------------- -Copyright 2016 Google Inc. - -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 the copyright holder 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: skcms -LIBRARY: vulkanmemoryallocator -ORIGIN: ../../../third_party/skia/include/third_party/skcms/skcms.h + ../../../third_party/skia/include/third_party/skcms/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/include/third_party/skcms/skcms.h -FILE: ../../../third_party/skia/third_party/skcms/skcms.cc -FILE: ../../../third_party/skia/third_party/skcms/skcms_internal.h -FILE: ../../../third_party/skia/third_party/skcms/src/Transform_inl.h -FILE: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.cpp -FILE: ../../../third_party/skia/third_party/vulkanmemoryallocator/GrVulkanMemoryAllocator.h +FILE: ../../../third_party/skia/src/core/SkTraceEventCommon.h ---------------------------------------------------------------------------------------------------- -Copyright 2018 Google Inc. +Copyright 2015 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -879,84 +597,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== -LIBRARY: skcms -ORIGIN: ../../../third_party/skia/include/third_party/skcms/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/third_party/skcms/version.sha1 ----------------------------------------------------------------------------------------------------- -Copyright (c) 2018 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: skia -ORIGIN: ../../../third_party/icu/scripts/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/src/core/SkTraceEventCommon.h ----------------------------------------------------------------------------------------------------- -Copyright 2015 The Chromium 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: skia -ORIGIN: ../../../third_party/skia/LICENSE +LIBRARY: skia +ORIGIN: ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/.bazelignore FILE: ../../../third_party/skia/.clang-tidy FILE: ../../../third_party/skia/BUILD.bazel FILE: ../../../third_party/skia/CQ_COMMITTERS FILE: ../../../third_party/skia/DEPS +FILE: ../../../third_party/skia/DIR_METADATA FILE: ../../../third_party/skia/WORKSPACE.bazel FILE: ../../../third_party/skia/animations/checkbox.xml FILE: ../../../third_party/skia/animations/chest#1.jpg @@ -978,11 +627,19 @@ FILE: ../../../third_party/skia/demos.skia.org/demos/hello_world/index.html FILE: ../../../third_party/skia/demos.skia.org/demos/image_decode_web_worker/index.html FILE: ../../../third_party/skia/demos.skia.org/demos/image_decode_web_worker/main.js FILE: ../../../third_party/skia/demos.skia.org/demos/image_decode_web_worker/worker.js +FILE: ../../../third_party/skia/demos.skia.org/demos/image_sampling/index.html FILE: ../../../third_party/skia/demos.skia.org/demos/path_performance/garbage.svg FILE: ../../../third_party/skia/demos.skia.org/demos/path_performance/index.html FILE: ../../../third_party/skia/demos.skia.org/demos/path_performance/main.js FILE: ../../../third_party/skia/demos.skia.org/demos/path_performance/shared.js FILE: ../../../third_party/skia/demos.skia.org/demos/path_performance/worker.js +FILE: ../../../third_party/skia/demos.skia.org/demos/sampling_types/index.html +FILE: ../../../third_party/skia/demos.skia.org/demos/textedit/index.html +FILE: ../../../third_party/skia/demos.skia.org/demos/textedit/spiralshader.js +FILE: ../../../third_party/skia/demos.skia.org/demos/textedit/textapi_utils.js +FILE: ../../../third_party/skia/demos.skia.org/demos/textures/index.html +FILE: ../../../third_party/skia/demos.skia.org/demos/textures/testimg.png +FILE: ../../../third_party/skia/demos.skia.org/demos/up_scaling/index.html FILE: ../../../third_party/skia/demos.skia.org/demos/web_worker/index.html FILE: ../../../third_party/skia/demos.skia.org/demos/web_worker/main.js FILE: ../../../third_party/skia/demos.skia.org/demos/web_worker/shared.js @@ -1015,6 +672,43 @@ FILE: ../../../third_party/skia/experimental/skottiekit/postamble.js FILE: ../../../third_party/skia/experimental/skottiekit/preamble.js FILE: ../../../third_party/skia/experimental/skottiekit/release.js FILE: ../../../third_party/skia/experimental/skottiekit/skottiekit/package.json +FILE: ../../../third_party/skia/experimental/sktext/editor/Cursor.cpp +FILE: ../../../third_party/skia/experimental/sktext/editor/Cursor.h +FILE: ../../../third_party/skia/experimental/sktext/editor/Defaults.h +FILE: ../../../third_party/skia/experimental/sktext/editor/Editor.cpp +FILE: ../../../third_party/skia/experimental/sktext/editor/Editor.h +FILE: ../../../third_party/skia/experimental/sktext/editor/Mouse.cpp +FILE: ../../../third_party/skia/experimental/sktext/editor/Mouse.h +FILE: ../../../third_party/skia/experimental/sktext/editor/Selection.cpp +FILE: ../../../third_party/skia/experimental/sktext/editor/Selection.h +FILE: ../../../third_party/skia/experimental/sktext/editor/Texts.cpp +FILE: ../../../third_party/skia/experimental/sktext/editor/Texts.h +FILE: ../../../third_party/skia/experimental/sktext/include/Text.h +FILE: ../../../third_party/skia/experimental/sktext/include/Types.h +FILE: ../../../third_party/skia/experimental/sktext/samples/Text.cpp +FILE: ../../../third_party/skia/experimental/sktext/src/Line.cpp +FILE: ../../../third_party/skia/experimental/sktext/src/Line.h +FILE: ../../../third_party/skia/experimental/sktext/src/Paint.cpp +FILE: ../../../third_party/skia/experimental/sktext/src/Paint.h +FILE: ../../../third_party/skia/experimental/sktext/src/Text.cpp +FILE: ../../../third_party/skia/experimental/sktext/src/TextRun.cpp +FILE: ../../../third_party/skia/experimental/sktext/src/TextRun.h +FILE: ../../../third_party/skia/experimental/tskit/.eslintignore +FILE: ../../../third_party/skia/experimental/tskit/.eslintrc.js +FILE: ../../../third_party/skia/experimental/tskit/bindings/core.d.ts +FILE: ../../../third_party/skia/experimental/tskit/bindings/embind.d.ts +FILE: ../../../third_party/skia/experimental/tskit/bindings/extension.d.ts +FILE: ../../../third_party/skia/experimental/tskit/build/externs.js +FILE: ../../../third_party/skia/experimental/tskit/interface/core.ts +FILE: ../../../third_party/skia/experimental/tskit/interface/extension.ts +FILE: ../../../third_party/skia/experimental/tskit/interface/load.ts +FILE: ../../../third_party/skia/experimental/tskit/interface/memory.ts +FILE: ../../../third_party/skia/experimental/tskit/interface/public_api.d.ts +FILE: ../../../third_party/skia/experimental/tskit/npm_build/example.html +FILE: ../../../third_party/skia/experimental/tskit/npm_build/types/index.d.ts +FILE: ../../../third_party/skia/experimental/tskit/package-lock.json +FILE: ../../../third_party/skia/experimental/tskit/package.json +FILE: ../../../third_party/skia/experimental/tskit/tsconfig.json FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/cpu.js FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/anim.mskp FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/index.html @@ -1027,12 +721,12 @@ FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/karma.conf.js FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/package.json FILE: ../../../third_party/skia/go.mod FILE: ../../../third_party/skia/go.sum -FILE: ../../../third_party/skia/infra/bots/assets.isolate FILE: ../../../third_party/skia/infra/bots/assets/android_ndk_darwin/VERSION FILE: ../../../third_party/skia/infra/bots/assets/android_ndk_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/android_ndk_windows/VERSION FILE: ../../../third_party/skia/infra/bots/assets/android_sdk_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/armhf_sysroot/VERSION +FILE: ../../../third_party/skia/infra/bots/assets/bazel/VERSION FILE: ../../../third_party/skia/infra/bots/assets/bloaty/VERSION FILE: ../../../third_party/skia/infra/bots/assets/cast_toolchain/VERSION FILE: ../../../third_party/skia/infra/bots/assets/ccache_linux/VERSION @@ -1053,15 +747,14 @@ FILE: ../../../third_party/skia/infra/bots/assets/ios-dev-image-13.3/VERSION FILE: ../../../third_party/skia/infra/bots/assets/ios-dev-image-13.4/VERSION FILE: ../../../third_party/skia/infra/bots/assets/ios-dev-image-13.5/VERSION FILE: ../../../third_party/skia/infra/bots/assets/ios-dev-image-13.6/VERSION +FILE: ../../../third_party/skia/infra/bots/assets/ios-dev-image-14.4/VERSION FILE: ../../../third_party/skia/infra/bots/assets/linux_vulkan_sdk/VERSION FILE: ../../../third_party/skia/infra/bots/assets/lottie-samples/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mesa_intel_driver_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mesa_intel_driver_linux/mesa-driver-builder/Dockerfile +FILE: ../../../third_party/skia/infra/bots/assets/mockery/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mskp/VERSION FILE: ../../../third_party/skia/infra/bots/assets/node/VERSION -FILE: ../../../third_party/skia/infra/bots/assets/opencl_headers/VERSION -FILE: ../../../third_party/skia/infra/bots/assets/opencl_intel_neo_linux/VERSION -FILE: ../../../third_party/skia/infra/bots/assets/opencl_ocl_icd_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/procdump_win/VERSION FILE: ../../../third_party/skia/infra/bots/assets/protoc/VERSION FILE: ../../../third_party/skia/infra/bots/assets/provisioning_profile_ios/VERSION @@ -1074,36 +767,25 @@ FILE: ../../../third_party/skia/infra/bots/assets/valgrind/VERSION FILE: ../../../third_party/skia/infra/bots/assets/win_ninja/VERSION FILE: ../../../third_party/skia/infra/bots/assets/win_toolchain/VERSION FILE: ../../../third_party/skia/infra/bots/assets/xcode-11.4.1/VERSION -FILE: ../../../third_party/skia/infra/bots/canvaskit.isolate FILE: ../../../third_party/skia/infra/bots/cfg.json -FILE: ../../../third_party/skia/infra/bots/compile.isolate -FILE: ../../../third_party/skia/infra/bots/compile_android_framework.isolate -FILE: ../../../third_party/skia/infra/bots/empty.isolate -FILE: ../../../third_party/skia/infra/bots/infra_tests.isolate -FILE: ../../../third_party/skia/infra/bots/infrabots.isolate -FILE: ../../../third_party/skia/infra/bots/ios_bin.isolate FILE: ../../../third_party/skia/infra/bots/jobs.json -FILE: ../../../third_party/skia/infra/bots/lottie_ci.isolate -FILE: ../../../third_party/skia/infra/bots/lottie_web.isolate -FILE: ../../../third_party/skia/infra/bots/pathkit.isolate -FILE: ../../../third_party/skia/infra/bots/perf_puppeteer.isolate -FILE: ../../../third_party/skia/infra/bots/perf_skia_bundled.isolate FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Android_API26.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Chromebook_GLES.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Flutter_Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm64-Release-Android_Wuffs.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86-devrel-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Chromebook_GLES.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Coverage.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-MSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SafeStack.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SwiftShader_MSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SwiftShader_TSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-TSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Tidy.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-V1andV2.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-V1only.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-V2only.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Wuffs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-ASAN.json @@ -1126,23 +808,20 @@ 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-Debian9-Clang-arm-Release-Flutter_Android_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 FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-Metal.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac10.15.5-Clang-arm64-Debug-iOS.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Xcode11.4.1-arm64-Debug-iOS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Dawn.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Direct3D.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Shared.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/unknown-docker-image.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json @@ -1160,7 +839,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NVIDIA_Shield-CPU-TegraX1-arm64-Release-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench_Mskp.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json @@ -1176,14 +855,12 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-ASAN_Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_golo.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_once.json @@ -1197,29 +874,28 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/retry_ios_install_retries_exhausted.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/git/examples/full.expected/test-win.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/git/examples/full.expected/test.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/gold_upload/examples/full.expected/upload_tests.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/gsutil/examples/full.expected/failed_all_uploads.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/gsutil/examples/full.expected/failed_one_upload.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/gsutil/examples/full.expected/gsutil_tests.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/gsutil/examples/full.expected/gsutil_win_tests.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/infra/examples/full.expected/infra_tests.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/run/examples/full.expected/test.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/Build-Debian10-Clang-x86_64-Release-SKNX_NO_SIMD.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/integer_issue.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/win_test.json -FILE: ../../../third_party/skia/infra/bots/recipes.isolate FILE: ../../../third_party/skia/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json FILE: ../../../third_party/skia/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json FILE: ../../../third_party/skia/infra/bots/recipes/compute_buildstats.expected/normal_bot.json FILE: ../../../third_party/skia/infra/bots/recipes/compute_buildstats.expected/trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/compute_test.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json FILE: ../../../third_party/skia/infra/bots/recipes/infra.expected/infra_tests.json +FILE: ../../../third_party/skia/infra/bots/recipes/infra.expected/infra_tests_lottie_ci.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-CPU-Tegra3-arm-Debug-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian10-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian10-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian10-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json @@ -1236,22 +912,18 @@ FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottiewasm_lottieweb.ex FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_gpu.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json -FILE: ../../../third_party/skia/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_DryRun.json -FILE: ../../../third_party/skia/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json -FILE: ../../../third_party/skia/infra/bots/recipes/recreate_skps.expected/failed_upload.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-GalaxyS20-GPU-MaliG77-arm64-Release-All-Android_AllPathsVolatile_Skpbench.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-GalaxyS20-GPU-MaliG77-arm64-Release-All-Android_Vulkan_AllPathsVolatile_Skpbench.json -FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_CCPR_Skpbench.json -FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json +FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench_Mskp.json +FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Release-All-Metal_AllPathsVolatile_Skpbench.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-AllPathsVolatile_Skpbench.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian10-Clang-arm-Release-Flutter_Android.json -FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian10-Clang-universal-devrel-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm-Debug-All-Android_ASAN.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm-Debug-All-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Debug-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json @@ -1263,7 +935,6 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-De FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian10-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian10-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/pathkit_trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian10-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_buildstats_results.expected/normal_bot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_buildstats_results.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json @@ -1273,19 +944,10 @@ FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/no FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_nano_results.expected/normal_bot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_nano_results.expected/trybot.json -FILE: ../../../third_party/skia/infra/bots/resources.isolate -FILE: ../../../third_party/skia/infra/bots/run_recipe.isolate -FILE: ../../../third_party/skia/infra/bots/skottie_wasm.isolate -FILE: ../../../third_party/skia/infra/bots/skpbench_skia_bundled.isolate -FILE: ../../../third_party/skia/infra/bots/skqp.isolate -FILE: ../../../third_party/skia/infra/bots/swarm_recipe.isolate -FILE: ../../../third_party/skia/infra/bots/task_drivers.isolate FILE: ../../../third_party/skia/infra/bots/tasks.json -FILE: ../../../third_party/skia/infra/bots/test_skia_bundled.isolate FILE: ../../../third_party/skia/infra/bots/tools/luci-go/linux64/isolate.sha1 FILE: ../../../third_party/skia/infra/bots/tools/luci-go/mac64/isolate.sha1 FILE: ../../../third_party/skia/infra/bots/tools/luci-go/win64/isolate.exe.sha1 -FILE: ../../../third_party/skia/infra/bots/whole_repo.isolate FILE: ../../../third_party/skia/infra/canvaskit/docker/canvaskit-emsdk/Dockerfile FILE: ../../../third_party/skia/infra/config/recipes.cfg FILE: ../../../third_party/skia/infra/cross-compile/docker/cross-linux-arm64/Dockerfile @@ -1297,19 +959,20 @@ FILE: ../../../third_party/skia/infra/lottiecap/docker/lottie-web-puppeteer/Dock FILE: ../../../third_party/skia/infra/project-config/cr-buildbucket.cfg FILE: ../../../third_party/skia/infra/project-config/project.cfg FILE: ../../../third_party/skia/infra/project-config/refs.cfg -FILE: ../../../third_party/skia/infra/skqp/docker/android-skqp/Dockerfile +FILE: ../../../third_party/skia/infra/skcq.json FILE: ../../../third_party/skia/infra/wasm-common/docker/emsdk-base/Dockerfile FILE: ../../../third_party/skia/infra/wasm-common/docker/gold-karma-chrome-tests/Dockerfile FILE: ../../../third_party/skia/infra/wasm-common/docker/karma-chrome-tests/Dockerfile FILE: ../../../third_party/skia/infra/wasm-common/docker/perf-karma-chrome-tests/Dockerfile FILE: ../../../third_party/skia/modules/canvaskit/catchExceptionNop.js +FILE: ../../../third_party/skia/modules/canvaskit/color.js FILE: ../../../third_party/skia/modules/canvaskit/cpu.js FILE: ../../../third_party/skia/modules/canvaskit/debug.js FILE: ../../../third_party/skia/modules/canvaskit/externs.js FILE: ../../../third_party/skia/modules/canvaskit/font.js FILE: ../../../third_party/skia/modules/canvaskit/fonts/NotoMono-Regular.ttf +FILE: ../../../third_party/skia/modules/canvaskit/gm.js FILE: ../../../third_party/skia/modules/canvaskit/gpu.js -FILE: ../../../third_party/skia/modules/canvaskit/helper.js FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/_namedcolors.js FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/canvas2dcontext.js FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/color.js @@ -1324,35 +987,29 @@ FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/preamble.js FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/radialgradient.js FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/util.js FILE: ../../../third_party/skia/modules/canvaskit/interface.js -FILE: ../../../third_party/skia/modules/canvaskit/karma.bench.conf.js FILE: ../../../third_party/skia/modules/canvaskit/karma.conf.js FILE: ../../../third_party/skia/modules/canvaskit/karma.google3.conf.js +FILE: ../../../third_party/skia/modules/canvaskit/matrix.js +FILE: ../../../third_party/skia/modules/canvaskit/memory.js FILE: ../../../third_party/skia/modules/canvaskit/package-lock.json FILE: ../../../third_party/skia/modules/canvaskit/package.json FILE: ../../../third_party/skia/modules/canvaskit/paragraph.js FILE: ../../../third_party/skia/modules/canvaskit/particles.js FILE: ../../../third_party/skia/modules/canvaskit/pathops.js -FILE: ../../../third_party/skia/modules/canvaskit/perf/animation.bench.js -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/confetti.json -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/drinks.json -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/lego_loader.json -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/onboarding.json -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/test_1500x959.jpg -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/test_512x512.png -FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/test_64x64.png -FILE: ../../../third_party/skia/modules/canvaskit/perf/canvas.bench.js -FILE: ../../../third_party/skia/modules/canvaskit/perf/matrix.bench.js FILE: ../../../third_party/skia/modules/canvaskit/postamble.js FILE: ../../../third_party/skia/modules/canvaskit/preamble.js FILE: ../../../third_party/skia/modules/canvaskit/release.js FILE: ../../../third_party/skia/modules/canvaskit/rt_shader.js FILE: ../../../third_party/skia/modules/canvaskit/skottie.js FILE: ../../../third_party/skia/modules/canvaskit/skp.js +FILE: ../../../third_party/skia/modules/canvaskit/util.js +FILE: ../../../third_party/skia/modules/canvaskit/wasm_tools/viewer.html FILE: ../../../third_party/skia/modules/pathkit/chaining.js FILE: ../../../third_party/skia/modules/pathkit/externs.js FILE: ../../../third_party/skia/modules/pathkit/helper.js FILE: ../../../third_party/skia/modules/pathkit/karma.bench.conf.js FILE: ../../../third_party/skia/modules/pathkit/karma.conf.js +FILE: ../../../third_party/skia/modules/pathkit/package-lock.json FILE: ../../../third_party/skia/modules/pathkit/package.json FILE: ../../../third_party/skia/modules/pathkit/perf/effects.bench.js FILE: ../../../third_party/skia/modules/pathkit/perf/path.bench.js @@ -1394,94 +1051,62 @@ FILE: ../../../third_party/skia/modules/skparagraph/test.html FILE: ../../../third_party/skia/modules/skparagraph/utils/TestFontCollection.cpp FILE: ../../../third_party/skia/modules/skparagraph/utils/TestFontCollection.h FILE: ../../../third_party/skia/public.bzl -FILE: ../../../third_party/skia/site/METADATA -FILE: ../../../third_party/skia/site/dev/METADATA -FILE: ../../../third_party/skia/site/dev/contrib/SuggestedReviewers.png -FILE: ../../../third_party/skia/site/dev/design/PdfLogicalDocumentStructure.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.1.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.1.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.1.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.1.svg -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.2.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.2.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.2.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.2.svg -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.2.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.1.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.1.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.1.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.1.svg -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.2.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.2.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.2.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.2.svg -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.3.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.3.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.3.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/corollary2.3.3.svg -FILE: ../../../third_party/skia/site/dev/design/conical/lemma1.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/lemma1.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/lemma1.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/lemma1.svg -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.1.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.1.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.1.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.1.svg -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.2.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.2.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.2.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.2.svg -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/lemma3.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/lemma4.ggb!/geogebra.xml -FILE: ../../../third_party/skia/site/dev/design/conical/lemma4.ggb!/geogebra_javascript.js -FILE: ../../../third_party/skia/site/dev/design/conical/lemma4.ggb!/geogebra_thumbnail.png -FILE: ../../../third_party/skia/site/dev/design/conical/lemma4.svg -FILE: ../../../third_party/skia/site/dev/tools/buttons.png -FILE: ../../../third_party/skia/site/dev/tools/calendar.mskp -FILE: ../../../third_party/skia/site/dev/tools/crosshair.png -FILE: ../../../third_party/skia/site/dev/tools/debugger.png -FILE: ../../../third_party/skia/site/dev/tools/end.png -FILE: ../../../third_party/skia/site/dev/tools/expand.png -FILE: ../../../third_party/skia/site/dev/tools/frameplayback.png -FILE: ../../../third_party/skia/site/dev/tools/gpuop.png -FILE: ../../../third_party/skia/site/dev/tools/image.png -FILE: ../../../third_party/skia/site/dev/tools/layers.png -FILE: ../../../third_party/skia/site/dev/tools/onlinedebugger.png -FILE: ../../../third_party/skia/site/dev/tools/playcommands.png -FILE: ../../../third_party/skia/site/dev/tools/resources.png -FILE: ../../../third_party/skia/site/dev/tools/settings.png -FILE: ../../../third_party/skia/site/dev/tools/tracing.png -FILE: ../../../third_party/skia/site/dev/tools/tracing_load.png -FILE: ../../../third_party/skia/site/favicon.ico -FILE: ../../../third_party/skia/site/user/METADATA -FILE: ../../../third_party/skia/site/user/api/METADATA -FILE: ../../../third_party/skia/site/user/api/catalog.htm -FILE: ../../../third_party/skia/site/user/modules/METADATA -FILE: ../../../third_party/skia/site/user/modules/PathKit_effects.png +FILE: ../../../third_party/skia/site/_index.html +FILE: ../../../third_party/skia/site/about/_index.html +FILE: ../../../third_party/skia/site/config.toml +FILE: ../../../third_party/skia/site/docs/dev/contrib/SuggestedReviewers.png +FILE: ../../../third_party/skia/site/docs/dev/design/PdfLogicalDocumentStructure.png +FILE: ../../../third_party/skia/site/docs/dev/design/conical/corollary2.2.1.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/corollary2.2.2.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/corollary2.3.1.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/corollary2.3.2.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/corollary2.3.3.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/lemma1.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/lemma3.1.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/lemma3.2.svg +FILE: ../../../third_party/skia/site/docs/dev/design/conical/lemma4.svg +FILE: ../../../third_party/skia/site/docs/dev/tools/buttons.png +FILE: ../../../third_party/skia/site/docs/dev/tools/calendar.mskp +FILE: ../../../third_party/skia/site/docs/dev/tools/crosshair.png +FILE: ../../../third_party/skia/site/docs/dev/tools/debugger.png +FILE: ../../../third_party/skia/site/docs/dev/tools/end.png +FILE: ../../../third_party/skia/site/docs/dev/tools/expand.png +FILE: ../../../third_party/skia/site/docs/dev/tools/frameplayback.png +FILE: ../../../third_party/skia/site/docs/dev/tools/gpuop.png +FILE: ../../../third_party/skia/site/docs/dev/tools/image.png +FILE: ../../../third_party/skia/site/docs/dev/tools/layers.png +FILE: ../../../third_party/skia/site/docs/dev/tools/onlinedebugger.png +FILE: ../../../third_party/skia/site/docs/dev/tools/playcommands.png +FILE: ../../../third_party/skia/site/docs/dev/tools/resources.png +FILE: ../../../third_party/skia/site/docs/dev/tools/settings.png +FILE: ../../../third_party/skia/site/docs/dev/tools/tracing.png +FILE: ../../../third_party/skia/site/docs/dev/tools/tracing_load.png +FILE: ../../../third_party/skia/site/docs/user/modules/PathKit_effects.png +FILE: ../../../third_party/skia/site/featured-background.png FILE: ../../../third_party/skia/specs/web-img-decode/current/index.html FILE: ../../../third_party/skia/specs/web-img-decode/proposed/impl/impl.js FILE: ../../../third_party/skia/specs/web-img-decode/proposed/index.html FILE: ../../../third_party/skia/src/core/SkOrderedReadBuffer.h FILE: ../../../third_party/skia/src/ports/SkTLS_pthread.cpp FILE: ../../../third_party/skia/src/ports/SkTLS_win.cpp -FILE: ../../../third_party/skia/src/sksl/generated/sksl_fp.dehydrated.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_frag.dehydrated.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_geom.dehydrated.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_gpu.dehydrated.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_interp.dehydrated.sksl -FILE: ../../../third_party/skia/src/sksl/generated/sksl_pipeline.dehydrated.sksl +FILE: ../../../third_party/skia/src/sksl/generated/sksl_public.dehydrated.sksl +FILE: ../../../third_party/skia/src/sksl/generated/sksl_rt_blend.dehydrated.sksl +FILE: ../../../third_party/skia/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl +FILE: ../../../third_party/skia/src/sksl/generated/sksl_rt_shader.dehydrated.sksl +FILE: ../../../third_party/skia/src/sksl/generated/sksl_runtime.dehydrated.sksl FILE: ../../../third_party/skia/src/sksl/generated/sksl_vert.dehydrated.sksl FILE: ../../../third_party/skia/src/sksl/lex/sksl.lex -FILE: ../../../third_party/skia/src/sksl/sksl_fp_raw.sksl FILE: ../../../third_party/skia/src/sksl/sksl_frag.sksl FILE: ../../../third_party/skia/src/sksl/sksl_geom.sksl FILE: ../../../third_party/skia/src/sksl/sksl_gpu.sksl -FILE: ../../../third_party/skia/src/sksl/sksl_interp.sksl -FILE: ../../../third_party/skia/src/sksl/sksl_pipeline.sksl +FILE: ../../../third_party/skia/src/sksl/sksl_public.sksl +FILE: ../../../third_party/skia/src/sksl/sksl_rt_blend.sksl +FILE: ../../../third_party/skia/src/sksl/sksl_rt_colorfilter.sksl +FILE: ../../../third_party/skia/src/sksl/sksl_rt_shader.sksl FILE: ../../../third_party/skia/src/sksl/sksl_vert.sksl ---------------------------------------------------------------------------------------------------- Copyright (c) 2011 Google Inc. All rights reserved. @@ -1522,8 +1147,6 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/bench/AAClipBench.cpp FILE: ../../../third_party/skia/bench/Benchmark.cpp FILE: ../../../third_party/skia/bench/Benchmark.h -FILE: ../../../third_party/skia/bench/BitmapBench.cpp -FILE: ../../../third_party/skia/bench/BitmapRectBench.cpp FILE: ../../../third_party/skia/bench/BlurBench.cpp FILE: ../../../third_party/skia/bench/ChromeBench.cpp FILE: ../../../third_party/skia/bench/DashBench.cpp @@ -1557,10 +1180,8 @@ FILE: ../../../third_party/skia/gm/dftext.cpp FILE: ../../../third_party/skia/gm/drawbitmaprect.cpp FILE: ../../../third_party/skia/gm/emptypath.cpp FILE: ../../../third_party/skia/gm/encode.cpp -FILE: ../../../third_party/skia/gm/extractbitmap.cpp FILE: ../../../third_party/skia/gm/filltypes.cpp FILE: ../../../third_party/skia/gm/filltypespersp.cpp -FILE: ../../../third_party/skia/gm/filterbitmap.cpp FILE: ../../../third_party/skia/gm/filterindiabox.cpp FILE: ../../../third_party/skia/gm/fontscaler.cpp FILE: ../../../third_party/skia/gm/gammatext.cpp @@ -1598,7 +1219,6 @@ FILE: ../../../third_party/skia/gm/xfermodes.cpp FILE: ../../../third_party/skia/include/core/SkData.h FILE: ../../../third_party/skia/include/core/SkImageEncoder.h FILE: ../../../third_party/skia/include/core/SkImageFilter.h -FILE: ../../../third_party/skia/include/core/SkMatrix44.h FILE: ../../../third_party/skia/include/core/SkSize.h FILE: ../../../third_party/skia/include/effects/SkLayerDrawLooper.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLConfig.h @@ -1611,42 +1231,32 @@ FILE: ../../../third_party/skia/include/utils/SkNWayCanvas.h FILE: ../../../third_party/skia/include/utils/mac/SkCGUtils.h FILE: ../../../third_party/skia/samplecode/Sample.h FILE: ../../../third_party/skia/samplecode/Sample2PtRadial.cpp -FILE: ../../../third_party/skia/samplecode/SampleAAClip.cpp FILE: ../../../third_party/skia/samplecode/SampleAARectModes.cpp FILE: ../../../third_party/skia/samplecode/SampleAARects.cpp FILE: ../../../third_party/skia/samplecode/SampleArc.cpp -FILE: ../../../third_party/skia/samplecode/SampleBitmapRect.cpp FILE: ../../../third_party/skia/samplecode/SampleCamera.cpp FILE: ../../../third_party/skia/samplecode/SampleCircle.cpp FILE: ../../../third_party/skia/samplecode/SampleClip.cpp -FILE: ../../../third_party/skia/samplecode/SampleColorFilter.cpp FILE: ../../../third_party/skia/samplecode/SampleComplexClip.cpp FILE: ../../../third_party/skia/samplecode/SampleDegenerateTwoPtRadials.cpp FILE: ../../../third_party/skia/samplecode/SampleEffects.cpp FILE: ../../../third_party/skia/samplecode/SampleEmboss.cpp FILE: ../../../third_party/skia/samplecode/SampleFillType.cpp -FILE: ../../../third_party/skia/samplecode/SampleFilter2.cpp -FILE: ../../../third_party/skia/samplecode/SampleFontCache.cpp FILE: ../../../third_party/skia/samplecode/SampleGradients.cpp FILE: ../../../third_party/skia/samplecode/SampleHairCurves.cpp FILE: ../../../third_party/skia/samplecode/SampleHairModes.cpp -FILE: ../../../third_party/skia/samplecode/SampleHairline.cpp FILE: ../../../third_party/skia/samplecode/SampleLCD.cpp -FILE: ../../../third_party/skia/samplecode/SampleLayerMask.cpp FILE: ../../../third_party/skia/samplecode/SampleLayers.cpp FILE: ../../../third_party/skia/samplecode/SamplePatch.cpp FILE: ../../../third_party/skia/samplecode/SamplePath.cpp FILE: ../../../third_party/skia/samplecode/SamplePathClip.cpp FILE: ../../../third_party/skia/samplecode/SamplePathEffects.cpp FILE: ../../../third_party/skia/samplecode/SamplePolyToPoly.cpp -FILE: ../../../third_party/skia/samplecode/SampleRegion.cpp FILE: ../../../third_party/skia/samplecode/SampleRepeatTile.cpp -FILE: ../../../third_party/skia/samplecode/SampleShaders.cpp FILE: ../../../third_party/skia/samplecode/SampleSlides.cpp FILE: ../../../third_party/skia/samplecode/SampleStrokePath.cpp FILE: ../../../third_party/skia/samplecode/SampleStrokeRect.cpp FILE: ../../../third_party/skia/samplecode/SampleTextBox.cpp -FILE: ../../../third_party/skia/samplecode/SampleTextEffects.cpp FILE: ../../../third_party/skia/samplecode/SampleVertices.cpp FILE: ../../../third_party/skia/samplecode/SampleWritePixels.cpp FILE: ../../../third_party/skia/samplecode/SampleXfermodesBlur.cpp @@ -1671,7 +1281,6 @@ FILE: ../../../third_party/skia/src/core/SkFontStream.cpp FILE: ../../../third_party/skia/src/core/SkLineClipper.cpp FILE: ../../../third_party/skia/src/core/SkLineClipper.h FILE: ../../../third_party/skia/src/core/SkMallocPixelRef.cpp -FILE: ../../../third_party/skia/src/core/SkMatrix44.cpp FILE: ../../../third_party/skia/src/core/SkPictureData.cpp FILE: ../../../third_party/skia/src/core/SkPictureData.h FILE: ../../../third_party/skia/src/core/SkPictureFlat.cpp @@ -1690,9 +1299,9 @@ FILE: ../../../third_party/skia/src/core/SkWriter32.cpp FILE: ../../../third_party/skia/src/effects/SkColorMatrix.cpp FILE: ../../../third_party/skia/src/effects/SkColorMatrixFilter.cpp FILE: ../../../third_party/skia/src/effects/SkLayerDrawLooper.cpp -FILE: ../../../third_party/skia/src/effects/SkPackBits.cpp FILE: ../../../third_party/skia/src/effects/SkTableMaskFilter.cpp -FILE: ../../../third_party/skia/src/gpu/GrContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrAttachment.cpp +FILE: ../../../third_party/skia/src/gpu/GrAttachment.h FILE: ../../../third_party/skia/src/gpu/GrGpu.h FILE: ../../../third_party/skia/src/gpu/GrGpuResource.cpp FILE: ../../../third_party/skia/src/gpu/GrNativeRect.h @@ -1703,14 +1312,13 @@ FILE: ../../../third_party/skia/src/gpu/GrPathRendererChain.cpp FILE: ../../../third_party/skia/src/gpu/GrPathRendererChain.h FILE: ../../../third_party/skia/src/gpu/GrRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/GrRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/GrStencilAttachment.cpp -FILE: ../../../third_party/skia/src/gpu/GrStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/GrStencilSettings.cpp FILE: ../../../third_party/skia/src/gpu/GrTexture.cpp FILE: ../../../third_party/skia/src/gpu/GrTexture.h -FILE: ../../../third_party/skia/src/gpu/SkGpuDevice.cpp FILE: ../../../third_party/skia/src/gpu/geometry/GrPathUtils.cpp FILE: ../../../third_party/skia/src/gpu/geometry/GrPathUtils.h +FILE: ../../../third_party/skia/src/gpu/gl/GrGLAttachment.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLAttachment.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLDefines.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLGLSL.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLGLSL.h @@ -1723,8 +1331,6 @@ FILE: ../../../third_party/skia/src/gpu/gl/GrGLProgram.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLProgram.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLStencilAttachment.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLTexture.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLTexture.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLUtil.cpp @@ -1732,10 +1338,11 @@ FILE: ../../../third_party/skia/src/gpu/gl/mac/GrGLMakeNativeInterface_mac.cpp FILE: ../../../third_party/skia/src/gpu/gl/win/GrGLMakeNativeInterface_win.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSL.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSL.h -FILE: ../../../third_party/skia/src/gpu/ops/GrAAHairLinePathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrAAHairLinePathRenderer.h -FILE: ../../../third_party/skia/src/gpu/ops/GrDefaultPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrDefaultPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/AAHairLinePathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/AAHairLinePathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/DefaultPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/DefaultPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/v1/Device.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFDevice.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFDevice.h FILE: ../../../third_party/skia/src/pdf/SkPDFDocument.cpp @@ -1864,7 +1471,7 @@ FILE: ../../../third_party/skia/gm/textblob.cpp FILE: ../../../third_party/skia/gm/textblobshader.cpp FILE: ../../../third_party/skia/gm/tiledscaledbitmap.cpp FILE: ../../../third_party/skia/gm/variedtext.cpp -FILE: ../../../third_party/skia/gm/yuvtorgbeffect.cpp +FILE: ../../../third_party/skia/gm/yuvtorgbsubset.cpp FILE: ../../../third_party/skia/include/c/sk_canvas.h FILE: ../../../third_party/skia/include/c/sk_data.h FILE: ../../../third_party/skia/include/c/sk_image.h @@ -1888,10 +1495,7 @@ FILE: ../../../third_party/skia/include/ports/SkFontMgr_indirect.h FILE: ../../../third_party/skia/include/ports/SkRemotableFontMgr.h FILE: ../../../third_party/skia/include/private/GrResourceKey.h FILE: ../../../third_party/skia/include/private/SkHalf.h -FILE: ../../../third_party/skia/samplecode/SampleHT.cpp -FILE: ../../../third_party/skia/samplecode/SampleIdentityScale.cpp FILE: ../../../third_party/skia/samplecode/SampleRectanizer.cpp -FILE: ../../../third_party/skia/samplecode/SampleSubpixelTranslate.cpp FILE: ../../../third_party/skia/src/c/sk_surface.cpp FILE: ../../../third_party/skia/src/core/SkBBHFactory.cpp FILE: ../../../third_party/skia/src/core/SkBitmapCache.cpp @@ -1931,11 +1535,9 @@ FILE: ../../../third_party/skia/src/fonts/SkRemotableFontMgr.cpp FILE: ../../../third_party/skia/src/gpu/GrDefaultGeoProcFactory.cpp FILE: ../../../third_party/skia/src/gpu/GrDefaultGeoProcFactory.h FILE: ../../../third_party/skia/src/gpu/GrFragmentProcessor.h +FILE: ../../../third_party/skia/src/gpu/GrGeometryProcessor.cpp FILE: ../../../third_party/skia/src/gpu/GrGpuResource.h FILE: ../../../third_party/skia/src/gpu/GrGpuResourceCacheAccess.h -FILE: ../../../third_party/skia/src/gpu/GrPathRendering.cpp -FILE: ../../../third_party/skia/src/gpu/GrPathRendering.h -FILE: ../../../third_party/skia/src/gpu/GrPrimitiveProcessor.cpp FILE: ../../../third_party/skia/src/gpu/GrProcessorAnalysis.cpp FILE: ../../../third_party/skia/src/gpu/GrProcessorAnalysis.h FILE: ../../../third_party/skia/src/gpu/GrProgramDesc.h @@ -1961,27 +1563,22 @@ FILE: ../../../third_party/skia/src/gpu/effects/GrPorterDuffXferProcessor.h FILE: ../../../third_party/skia/src/gpu/effects/GrRRectEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrRRectEffect.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleInterface.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLPathRendering.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLPathRendering.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLTextureRenderTarget.h FILE: ../../../third_party/skia/src/gpu/gl/android/GrGLMakeNativeInterface_android.cpp FILE: ../../../third_party/skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp FILE: ../../../third_party/skia/src/gpu/gl/builders/GrGLProgramBuilder.h FILE: ../../../third_party/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp FILE: ../../../third_party/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.h -FILE: ../../../third_party/skia/src/gpu/gl/egl/GrGLMakeNativeInterface_egl.cpp -FILE: ../../../third_party/skia/src/gpu/gl/glx/GrGLMakeNativeInterface_glx.cpp +FILE: ../../../third_party/skia/src/gpu/gl/egl/GrGLMakeEGLInterface.cpp +FILE: ../../../third_party/skia/src/gpu/gl/glx/GrGLMakeGLXInterface.cpp FILE: ../../../third_party/skia/src/gpu/gl/iOS/GrGLMakeNativeInterface_iOS.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLShaderBuilder.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLXferProcessor.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrDashOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrDashOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/SmallPathRenderer.h FILE: ../../../third_party/skia/src/image/SkReadPixelsRec.h FILE: ../../../third_party/skia/src/image/SkSurface_Gpu.h FILE: ../../../third_party/skia/src/pathops/SkOpBuilder.cpp @@ -2014,7 +1611,398 @@ FILE: ../../../third_party/skia/src/utils/SkPatchUtils.h FILE: ../../../third_party/skia/src/utils/win/SkDWrite.cpp FILE: ../../../third_party/skia/src/utils/win/SkDWrite.h ---------------------------------------------------------------------------------------------------- -Copyright 2014 Google Inc. +Copyright 2014 Google Inc. + +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 the copyright holder 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: skia +ORIGIN: ../../../third_party/skia/bench/AndroidCodecBench.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/bench/AndroidCodecBench.cpp +FILE: ../../../third_party/skia/bench/AndroidCodecBench.h +FILE: ../../../third_party/skia/bench/DecodeBench.cpp +FILE: ../../../third_party/skia/bench/EncodeBench.cpp +FILE: ../../../third_party/skia/bench/GrMipmapBench.cpp +FILE: ../../../third_party/skia/bench/HardStopGradientBench_ScaleNumColors.cpp +FILE: ../../../third_party/skia/bench/HardStopGradientBench_ScaleNumHardStops.cpp +FILE: ../../../third_party/skia/bench/HardStopGradientBench_SpecialHardStops.cpp +FILE: ../../../third_party/skia/bench/ImageCacheBudgetBench.cpp +FILE: ../../../third_party/skia/bench/PDFBench.cpp +FILE: ../../../third_party/skia/bench/QuickRejectBench.cpp +FILE: ../../../third_party/skia/bench/ShapesBench.cpp +FILE: ../../../third_party/skia/bench/StreamBench.cpp +FILE: ../../../third_party/skia/bench/SwizzleBench.cpp +FILE: ../../../third_party/skia/bench/TileImageFilterBench.cpp +FILE: ../../../third_party/skia/bench/VertexColorSpaceBench.cpp +FILE: ../../../third_party/skia/experimental/xps_to_png/xps_to_png.cs +FILE: ../../../third_party/skia/fuzz/Fuzz.cpp +FILE: ../../../third_party/skia/fuzz/Fuzz.h +FILE: ../../../third_party/skia/fuzz/FuzzGradients.cpp +FILE: ../../../third_party/skia/fuzz/FuzzMain.cpp +FILE: ../../../third_party/skia/fuzz/FuzzParsePath.cpp +FILE: ../../../third_party/skia/fuzz/FuzzPathop.cpp +FILE: ../../../third_party/skia/gm/animated_gif.cpp +FILE: ../../../third_party/skia/gm/animatedimageblurs.cpp +FILE: ../../../third_party/skia/gm/arcto.cpp +FILE: ../../../third_party/skia/gm/bigrect.cpp +FILE: ../../../third_party/skia/gm/bitmapimage.cpp +FILE: ../../../third_party/skia/gm/blurcircles2.cpp +FILE: ../../../third_party/skia/gm/bug5252.cpp +FILE: ../../../third_party/skia/gm/bug530095.cpp +FILE: ../../../third_party/skia/gm/bug615686.cpp +FILE: ../../../third_party/skia/gm/circulararcs.cpp +FILE: ../../../third_party/skia/gm/clip_error.cpp +FILE: ../../../third_party/skia/gm/colorfilteralpha8.cpp +FILE: ../../../third_party/skia/gm/complexclip4.cpp +FILE: ../../../third_party/skia/gm/complexclip_blur_tiled.cpp +FILE: ../../../third_party/skia/gm/croppedrects.cpp +FILE: ../../../third_party/skia/gm/dashcircle.cpp +FILE: ../../../third_party/skia/gm/drawregion.cpp +FILE: ../../../third_party/skia/gm/drawregionmodes.cpp +FILE: ../../../third_party/skia/gm/encode_platform.cpp +FILE: ../../../third_party/skia/gm/encode_srgb.cpp +FILE: ../../../third_party/skia/gm/filterbug.cpp +FILE: ../../../third_party/skia/gm/hardstop_gradients.cpp +FILE: ../../../third_party/skia/gm/imagemakewithfilter.cpp +FILE: ../../../third_party/skia/gm/imagemasksubset.cpp +FILE: ../../../third_party/skia/gm/lattice.cpp +FILE: ../../../third_party/skia/gm/overdrawcolorfilter.cpp +FILE: ../../../third_party/skia/gm/overstroke.cpp +FILE: ../../../third_party/skia/gm/pathmaskcache.cpp +FILE: ../../../third_party/skia/gm/readpixels.cpp +FILE: ../../../third_party/skia/gm/rectangletexture.cpp +FILE: ../../../third_party/skia/gm/rrectclipdrawpaint.cpp +FILE: ../../../third_party/skia/gm/shapes.cpp +FILE: ../../../third_party/skia/gm/showmiplevels.cpp +FILE: ../../../third_party/skia/gm/simplerect.cpp +FILE: ../../../third_party/skia/gm/skbug_4868.cpp +FILE: ../../../third_party/skia/gm/skbug_5321.cpp +FILE: ../../../third_party/skia/gm/stroke_rect_shader.cpp +FILE: ../../../third_party/skia/gm/strokedlines.cpp +FILE: ../../../third_party/skia/gm/subsetshader.cpp +FILE: ../../../third_party/skia/gm/textblobblockreordering.cpp +FILE: ../../../third_party/skia/gm/windowrectangles.cpp +FILE: ../../../third_party/skia/include/codec/SkCodecAnimation.h +FILE: ../../../third_party/skia/include/core/SkBlendMode.h +FILE: ../../../third_party/skia/include/core/SkClipOp.h +FILE: ../../../third_party/skia/include/core/SkColorSpace.h +FILE: ../../../third_party/skia/include/core/SkICC.h +FILE: ../../../third_party/skia/include/core/SkMilestone.h +FILE: ../../../third_party/skia/include/core/SkOverdrawCanvas.h +FILE: ../../../third_party/skia/include/core/SkRasterHandleAllocator.h +FILE: ../../../third_party/skia/include/core/SkSwizzle.h +FILE: ../../../third_party/skia/include/effects/SkOverdrawColorFilter.h +FILE: ../../../third_party/skia/include/gpu/vk/GrVkBackendContext.h +FILE: ../../../third_party/skia/include/gpu/vk/GrVkExtensions.h +FILE: ../../../third_party/skia/include/gpu/vk/GrVkTypes.h +FILE: ../../../third_party/skia/include/ports/SkFontMgr_FontConfigInterface.h +FILE: ../../../third_party/skia/include/ports/SkImageGeneratorCG.h +FILE: ../../../third_party/skia/include/ports/SkImageGeneratorWIC.h +FILE: ../../../third_party/skia/include/private/GrSingleOwner.h +FILE: ../../../third_party/skia/include/private/SkBitmaskEnum.h +FILE: ../../../third_party/skia/include/private/SkEncodedInfo.h +FILE: ../../../third_party/skia/include/private/SkSLIRNode.h +FILE: ../../../third_party/skia/include/private/SkSLLayout.h +FILE: ../../../third_party/skia/include/private/SkSLModifiers.h +FILE: ../../../third_party/skia/include/private/SkSLProgramElement.h +FILE: ../../../third_party/skia/include/private/SkSLStatement.h +FILE: ../../../third_party/skia/include/private/SkSLSymbol.h +FILE: ../../../third_party/skia/include/private/SkSafe_math.h +FILE: ../../../third_party/skia/include/utils/SkNoDrawCanvas.h +FILE: ../../../third_party/skia/modules/sksg/samples/SampleSVGPong.cpp +FILE: ../../../third_party/skia/modules/skshaper/include/SkShaper.h +FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp +FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_primitive.cpp +FILE: ../../../third_party/skia/modules/svg/include/SkSVGAttribute.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGAttributeParser.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGCircle.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGClipPath.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGContainer.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGDOM.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGDefs.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGEllipse.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGG.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGHiddenContainer.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGIDMapper.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGLine.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGLinearGradient.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGNode.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGPath.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGPoly.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGRect.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGRenderContext.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGSVG.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGShape.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGStop.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGTransformableNode.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGTypes.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGValue.h +FILE: ../../../third_party/skia/modules/svg/src/SkSVGAttribute.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGAttributeParser.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGCircle.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGClipPath.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGContainer.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGDOM.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGEllipse.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGLine.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGLinearGradient.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGNode.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGPath.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGPoly.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGRect.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGRenderContext.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGSVG.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGShape.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGStop.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGTransformableNode.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGValue.cpp +FILE: ../../../third_party/skia/samplecode/DecodeFile.h +FILE: ../../../third_party/skia/samplecode/Sample.cpp +FILE: ../../../third_party/skia/samplecode/SampleAndroidShadows.cpp +FILE: ../../../third_party/skia/samplecode/SampleMegaStroke.cpp +FILE: ../../../third_party/skia/samplecode/SamplePathOverstroke.cpp +FILE: ../../../third_party/skia/samplecode/SampleSVGFile.cpp +FILE: ../../../third_party/skia/src/codec/SkRawCodec.cpp +FILE: ../../../third_party/skia/src/codec/SkRawCodec.h +FILE: ../../../third_party/skia/src/codec/SkStreamBuffer.cpp +FILE: ../../../third_party/skia/src/codec/SkStreamBuffer.h +FILE: ../../../third_party/skia/src/core/SkATrace.cpp +FILE: ../../../third_party/skia/src/core/SkATrace.h +FILE: ../../../third_party/skia/src/core/SkAnnotationKeys.h +FILE: ../../../third_party/skia/src/core/SkArenaAlloc.cpp +FILE: ../../../third_party/skia/src/core/SkArenaAlloc.h +FILE: ../../../third_party/skia/src/core/SkAutoMalloc.h +FILE: ../../../third_party/skia/src/core/SkAutoPixmapStorage.cpp +FILE: ../../../third_party/skia/src/core/SkAutoPixmapStorage.h +FILE: ../../../third_party/skia/src/core/SkBlendModePriv.h +FILE: ../../../third_party/skia/src/core/SkColorFilter_Matrix.h +FILE: ../../../third_party/skia/src/core/SkColorSpace.cpp +FILE: ../../../third_party/skia/src/core/SkColorSpacePriv.h +FILE: ../../../third_party/skia/src/core/SkCpu.cpp +FILE: ../../../third_party/skia/src/core/SkCpu.h +FILE: ../../../third_party/skia/src/core/SkFixed15.h +FILE: ../../../third_party/skia/src/core/SkFuzzLogging.h +FILE: ../../../third_party/skia/src/core/SkGlobalInitialization_core.cpp +FILE: ../../../third_party/skia/src/core/SkICC.cpp +FILE: ../../../third_party/skia/src/core/SkICCPriv.h +FILE: ../../../third_party/skia/src/core/SkImageFilterCache.cpp +FILE: ../../../third_party/skia/src/core/SkImageFilterCache.h +FILE: ../../../third_party/skia/src/core/SkLRUCache.h +FILE: ../../../third_party/skia/src/core/SkLeanWindows.h +FILE: ../../../third_party/skia/src/core/SkMSAN.h +FILE: ../../../third_party/skia/src/core/SkMatrixPriv.h +FILE: ../../../third_party/skia/src/core/SkModeColorFilter.h +FILE: ../../../third_party/skia/src/core/SkOverdrawCanvas.cpp +FILE: ../../../third_party/skia/src/core/SkPathMeasurePriv.h +FILE: ../../../third_party/skia/src/core/SkRasterPipeline.cpp +FILE: ../../../third_party/skia/src/core/SkRasterPipeline.h +FILE: ../../../third_party/skia/src/core/SkRasterPipelineBlitter.cpp +FILE: ../../../third_party/skia/src/core/SkRecordedDrawable.cpp +FILE: ../../../third_party/skia/src/core/SkRecordedDrawable.h +FILE: ../../../third_party/skia/src/core/SkScaleToSides.h +FILE: ../../../third_party/skia/src/core/SkScopeExit.h +FILE: ../../../third_party/skia/src/core/SkSpecialImage.cpp +FILE: ../../../third_party/skia/src/core/SkSpecialImage.h +FILE: ../../../third_party/skia/src/core/SkSpecialSurface.cpp +FILE: ../../../third_party/skia/src/core/SkSpecialSurface.h +FILE: ../../../third_party/skia/src/core/SkSwizzle.cpp +FILE: ../../../third_party/skia/src/effects/SkOverdrawColorFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkArithmeticImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkShaderImageFilter.cpp +FILE: ../../../third_party/skia/src/gpu/GrAppliedClip.h +FILE: ../../../third_party/skia/src/gpu/GrAuditTrail.cpp +FILE: ../../../third_party/skia/src/gpu/GrAuditTrail.h +FILE: ../../../third_party/skia/src/gpu/GrColorSpaceXform.cpp +FILE: ../../../third_party/skia/src/gpu/GrColorSpaceXform.h +FILE: ../../../third_party/skia/src/gpu/GrDirectContextPriv.h +FILE: ../../../third_party/skia/src/gpu/GrFixedClip.h +FILE: ../../../third_party/skia/src/gpu/GrOpsRenderPass.cpp +FILE: ../../../third_party/skia/src/gpu/GrOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/GrProgramDesc.cpp +FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxy.cpp +FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxy.h +FILE: ../../../third_party/skia/src/gpu/GrResourceHandle.h +FILE: ../../../third_party/skia/src/gpu/GrScissorState.h +FILE: ../../../third_party/skia/src/gpu/GrShaderVar.cpp +FILE: ../../../third_party/skia/src/gpu/GrShaderVar.h +FILE: ../../../third_party/skia/src/gpu/GrStencilSettings.h +FILE: ../../../third_party/skia/src/gpu/GrStyle.cpp +FILE: ../../../third_party/skia/src/gpu/GrStyle.h +FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxy.cpp +FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxy.h +FILE: ../../../third_party/skia/src/gpu/GrSwizzle.h +FILE: ../../../third_party/skia/src/gpu/GrTextureProxy.cpp +FILE: ../../../third_party/skia/src/gpu/GrTextureProxy.h +FILE: ../../../third_party/skia/src/gpu/GrTextureRenderTargetProxy.cpp +FILE: ../../../third_party/skia/src/gpu/GrTextureRenderTargetProxy.h +FILE: ../../../third_party/skia/src/gpu/GrUserStencilSettings.h +FILE: ../../../third_party/skia/src/gpu/GrWindowRectangles.h +FILE: ../../../third_party/skia/src/gpu/GrWindowRectsState.h +FILE: ../../../third_party/skia/src/gpu/SurfaceContext.cpp +FILE: ../../../third_party/skia/src/gpu/SurfaceContext.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineStateDataManager.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrShadowGeoProc.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrShadowGeoProc.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrStyledShape.cpp +FILE: ../../../third_party/skia/src/gpu/geometry/GrStyledShape.h +FILE: ../../../third_party/skia/src/gpu/gl/GrGLBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLBuffer.h +FILE: ../../../third_party/skia/src/gpu/gl/GrGLOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/gl/glfw/GrGLMakeNativeInterface_glfw.cpp +FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLColorSpaceXformHelper.h +FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramDataManager.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrPathStencilSettings.h +FILE: ../../../third_party/skia/src/gpu/ops/GrRegionOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrRegionOp.h +FILE: ../../../third_party/skia/src/gpu/ops/GrShadowRRectOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrShadowRRectOp.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorPool.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorPool.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSet.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSet.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSetManager.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkDescriptorSetManager.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkExtensions.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkFramebuffer.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkFramebuffer.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageView.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageView.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkOpsRenderPass.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipeline.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipeline.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineState.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineState.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateBuilder.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateBuilder.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateCache.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateDataManager.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineStateDataManager.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkResourceProvider.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkResourceProvider.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSampler.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSampler.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformHandler.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformHandler.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkVaryingHandler.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkVaryingHandler.h +FILE: ../../../third_party/skia/src/images/SkImageEncoderPriv.h +FILE: ../../../third_party/skia/src/opts/SkChecksum_opts.h +FILE: ../../../third_party/skia/src/opts/SkOpts_avx.cpp +FILE: ../../../third_party/skia/src/opts/SkOpts_crc32.cpp +FILE: ../../../third_party/skia/src/opts/SkOpts_sse42.cpp +FILE: ../../../third_party/skia/src/opts/SkSwizzler_opts.h +FILE: ../../../third_party/skia/src/pdf/SkBitmapKey.h +FILE: ../../../third_party/skia/src/pdf/SkPDFDocumentPriv.h +FILE: ../../../third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp +FILE: ../../../third_party/skia/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h +FILE: ../../../third_party/skia/src/pdf/SkPDFMakeToUnicodeCmap.h +FILE: ../../../third_party/skia/src/ports/SkFontConfigInterface.cpp +FILE: ../../../third_party/skia/src/ports/SkFontMgr_custom_empty_factory.cpp +FILE: ../../../third_party/skia/src/ports/SkImageGeneratorCG.cpp +FILE: ../../../third_party/skia/src/ports/SkImageGeneratorWIC.cpp +FILE: ../../../third_party/skia/src/shaders/SkColorFilterShader.h +FILE: ../../../third_party/skia/src/shaders/SkColorShader.cpp +FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.cpp +FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.h +FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientPriv.h +FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fLinearGradient.cpp +FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fLinearGradient.h +FILE: ../../../third_party/skia/src/sksl/SkSLCompiler.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLCompiler.h +FILE: ../../../third_party/skia/src/sksl/SkSLContext.h +FILE: ../../../third_party/skia/src/sksl/SkSLIRGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLIRGenerator.h +FILE: ../../../third_party/skia/src/sksl/SkSLMain.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLMemoryLayout.h +FILE: ../../../third_party/skia/src/sksl/SkSLParser.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLParser.h +FILE: ../../../third_party/skia/src/sksl/SkSLPosition.h +FILE: ../../../third_party/skia/src/sksl/SkSLProgramSettings.h +FILE: ../../../third_party/skia/src/sksl/SkSLUtil.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLUtil.h +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLCodeGenerator.h +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.h +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLMetalCodeGenerator.h +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLBinaryExpression.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLBlock.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLBoolLiteral.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLBreakStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructor.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLContinueStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLDiscardStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLDoStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExpression.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExpressionStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExtension.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLField.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFieldAccess.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFloatLiteral.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLForStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionCall.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionDeclaration.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionDefinition.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionReference.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLIfStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLIndexExpression.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLIntLiteral.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLInterfaceBlock.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLModifiersDeclaration.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLNop.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLPostfixExpression.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLPrefixExpression.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLProgram.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLReturnStatement.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSetting.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSwizzle.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSymbolTable.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSymbolTable.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLTernaryExpression.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLType.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLType.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLTypeReference.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLUnresolvedFunction.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLVarDeclarations.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariable.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariableReference.h +FILE: ../../../third_party/skia/src/utils/SkMultiPictureDocument.cpp +FILE: ../../../third_party/skia/src/utils/SkMultiPictureDocument.h +FILE: ../../../third_party/skia/src/utils/SkMultiPictureDocumentPriv.h +FILE: ../../../third_party/skia/src/utils/SkOSPath.h +---------------------------------------------------------------------------------------------------- +Copyright 2016 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2059,6 +2047,7 @@ FILE: ../../../third_party/skia/bench/LineBench.cpp FILE: ../../../third_party/skia/bench/Matrix44Bench.cpp FILE: ../../../third_party/skia/bench/MatrixConvolutionBench.cpp FILE: ../../../third_party/skia/bench/MorphologyBench.cpp +FILE: ../../../third_party/skia/bench/ParagraphBench.cpp FILE: ../../../third_party/skia/bench/RTreeBench.cpp FILE: ../../../third_party/skia/bench/RefCntBench.cpp FILE: ../../../third_party/skia/bench/TableBench.cpp @@ -2081,6 +2070,7 @@ FILE: ../../../third_party/skia/gm/patheffects.cpp FILE: ../../../third_party/skia/gm/pathinterior.cpp FILE: ../../../third_party/skia/gm/rrect.cpp FILE: ../../../third_party/skia/gm/rrects.cpp +FILE: ../../../third_party/skia/gm/runtimeimagefilter.cpp FILE: ../../../third_party/skia/gm/samplerstress.cpp FILE: ../../../third_party/skia/gm/simpleaaclip.cpp FILE: ../../../third_party/skia/gm/srcmode.cpp @@ -2118,12 +2108,9 @@ FILE: ../../../third_party/skia/src/core/SkRTree.h FILE: ../../../third_party/skia/src/core/SkReadBuffer.cpp FILE: ../../../third_party/skia/src/core/SkStrokeRec.cpp FILE: ../../../third_party/skia/src/core/SkTInternalLList.h -FILE: ../../../third_party/skia/src/core/SkTLList.h FILE: ../../../third_party/skia/src/core/SkWriteBuffer.cpp FILE: ../../../third_party/skia/src/gpu/GrMemoryPool.cpp FILE: ../../../third_party/skia/src/gpu/GrMemoryPool.h -FILE: ../../../third_party/skia/src/gpu/GrPath.cpp -FILE: ../../../third_party/skia/src/gpu/GrPath.h FILE: ../../../third_party/skia/src/gpu/GrProcessor.cpp FILE: ../../../third_party/skia/src/gpu/GrProcessor.h FILE: ../../../third_party/skia/src/gpu/GrProcessorUnitTest.h @@ -2131,23 +2118,19 @@ FILE: ../../../third_party/skia/src/gpu/GrSWMaskHelper.cpp FILE: ../../../third_party/skia/src/gpu/GrSWMaskHelper.h FILE: ../../../third_party/skia/src/gpu/GrShaderCaps.cpp FILE: ../../../third_party/skia/src/gpu/GrShaderCaps.h -FILE: ../../../third_party/skia/src/gpu/GrSoftwarePathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/GrSoftwarePathRenderer.h FILE: ../../../third_party/skia/src/gpu/GrSurface.cpp FILE: ../../../third_party/skia/src/gpu/GrSurface.h FILE: ../../../third_party/skia/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLCaps.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLCaps.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLPath.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLPath.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLProgramDataManager.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLProgramDataManager.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLUtil.h -FILE: ../../../third_party/skia/src/gpu/ops/GrAAConvexPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrAAConvexPathRenderer.h -FILE: ../../../third_party/skia/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrStencilAndCoverPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/AAConvexPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/AAConvexPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/SoftwarePathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/SoftwarePathRenderer.h FILE: ../../../third_party/skia/src/image/SkImage.cpp FILE: ../../../third_party/skia/src/image/SkImage_Base.h FILE: ../../../third_party/skia/src/image/SkImage_Gpu.cpp @@ -2294,7 +2277,6 @@ FILE: ../../../third_party/skia/client_utils/android/BitmapRegionDecoder.h FILE: ../../../third_party/skia/client_utils/android/BitmapRegionDecoderPriv.h FILE: ../../../third_party/skia/dm/DMSrcSink.cpp FILE: ../../../third_party/skia/dm/DMSrcSink.h -FILE: ../../../third_party/skia/example/SkiaSDLExample.cpp FILE: ../../../third_party/skia/experimental/c-api-example/skia-c-example.c FILE: ../../../third_party/skia/experimental/tools/coreGraphicsPdf2png.cpp FILE: ../../../third_party/skia/gm/aaxfermodes.cpp @@ -2355,15 +2337,12 @@ FILE: ../../../third_party/skia/gm/xform.cpp FILE: ../../../third_party/skia/include/codec/SkAndroidCodec.h FILE: ../../../third_party/skia/include/codec/SkCodec.h FILE: ../../../third_party/skia/include/core/SkEncodedImageFormat.h -FILE: ../../../third_party/skia/include/core/SkFilterQuality.h FILE: ../../../third_party/skia/include/core/SkPathBuilder.h FILE: ../../../third_party/skia/include/core/SkPixmap.h FILE: ../../../third_party/skia/include/core/SkPngChunkReader.h FILE: ../../../third_party/skia/include/core/SkPoint3.h FILE: ../../../third_party/skia/include/core/SkRSXform.h -FILE: ../../../third_party/skia/include/core/SkRWBuffer.h FILE: ../../../third_party/skia/include/core/SkTraceMemoryDump.h -FILE: ../../../third_party/skia/include/effects/SkImageSource.h FILE: ../../../third_party/skia/include/effects/SkTableColorFilter.h FILE: ../../../third_party/skia/include/gpu/GrContextOptions.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLTypes.h @@ -2381,12 +2360,8 @@ FILE: ../../../third_party/skia/include/private/SkTHash.h FILE: ../../../third_party/skia/include/private/SkThreadID.h FILE: ../../../third_party/skia/include/svg/SkSVGCanvas.h FILE: ../../../third_party/skia/include/utils/SkPaintFilterCanvas.h -FILE: ../../../third_party/skia/samplecode/PerlinPatch.cpp -FILE: ../../../third_party/skia/samplecode/SampleAAGeometry.cpp FILE: ../../../third_party/skia/samplecode/SampleAnimatedText.cpp FILE: ../../../third_party/skia/samplecode/SampleAtlas.cpp -FILE: ../../../third_party/skia/samplecode/SampleClipDrawMatch.cpp -FILE: ../../../third_party/skia/samplecode/SampleFilterQuality.cpp FILE: ../../../third_party/skia/samplecode/SampleShip.cpp FILE: ../../../third_party/skia/samplecode/SampleXfer.cpp FILE: ../../../third_party/skia/src/c/sk_c_from_to.h @@ -2433,14 +2408,14 @@ FILE: ../../../third_party/skia/src/codec/SkWebpCodec.h FILE: ../../../third_party/skia/src/core/Sk4px.h FILE: ../../../third_party/skia/src/core/SkBigPicture.cpp FILE: ../../../third_party/skia/src/core/SkBigPicture.h -FILE: ../../../third_party/skia/src/core/SkBitmapController.cpp -FILE: ../../../third_party/skia/src/core/SkBitmapController.h FILE: ../../../third_party/skia/src/core/SkFontMgr.cpp FILE: ../../../third_party/skia/src/core/SkLatticeIter.cpp FILE: ../../../third_party/skia/src/core/SkLatticeIter.h FILE: ../../../third_party/skia/src/core/SkLocalMatrixImageFilter.cpp FILE: ../../../third_party/skia/src/core/SkMiniRecorder.cpp FILE: ../../../third_party/skia/src/core/SkMiniRecorder.h +FILE: ../../../third_party/skia/src/core/SkMipmapAccessor.cpp +FILE: ../../../third_party/skia/src/core/SkMipmapAccessor.h FILE: ../../../third_party/skia/src/core/SkNextID.h FILE: ../../../third_party/skia/src/core/SkOpts.cpp FILE: ../../../third_party/skia/src/core/SkOpts.h @@ -2451,7 +2426,6 @@ FILE: ../../../third_party/skia/src/core/SkPictureImageGenerator.cpp FILE: ../../../third_party/skia/src/core/SkPixmap.cpp FILE: ../../../third_party/skia/src/core/SkPixmapPriv.h FILE: ../../../third_party/skia/src/core/SkPoint3.cpp -FILE: ../../../third_party/skia/src/core/SkRWBuffer.cpp FILE: ../../../third_party/skia/src/core/SkRecord.cpp FILE: ../../../third_party/skia/src/core/SkRecordPattern.h FILE: ../../../third_party/skia/src/core/SkRecords.cpp @@ -2460,7 +2434,6 @@ FILE: ../../../third_party/skia/src/core/SkSharedMutex.cpp FILE: ../../../third_party/skia/src/core/SkSharedMutex.h FILE: ../../../third_party/skia/src/core/SkSpinlock.cpp FILE: ../../../third_party/skia/src/core/SkTDPQueue.h -FILE: ../../../third_party/skia/src/core/SkTTopoSort.h FILE: ../../../third_party/skia/src/core/SkThreadID.cpp FILE: ../../../third_party/skia/src/core/SkTime.cpp FILE: ../../../third_party/skia/src/core/SkXfermodeInterpretation.cpp @@ -2468,7 +2441,7 @@ FILE: ../../../third_party/skia/src/core/SkXfermodeInterpretation.h FILE: ../../../third_party/skia/src/core/SkYUVPlanesCache.cpp FILE: ../../../third_party/skia/src/core/SkYUVPlanesCache.h FILE: ../../../third_party/skia/src/effects/SkTableColorFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkImageSource.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkImageImageFilter.cpp FILE: ../../../third_party/skia/src/gpu/GrAutoLocaleSetter.h FILE: ../../../third_party/skia/src/gpu/GrBlurUtils.cpp FILE: ../../../third_party/skia/src/gpu/GrBlurUtils.h @@ -2488,52 +2461,45 @@ FILE: ../../../third_party/skia/src/gpu/GrOpFlushState.h FILE: ../../../third_party/skia/src/gpu/GrPipeline.cpp FILE: ../../../third_party/skia/src/gpu/GrPipeline.h FILE: ../../../third_party/skia/src/gpu/GrProcessorUnitTest.cpp -FILE: ../../../third_party/skia/src/gpu/GrRenderTargetContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrRenderTargetContext.h FILE: ../../../third_party/skia/src/gpu/GrResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/GrResourceProvider.h FILE: ../../../third_party/skia/src/gpu/GrSamplerState.h FILE: ../../../third_party/skia/src/gpu/GrSimpleMesh.h +FILE: ../../../third_party/skia/src/gpu/GrTTopoSort.h FILE: ../../../third_party/skia/src/gpu/GrTestUtils.cpp FILE: ../../../third_party/skia/src/gpu/GrTestUtils.h -FILE: ../../../third_party/skia/src/gpu/GrTriangulator.cpp -FILE: ../../../third_party/skia/src/gpu/GrTriangulator.h FILE: ../../../third_party/skia/src/gpu/GrXferProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/SkGpuDevice_drawTexture.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrBlendFragmentProcessor.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrBlendFragmentProcessor.h FILE: ../../../third_party/skia/src/gpu/effects/GrCustomXfermode.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrCustomXfermode.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrAAConvexTessellator.cpp +FILE: ../../../third_party/skia/src/gpu/geometry/GrAAConvexTessellator.h FILE: ../../../third_party/skia/src/gpu/geometry/GrQuad.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrTriangulator.cpp +FILE: ../../../third_party/skia/src/gpu/geometry/GrTriangulator.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLTextureRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLUniformHandler.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLUniformHandler.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLVaryingHandler.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLVaryingHandler.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLBlend.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLBlend.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramBuilder.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramDataManager.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLUniformHandler.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLVarying.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLVarying.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLXferProcessor.h -FILE: ../../../third_party/skia/src/gpu/ops/GrAAConvexTessellator.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrAAConvexTessellator.h -FILE: ../../../third_party/skia/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrAALinearizingConvexPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/AALinearizingConvexPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/AALinearizingConvexPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/DashLinePathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/DashLinePathRenderer.h FILE: ../../../third_party/skia/src/gpu/ops/GrAtlasTextOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrAtlasTextOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrClearOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrDashLinePathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrDashLinePathRenderer.h FILE: ../../../third_party/skia/src/gpu/ops/GrDrawAtlasOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrDrawAtlasOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrDrawOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrDrawPathOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrDrawPathOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrDrawVerticesOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrDrawVerticesOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrLatticeOp.cpp @@ -2542,7 +2508,6 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrMeshDrawOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrMeshDrawOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrStencilPathOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrTriangulatingPathRenderer.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrTriangulatingPathRenderer.h FILE: ../../../third_party/skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp @@ -2553,8 +2518,11 @@ FILE: ../../../third_party/skia/src/gpu/text/GrTextBlob.cpp FILE: ../../../third_party/skia/src/gpu/text/GrTextBlob.h FILE: ../../../third_party/skia/src/gpu/text/GrTextBlobCache.cpp FILE: ../../../third_party/skia/src/gpu/text/GrTextBlobCache.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkBuffer.h +FILE: ../../../third_party/skia/src/gpu/v1/Device_drawTexture.cpp +FILE: ../../../third_party/skia/src/gpu/v1/SurfaceDrawContext.cpp +FILE: ../../../third_party/skia/src/gpu/v1/SurfaceDrawContext_v1.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkAttachment.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkAttachment.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkCaps.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkCaps.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandBuffer.cpp @@ -2567,22 +2535,14 @@ FILE: ../../../third_party/skia/src/gpu/vk/GrVkInterface.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkInterface.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkMemory.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkMemory.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkMeshBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkMeshBuffer.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderPass.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkStencilAttachment.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkTexture.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkTexture.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkTextureRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkTextureRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkTransferBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkTransferBuffer.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformBuffer.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkUtil.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkUtil.h FILE: ../../../third_party/skia/src/image/SkImage_Gpu.h @@ -2675,7 +2635,6 @@ FILE: ../../../third_party/skia/bench/GrResourceCacheBench.cpp FILE: ../../../third_party/skia/bench/HairlinePathBench.cpp FILE: ../../../third_party/skia/bench/ImageCacheBench.cpp FILE: ../../../third_party/skia/bench/LightingBench.cpp -FILE: ../../../third_party/skia/bench/MagnifierBench.cpp FILE: ../../../third_party/skia/bench/MemsetBench.cpp FILE: ../../../third_party/skia/bench/MergeBench.cpp FILE: ../../../third_party/skia/bench/PerlinNoiseBench.cpp @@ -2706,7 +2665,6 @@ FILE: ../../../third_party/skia/gm/coloremoji.cpp FILE: ../../../third_party/skia/gm/conicpaths.cpp FILE: ../../../third_party/skia/gm/copy_to_4444.cpp FILE: ../../../third_party/skia/gm/displacement.cpp -FILE: ../../../third_party/skia/gm/downsamplebitmap.cpp FILE: ../../../third_party/skia/gm/dropshadowimagefilter.cpp FILE: ../../../third_party/skia/gm/dstreadshuffle.cpp FILE: ../../../third_party/skia/gm/fontcache.cpp @@ -2729,257 +2687,110 @@ FILE: ../../../third_party/skia/gm/pathopsinverse.cpp FILE: ../../../third_party/skia/gm/perlinnoise.cpp FILE: ../../../third_party/skia/gm/pictureimagefilter.cpp FILE: ../../../third_party/skia/gm/polygons.cpp -FILE: ../../../third_party/skia/gm/rects.cpp FILE: ../../../third_party/skia/gm/resizeimagefilter.cpp FILE: ../../../third_party/skia/gm/roundrects.cpp FILE: ../../../third_party/skia/gm/shallowgradient.cpp FILE: ../../../third_party/skia/gm/skbug1719.cpp FILE: ../../../third_party/skia/gm/spritebitmap.cpp FILE: ../../../third_party/skia/gm/stringart.cpp -FILE: ../../../third_party/skia/gm/textbloblooper.cpp FILE: ../../../third_party/skia/gm/thinrects.cpp FILE: ../../../third_party/skia/gm/thinstrokedrects.cpp FILE: ../../../third_party/skia/gm/tileimagefilter.cpp FILE: ../../../third_party/skia/gm/vertices.cpp FILE: ../../../third_party/skia/gm/xfermodeimagefilter.cpp FILE: ../../../third_party/skia/gm/xfermodes2.cpp -FILE: ../../../third_party/skia/gm/xfermodes3.cpp -FILE: ../../../third_party/skia/include/core/SkDataTable.h -FILE: ../../../third_party/skia/include/core/SkDocument.h -FILE: ../../../third_party/skia/include/core/SkFontLCDConfig.h -FILE: ../../../third_party/skia/include/core/SkFontMgr.h -FILE: ../../../third_party/skia/include/core/SkFontStyle.h -FILE: ../../../third_party/skia/include/core/SkImageGenerator.h -FILE: ../../../third_party/skia/include/core/SkImageInfo.h -FILE: ../../../third_party/skia/include/effects/SkAlphaThresholdFilter.h -FILE: ../../../third_party/skia/include/effects/SkComposeImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkDisplacementMapEffect.h -FILE: ../../../third_party/skia/include/effects/SkDropShadowImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkLumaColorFilter.h -FILE: ../../../third_party/skia/include/effects/SkPerlinNoiseShader.h -FILE: ../../../third_party/skia/include/effects/SkTileImageFilter.h -FILE: ../../../third_party/skia/include/gpu/gl/GrGLExtensions.h -FILE: ../../../third_party/skia/include/ports/SkFontConfigInterface.h -FILE: ../../../third_party/skia/include/private/GrTypesPriv.h -FILE: ../../../third_party/skia/include/private/SkOnce.h -FILE: ../../../third_party/skia/include/private/SkTFitsIn.h -FILE: ../../../third_party/skia/include/private/SkTLogic.h -FILE: ../../../third_party/skia/include/utils/SkCanvasStateUtils.h -FILE: ../../../third_party/skia/samplecode/SampleChart.cpp -FILE: ../../../third_party/skia/samplecode/SampleClock.cpp -FILE: ../../../third_party/skia/samplecode/SampleManyRects.cpp -FILE: ../../../third_party/skia/samplecode/SampleStringArt.cpp -FILE: ../../../third_party/skia/samplecode/SampleUnpremul.cpp -FILE: ../../../third_party/skia/src/core/SkBitmapDevice.cpp -FILE: ../../../third_party/skia/src/core/SkBitmapDevice.h -FILE: ../../../third_party/skia/src/core/SkDataTable.cpp -FILE: ../../../third_party/skia/src/core/SkDiscardableMemory.h -FILE: ../../../third_party/skia/src/core/SkDocument.cpp -FILE: ../../../third_party/skia/src/core/SkDrawLooper.cpp -FILE: ../../../third_party/skia/src/core/SkFontStream.h -FILE: ../../../third_party/skia/src/core/SkGpuBlurUtils.cpp -FILE: ../../../third_party/skia/src/core/SkGpuBlurUtils.h -FILE: ../../../third_party/skia/src/core/SkLegacyGpuBlurUtils.cpp -FILE: ../../../third_party/skia/src/core/SkMatrixUtils.h -FILE: ../../../third_party/skia/src/core/SkMessageBus.h -FILE: ../../../third_party/skia/src/core/SkMipmap.cpp -FILE: ../../../third_party/skia/src/core/SkMipmap.h -FILE: ../../../third_party/skia/src/core/SkPaintPriv.cpp -FILE: ../../../third_party/skia/src/core/SkPaintPriv.h -FILE: ../../../third_party/skia/src/core/SkPathRef.cpp -FILE: ../../../third_party/skia/src/core/SkResourceCache.cpp -FILE: ../../../third_party/skia/src/core/SkResourceCache.h -FILE: ../../../third_party/skia/src/core/SkStreamPriv.h -FILE: ../../../third_party/skia/src/core/SkStringUtils.cpp -FILE: ../../../third_party/skia/src/core/SkStringUtils.h -FILE: ../../../third_party/skia/src/core/SkTDynamicHash.h -FILE: ../../../third_party/skia/src/core/SkTMultiMap.h -FILE: ../../../third_party/skia/src/core/SkValidationUtils.h -FILE: ../../../third_party/skia/src/effects/SkLumaColorFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkAlphaThresholdFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkComposeImageFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkDisplacementMapEffect.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkDropShadowImageFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkTileImageFilter.cpp -FILE: ../../../third_party/skia/src/gpu/GrBlend.h -FILE: ../../../third_party/skia/src/gpu/GrCaps.h -FILE: ../../../third_party/skia/src/gpu/GrGeometryProcessor.h -FILE: ../../../third_party/skia/src/gpu/GrPaint.cpp -FILE: ../../../third_party/skia/src/gpu/GrPathProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/GrPathProcessor.h -FILE: ../../../third_party/skia/src/gpu/GrPrimitiveProcessor.h -FILE: ../../../third_party/skia/src/gpu/GrRectanizerSkyline.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrBezierEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrBezierEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/GrBicubicEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/GrBitmapTextGeoProc.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrBitmapTextGeoProc.h -FILE: ../../../third_party/skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrDistanceFieldGeoProc.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLContext.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLContext.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLExtensions.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLVertexArray.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLVertexArray.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLGeometryProcessor.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.h -FILE: ../../../third_party/skia/src/gpu/ops/GrOvalOpFactory.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrOvalOpFactory.h -FILE: ../../../third_party/skia/src/lazy/SkDiscardableMemoryPool.cpp -FILE: ../../../third_party/skia/src/lazy/SkDiscardableMemoryPool.h -FILE: ../../../third_party/skia/src/pathops/SkOpCoincidence.h -FILE: ../../../third_party/skia/src/pathops/SkOpContour.cpp -FILE: ../../../third_party/skia/src/pathops/SkOpContour.h -FILE: ../../../third_party/skia/src/pathops/SkPathOpsDebug.cpp -FILE: ../../../third_party/skia/src/pathops/SkPathOpsDebug.h -FILE: ../../../third_party/skia/src/pdf/SkPDFResourceDict.cpp -FILE: ../../../third_party/skia/src/pdf/SkPDFResourceDict.h -FILE: ../../../third_party/skia/src/ports/SkDiscardableMemory_none.cpp -FILE: ../../../third_party/skia/src/ports/SkFontConfigTypeface.h -FILE: ../../../third_party/skia/src/ports/SkFontMgr_FontConfigInterface.cpp -FILE: ../../../third_party/skia/src/ports/SkOSFile_posix.cpp -FILE: ../../../third_party/skia/src/ports/SkOSFile_win.cpp -FILE: ../../../third_party/skia/src/sfnt/SkOTTable_name.cpp -FILE: ../../../third_party/skia/src/sfnt/SkTTCFHeader.h -FILE: ../../../third_party/skia/src/shaders/SkColorFilterShader.cpp -FILE: ../../../third_party/skia/src/shaders/SkPerlinNoiseShader.cpp -FILE: ../../../third_party/skia/src/utils/SkCanvasStack.cpp -FILE: ../../../third_party/skia/src/utils/SkCanvasStack.h -FILE: ../../../third_party/skia/src/utils/SkCanvasStateUtils.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2013 Google Inc. - -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 the copyright holder 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: skia -ORIGIN: ../../../third_party/skia/bench/BulkRectBench.cpp + ../../../third_party/skia/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/bench/BulkRectBench.cpp -FILE: ../../../third_party/skia/bench/DDLRecorderBench.cpp -FILE: ../../../third_party/skia/bench/SkSLBench.cpp -FILE: ../../../third_party/skia/bench/SkSLInterpreterBench.cpp -FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger_bindings.cpp -FILE: ../../../third_party/skia/gm/asyncrescaleandread.cpp -FILE: ../../../third_party/skia/gm/crbug_908646.cpp -FILE: ../../../third_party/skia/gm/crbug_946965.cpp -FILE: ../../../third_party/skia/gm/patharcto.cpp -FILE: ../../../third_party/skia/gm/runtimecolorfilter.cpp -FILE: ../../../third_party/skia/gm/runtimefunctions.cpp -FILE: ../../../third_party/skia/gm/runtimeshader.cpp -FILE: ../../../third_party/skia/gm/skbug_9319.cpp -FILE: ../../../third_party/skia/include/effects/SkImageFilters.h -FILE: ../../../third_party/skia/include/effects/SkRuntimeEffect.h -FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h -FILE: ../../../third_party/skia/include/private/GrGLTypesPriv.h -FILE: ../../../third_party/skia/include/private/SkThreadAnnotations.h -FILE: ../../../third_party/skia/modules/canvaskit/WasmCommon.h -FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp -FILE: ../../../third_party/skia/modules/canvaskit/particles_bindings.cpp -FILE: ../../../third_party/skia/modules/canvaskit/skottie_bindings.cpp -FILE: ../../../third_party/skia/modules/particles/include/SkParticleBinding.h -FILE: ../../../third_party/skia/modules/particles/include/SkParticleData.h -FILE: ../../../third_party/skia/modules/particles/include/SkParticleDrawable.h -FILE: ../../../third_party/skia/modules/particles/include/SkParticleEffect.h -FILE: ../../../third_party/skia/modules/particles/include/SkParticleSerialization.h -FILE: ../../../third_party/skia/modules/particles/include/SkReflected.h -FILE: ../../../third_party/skia/modules/particles/src/SkParticleBinding.cpp -FILE: ../../../third_party/skia/modules/particles/src/SkParticleDrawable.cpp -FILE: ../../../third_party/skia/modules/particles/src/SkParticleEffect.cpp -FILE: ../../../third_party/skia/modules/particles/src/SkReflected.cpp -FILE: ../../../third_party/skia/modules/skresources/include/SkResources.h -FILE: ../../../third_party/skia/modules/skresources/src/SkResources.cpp -FILE: ../../../third_party/skia/samplecode/SampleBackdropBounds.cpp -FILE: ../../../third_party/skia/samplecode/SampleImageFilterDAG.cpp -FILE: ../../../third_party/skia/src/core/SkImageFilterTypes.cpp -FILE: ../../../third_party/skia/src/core/SkImageFilterTypes.h -FILE: ../../../third_party/skia/src/core/SkImageFilter_Base.h -FILE: ../../../third_party/skia/src/core/SkRuntimeEffect.cpp -FILE: ../../../third_party/skia/src/core/SkVM.cpp -FILE: ../../../third_party/skia/src/core/SkVM.h -FILE: ../../../third_party/skia/src/effects/imagefilters/SkImageFilters.cpp -FILE: ../../../third_party/skia/src/gpu/GrClientMappedBufferManager.cpp -FILE: ../../../third_party/skia/src/gpu/GrClientMappedBufferManager.h -FILE: ../../../third_party/skia/src/gpu/GrCopyRenderTask.cpp -FILE: ../../../third_party/skia/src/gpu/GrCopyRenderTask.h -FILE: ../../../third_party/skia/src/gpu/GrImageInfo.h -FILE: ../../../third_party/skia/src/gpu/GrProgramInfo.cpp -FILE: ../../../third_party/skia/src/gpu/GrProgramInfo.h -FILE: ../../../third_party/skia/src/gpu/GrRenderTask.cpp -FILE: ../../../third_party/skia/src/gpu/GrRenderTask.h -FILE: ../../../third_party/skia/src/gpu/GrShaderUtils.cpp -FILE: ../../../third_party/skia/src/gpu/GrShaderUtils.h -FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxyView.h -FILE: ../../../third_party/skia/src/gpu/GrSwizzle.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureResolveManager.h -FILE: ../../../third_party/skia/src/gpu/GrTextureResolveRenderTask.cpp -FILE: ../../../third_party/skia/src/gpu/GrTextureResolveRenderTask.h -FILE: ../../../third_party/skia/src/gpu/GrTransferFromRenderTask.cpp -FILE: ../../../third_party/skia/src/gpu/GrTransferFromRenderTask.h -FILE: ../../../third_party/skia/src/gpu/GrUtil.h -FILE: ../../../third_party/skia/src/gpu/GrWaitRenderTask.cpp -FILE: ../../../third_party/skia/src/gpu/GrWaitRenderTask.h -FILE: ../../../third_party/skia/src/gpu/effects/GrClampFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrColorMatrixFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrHSLToRGBFilterEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrRGBToHSLFilterEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrClampFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrClampFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrHSLToRGBFilterEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrHSLToRGBFilterEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRGBToHSLFilterEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRGBToHSLFilterEffect.h -FILE: ../../../third_party/skia/src/gpu/geometry/GrQuadBuffer.h -FILE: ../../../third_party/skia/src/gpu/geometry/GrQuadUtils.cpp -FILE: ../../../third_party/skia/src/gpu/geometry/GrQuadUtils.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleGLESInterfaceAutogen.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleGLInterfaceAutogen.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleHelpers.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleWebGLInterfaceAutogen.cpp -FILE: ../../../third_party/skia/src/gpu/gl/GrGLTypesPriv.cpp -FILE: ../../../third_party/skia/src/gpu/mock/GrMockCaps.cpp -FILE: ../../../third_party/skia/src/gpu/mock/GrMockTypes.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLASTFile.h -FILE: ../../../third_party/skia/src/sksl/SkSLASTNode.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLASTNode.h -FILE: ../../../third_party/skia/src/sksl/SkSLByteCode.h -FILE: ../../../third_party/skia/src/sksl/SkSLByteCodeGenerator.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLByteCodeGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLExternalValue.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLExternalFunctionCall.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLExternalValueReference.h +FILE: ../../../third_party/skia/gm/xfermodes3.cpp +FILE: ../../../third_party/skia/include/core/SkDataTable.h +FILE: ../../../third_party/skia/include/core/SkDocument.h +FILE: ../../../third_party/skia/include/core/SkFontMgr.h +FILE: ../../../third_party/skia/include/core/SkFontStyle.h +FILE: ../../../third_party/skia/include/core/SkImageGenerator.h +FILE: ../../../third_party/skia/include/core/SkImageInfo.h +FILE: ../../../third_party/skia/include/effects/SkLumaColorFilter.h +FILE: ../../../third_party/skia/include/effects/SkPerlinNoiseShader.h +FILE: ../../../third_party/skia/include/gpu/gl/GrGLExtensions.h +FILE: ../../../third_party/skia/include/ports/SkFontConfigInterface.h +FILE: ../../../third_party/skia/include/private/GrTypesPriv.h +FILE: ../../../third_party/skia/include/private/SkOnce.h +FILE: ../../../third_party/skia/include/private/SkTFitsIn.h +FILE: ../../../third_party/skia/include/private/SkTLogic.h +FILE: ../../../third_party/skia/include/utils/SkCanvasStateUtils.h +FILE: ../../../third_party/skia/samplecode/SampleChart.cpp +FILE: ../../../third_party/skia/samplecode/SampleClock.cpp +FILE: ../../../third_party/skia/samplecode/SampleManyRects.cpp +FILE: ../../../third_party/skia/samplecode/SampleStringArt.cpp +FILE: ../../../third_party/skia/src/core/SkBitmapDevice.cpp +FILE: ../../../third_party/skia/src/core/SkBitmapDevice.h +FILE: ../../../third_party/skia/src/core/SkDataTable.cpp +FILE: ../../../third_party/skia/src/core/SkDiscardableMemory.h +FILE: ../../../third_party/skia/src/core/SkDocument.cpp +FILE: ../../../third_party/skia/src/core/SkDrawLooper.cpp +FILE: ../../../third_party/skia/src/core/SkFontStream.h +FILE: ../../../third_party/skia/src/core/SkGpuBlurUtils.cpp +FILE: ../../../third_party/skia/src/core/SkGpuBlurUtils.h +FILE: ../../../third_party/skia/src/core/SkMatrixUtils.h +FILE: ../../../third_party/skia/src/core/SkMessageBus.h +FILE: ../../../third_party/skia/src/core/SkMipmap.cpp +FILE: ../../../third_party/skia/src/core/SkMipmap.h +FILE: ../../../third_party/skia/src/core/SkPaintPriv.cpp +FILE: ../../../third_party/skia/src/core/SkPaintPriv.h +FILE: ../../../third_party/skia/src/core/SkPathRef.cpp +FILE: ../../../third_party/skia/src/core/SkResourceCache.cpp +FILE: ../../../third_party/skia/src/core/SkResourceCache.h +FILE: ../../../third_party/skia/src/core/SkStreamPriv.h +FILE: ../../../third_party/skia/src/core/SkStringUtils.cpp +FILE: ../../../third_party/skia/src/core/SkStringUtils.h +FILE: ../../../third_party/skia/src/core/SkTDynamicHash.h +FILE: ../../../third_party/skia/src/core/SkTMultiMap.h +FILE: ../../../third_party/skia/src/core/SkValidationUtils.h +FILE: ../../../third_party/skia/src/effects/SkLumaColorFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkAlphaThresholdImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkComposeImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkDropShadowImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkTileImageFilter.cpp +FILE: ../../../third_party/skia/src/gpu/GrBlend.h +FILE: ../../../third_party/skia/src/gpu/GrCaps.h +FILE: ../../../third_party/skia/src/gpu/GrGeometryProcessor.h +FILE: ../../../third_party/skia/src/gpu/GrPaint.cpp +FILE: ../../../third_party/skia/src/gpu/GrRectanizerSkyline.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrBezierEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrBezierEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/GrBicubicEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/GrBitmapTextGeoProc.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrBitmapTextGeoProc.h +FILE: ../../../third_party/skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrDistanceFieldGeoProc.h +FILE: ../../../third_party/skia/src/gpu/gl/GrGLContext.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLContext.h +FILE: ../../../third_party/skia/src/gpu/gl/GrGLExtensions.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLVertexArray.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLVertexArray.h +FILE: ../../../third_party/skia/src/gpu/ops/GrOvalOpFactory.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrOvalOpFactory.h +FILE: ../../../third_party/skia/src/lazy/SkDiscardableMemoryPool.cpp +FILE: ../../../third_party/skia/src/lazy/SkDiscardableMemoryPool.h +FILE: ../../../third_party/skia/src/pathops/SkOpCoincidence.h +FILE: ../../../third_party/skia/src/pathops/SkOpContour.cpp +FILE: ../../../third_party/skia/src/pathops/SkOpContour.h +FILE: ../../../third_party/skia/src/pathops/SkPathOpsDebug.cpp +FILE: ../../../third_party/skia/src/pathops/SkPathOpsDebug.h +FILE: ../../../third_party/skia/src/pdf/SkPDFResourceDict.cpp +FILE: ../../../third_party/skia/src/pdf/SkPDFResourceDict.h +FILE: ../../../third_party/skia/src/ports/SkDiscardableMemory_none.cpp +FILE: ../../../third_party/skia/src/ports/SkFontConfigTypeface.h +FILE: ../../../third_party/skia/src/ports/SkFontMgr_FontConfigInterface.cpp +FILE: ../../../third_party/skia/src/ports/SkOSFile_posix.cpp +FILE: ../../../third_party/skia/src/ports/SkOSFile_win.cpp +FILE: ../../../third_party/skia/src/sfnt/SkOTTable_name.cpp +FILE: ../../../third_party/skia/src/sfnt/SkTTCFHeader.h +FILE: ../../../third_party/skia/src/shaders/SkColorFilterShader.cpp +FILE: ../../../third_party/skia/src/shaders/SkPerlinNoiseShader.cpp +FILE: ../../../third_party/skia/src/utils/SkCanvasStack.cpp +FILE: ../../../third_party/skia/src/utils/SkCanvasStack.h +FILE: ../../../third_party/skia/src/utils/SkCanvasStateUtils.cpp ---------------------------------------------------------------------------------------------------- -Copyright 2019 Google LLC +Copyright 2013 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2997,197 +2808,238 @@ met: 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: skia -ORIGIN: ../../../third_party/skia/bench/CTConvertBench.cpp + ../../../third_party/skia/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/bench/CTConvertBench.cpp -FILE: ../../../third_party/skia/bench/SkVMBench.cpp -FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.cpp -FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.h -FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoEncoder.cpp -FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoEncoder.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGText.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGText.h -FILE: ../../../third_party/skia/gm/backdrop.cpp -FILE: ../../../third_party/skia/gm/backdrop_imagefilter_croprect.cpp -FILE: ../../../third_party/skia/gm/bug9331.cpp -FILE: ../../../third_party/skia/gm/clip_sierpinski_region.cpp -FILE: ../../../third_party/skia/gm/collapsepaths.cpp -FILE: ../../../third_party/skia/gm/compositor_quads.cpp -FILE: ../../../third_party/skia/gm/crbug_913349.cpp -FILE: ../../../third_party/skia/gm/crbug_938592.cpp -FILE: ../../../third_party/skia/gm/crbug_947055.cpp -FILE: ../../../third_party/skia/gm/crbug_996140.cpp -FILE: ../../../third_party/skia/gm/ducky_yuv_blend.cpp -FILE: ../../../third_party/skia/gm/fiddle.cpp -FILE: ../../../third_party/skia/gm/mac_aa_explorer.cpp -FILE: ../../../third_party/skia/gm/mixercolorfilter.cpp -FILE: ../../../third_party/skia/gm/overdrawcanvas.cpp -FILE: ../../../third_party/skia/gm/postercircle.cpp -FILE: ../../../third_party/skia/gm/samplelocations.cpp -FILE: ../../../third_party/skia/gm/skbug_8664.cpp -FILE: ../../../third_party/skia/gm/skbug_8955.cpp -FILE: ../../../third_party/skia/gm/tessellation.cpp -FILE: ../../../third_party/skia/gm/video_decoder.cpp -FILE: ../../../third_party/skia/gm/yuv420_odd_dim.cpp -FILE: ../../../third_party/skia/include/core/SkTileMode.h -FILE: ../../../third_party/skia/include/gpu/GrContextThreadSafeProxy.h -FILE: ../../../third_party/skia/include/gpu/GrRecordingContext.h -FILE: ../../../third_party/skia/include/gpu/dawn/GrDawnTypes.h -FILE: ../../../third_party/skia/include/ports/SkCFObject.h -FILE: ../../../third_party/skia/include/private/GrContext_Base.h -FILE: ../../../third_party/skia/include/private/GrImageContext.h -FILE: ../../../third_party/skia/include/private/SkVx.h -FILE: ../../../third_party/skia/modules/skottie/src/Composition.cpp -FILE: ../../../third_party/skia/modules/skottie/src/Composition.h -FILE: ../../../third_party/skia/modules/skottie/src/Layer.cpp -FILE: ../../../third_party/skia/modules/skottie/src/Layer.h -FILE: ../../../third_party/skia/modules/skottie/src/effects/DropShadowEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/Effects.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/Effects.h -FILE: ../../../third_party/skia/modules/skottie/src/effects/FillEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/GaussianBlurEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/GradientEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/HueSaturationEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/InvertEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/LevelsEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/LinearWipeEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionBlurEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionBlurEffect.h -FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/RadialWipeEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/ShiftChannelsEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/TintEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/TransformEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/TritoneEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/effects/VenetianBlindsEffect.cpp -FILE: ../../../third_party/skia/modules/skottie/src/layers/FootageLayer.cpp -FILE: ../../../third_party/skia/modules/skottie/src/layers/NullLayer.cpp -FILE: ../../../third_party/skia/modules/skottie/src/layers/SolidLayer.cpp -FILE: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.cpp -FILE: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.h -FILE: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.cpp -FILE: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.h -FILE: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.cpp -FILE: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.h -FILE: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.cpp -FILE: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.h -FILE: ../../../third_party/skia/modules/skottie/src/text/TextValue.cpp -FILE: ../../../third_party/skia/modules/skottie/src/text/TextValue.h -FILE: ../../../third_party/skia/modules/sksg/include/SkSGRenderEffect.h -FILE: ../../../third_party/skia/modules/sksg/src/SkSGNodePriv.h -FILE: ../../../third_party/skia/modules/sksg/src/SkSGRenderEffect.cpp -FILE: ../../../third_party/skia/modules/sksg/src/SkSGTransformPriv.h -FILE: ../../../third_party/skia/samplecode/SampleDegenerateQuads.cpp -FILE: ../../../third_party/skia/samplecode/SampleSG.cpp -FILE: ../../../third_party/skia/samplecode/SampleThinAA.cpp -FILE: ../../../third_party/skia/src/codec/SkScalingCodec.h -FILE: ../../../third_party/skia/src/core/SkDescriptor.cpp -FILE: ../../../third_party/skia/src/core/SkDraw_atlas.cpp -FILE: ../../../third_party/skia/src/core/SkEffectPriv.h -FILE: ../../../third_party/skia/src/core/SkEnumerate.h -FILE: ../../../third_party/skia/src/core/SkGlyphBuffer.cpp -FILE: ../../../third_party/skia/src/core/SkGlyphBuffer.h -FILE: ../../../third_party/skia/src/core/SkPathMakers.h -FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.cpp -FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.h -FILE: ../../../third_party/skia/src/core/SkStrikeSpec.h -FILE: ../../../third_party/skia/src/core/SkVMBlitter.cpp -FILE: ../../../third_party/skia/src/core/SkYUVMath.cpp -FILE: ../../../third_party/skia/src/core/SkYUVMath.h -FILE: ../../../third_party/skia/src/core/SkZip.h -FILE: ../../../third_party/skia/src/gpu/GrAHardwareBufferUtils.cpp -FILE: ../../../third_party/skia/src/gpu/GrAHardwareBufferUtils.h -FILE: ../../../third_party/skia/src/gpu/GrBaseContextPriv.h -FILE: ../../../third_party/skia/src/gpu/GrBuffer.h -FILE: ../../../third_party/skia/src/gpu/GrContextPriv.cpp -FILE: ../../../third_party/skia/src/gpu/GrContextThreadSafeProxy.cpp -FILE: ../../../third_party/skia/src/gpu/GrContext_Base.cpp -FILE: ../../../third_party/skia/src/gpu/GrCpuBuffer.h -FILE: ../../../third_party/skia/src/gpu/GrDataUtils.cpp -FILE: ../../../third_party/skia/src/gpu/GrDataUtils.h -FILE: ../../../third_party/skia/src/gpu/GrGpuBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/GrGpuBuffer.h -FILE: ../../../third_party/skia/src/gpu/GrImageContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrImageContextPriv.h -FILE: ../../../third_party/skia/src/gpu/GrOpsTask.cpp -FILE: ../../../third_party/skia/src/gpu/GrOpsTask.h -FILE: ../../../third_party/skia/src/gpu/GrRecordingContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrRecordingContextPriv.h -FILE: ../../../third_party/skia/src/gpu/GrSPIRVUniformHandler.cpp -FILE: ../../../third_party/skia/src/gpu/GrSPIRVUniformHandler.h -FILE: ../../../third_party/skia/src/gpu/GrSPIRVVaryingHandler.cpp -FILE: ../../../third_party/skia/src/gpu/GrSPIRVVaryingHandler.h -FILE: ../../../third_party/skia/src/gpu/GrSamplePatternDictionary.cpp -FILE: ../../../third_party/skia/src/gpu/GrSamplePatternDictionary.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrGSCoverageProcessor.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrOctoBounds.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrOctoBounds.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrStencilAtlasOp.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrVSCoverageProcessor.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnBuffer.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnCaps.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnCaps.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnGpu.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnGpu.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnOpsRenderPass.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramBuilder.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramBuilder.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramDataManager.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramDataManager.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRenderTarget.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRingBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRingBuffer.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStencilAttachment.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStencilAttachment.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTexture.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTexture.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTextureRenderTarget.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTextureRenderTarget.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnUtil.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnUtil.h -FILE: ../../../third_party/skia/src/gpu/effects/GrMixerEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrOverrideInputFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMixerEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMixerEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCommandBuffer.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCommandBuffer.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlDepthStencil.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSemaphore.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSemaphore.mm -FILE: ../../../third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.h -FILE: ../../../third_party/skia/src/image/SkSurface_GpuMtl.mm -FILE: ../../../third_party/skia/src/sksl/SkSLDefines.h -FILE: ../../../third_party/skia/src/sksl/SkSLOutputStream.cpp -FILE: ../../../third_party/skia/src/utils/SkCharToGlyphCache.cpp -FILE: ../../../third_party/skia/src/utils/SkCharToGlyphCache.h -FILE: ../../../third_party/skia/src/utils/SkClipStackUtils.cpp -FILE: ../../../third_party/skia/src/utils/SkClipStackUtils.h -FILE: ../../../third_party/skia/src/utils/SkShaperJSONWriter.cpp -FILE: ../../../third_party/skia/src/utils/SkShaperJSONWriter.h -FILE: ../../../third_party/skia/src/utils/win/SkObjBase.h +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: skia +ORIGIN: ../../../third_party/skia/bench/BulkRectBench.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/bench/BulkRectBench.cpp +FILE: ../../../third_party/skia/bench/DDLRecorderBench.cpp +FILE: ../../../third_party/skia/bench/SkSLBench.cpp +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger_bindings.cpp +FILE: ../../../third_party/skia/gm/asyncrescaleandread.cpp +FILE: ../../../third_party/skia/gm/crbug_908646.cpp +FILE: ../../../third_party/skia/gm/crbug_946965.cpp +FILE: ../../../third_party/skia/gm/patharcto.cpp +FILE: ../../../third_party/skia/gm/runtimecolorfilter.cpp +FILE: ../../../third_party/skia/gm/runtimefunctions.cpp +FILE: ../../../third_party/skia/gm/runtimeintrinsics.cpp +FILE: ../../../third_party/skia/gm/runtimeshader.cpp +FILE: ../../../third_party/skia/gm/skbug_9319.cpp +FILE: ../../../third_party/skia/include/effects/SkImageFilters.h +FILE: ../../../third_party/skia/include/effects/SkRuntimeEffect.h +FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h +FILE: ../../../third_party/skia/include/private/GrGLTypesPriv.h +FILE: ../../../third_party/skia/include/private/SkThreadAnnotations.h +FILE: ../../../third_party/skia/modules/canvaskit/WasmCommon.h +FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp +FILE: ../../../third_party/skia/modules/canvaskit/particles_bindings.cpp +FILE: ../../../third_party/skia/modules/canvaskit/skottie_bindings.cpp +FILE: ../../../third_party/skia/modules/particles/include/SkParticleBinding.h +FILE: ../../../third_party/skia/modules/particles/include/SkParticleData.h +FILE: ../../../third_party/skia/modules/particles/include/SkParticleDrawable.h +FILE: ../../../third_party/skia/modules/particles/include/SkParticleEffect.h +FILE: ../../../third_party/skia/modules/particles/include/SkParticleSerialization.h +FILE: ../../../third_party/skia/modules/particles/include/SkReflected.h +FILE: ../../../third_party/skia/modules/particles/src/SkParticleBinding.cpp +FILE: ../../../third_party/skia/modules/particles/src/SkParticleDrawable.cpp +FILE: ../../../third_party/skia/modules/particles/src/SkParticleEffect.cpp +FILE: ../../../third_party/skia/modules/particles/src/SkReflected.cpp +FILE: ../../../third_party/skia/modules/skresources/include/SkResources.h +FILE: ../../../third_party/skia/modules/skresources/src/SkResources.cpp +FILE: ../../../third_party/skia/samplecode/SampleFilterBounds.cpp +FILE: ../../../third_party/skia/samplecode/SampleImageFilterDAG.cpp +FILE: ../../../third_party/skia/src/core/SkImageFilterTypes.cpp +FILE: ../../../third_party/skia/src/core/SkImageFilterTypes.h +FILE: ../../../third_party/skia/src/core/SkImageFilter_Base.h +FILE: ../../../third_party/skia/src/core/SkRuntimeEffect.cpp +FILE: ../../../third_party/skia/src/core/SkVM.cpp +FILE: ../../../third_party/skia/src/core/SkVM.h +FILE: ../../../third_party/skia/src/gpu/GrClientMappedBufferManager.cpp +FILE: ../../../third_party/skia/src/gpu/GrClientMappedBufferManager.h +FILE: ../../../third_party/skia/src/gpu/GrCopyRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrCopyRenderTask.h +FILE: ../../../third_party/skia/src/gpu/GrImageInfo.h +FILE: ../../../third_party/skia/src/gpu/GrPersistentCacheUtils.h +FILE: ../../../third_party/skia/src/gpu/GrProgramInfo.cpp +FILE: ../../../third_party/skia/src/gpu/GrProgramInfo.h +FILE: ../../../third_party/skia/src/gpu/GrRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrRenderTask.h +FILE: ../../../third_party/skia/src/gpu/GrShaderUtils.cpp +FILE: ../../../third_party/skia/src/gpu/GrShaderUtils.h +FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxyView.h +FILE: ../../../third_party/skia/src/gpu/GrSwizzle.cpp +FILE: ../../../third_party/skia/src/gpu/GrTextureResolveManager.h +FILE: ../../../third_party/skia/src/gpu/GrTextureResolveRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrTextureResolveRenderTask.h +FILE: ../../../third_party/skia/src/gpu/GrTransferFromRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrTransferFromRenderTask.h +FILE: ../../../third_party/skia/src/gpu/GrUtil.h +FILE: ../../../third_party/skia/src/gpu/GrWaitRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrWaitRenderTask.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrQuadBuffer.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrQuadUtils.cpp +FILE: ../../../third_party/skia/src/gpu/geometry/GrQuadUtils.h +FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleGLESInterfaceAutogen.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleGLInterfaceAutogen.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleHelpers.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLAssembleWebGLInterfaceAutogen.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLTypesPriv.cpp +FILE: ../../../third_party/skia/src/gpu/mock/GrMockCaps.cpp +FILE: ../../../third_party/skia/src/gpu/mock/GrMockTypes.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLASTFile.h +FILE: ../../../third_party/skia/src/sksl/SkSLASTNode.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLASTNode.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExternalFunction.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExternalFunctionCall.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExternalFunctionReference.h ---------------------------------------------------------------------------------------------------- -Copyright 2019 Google Inc. +Copyright 2019 Google LLC + +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 the copyright holder 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: skia +ORIGIN: ../../../third_party/skia/bench/CanvasSaveRestoreBench.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/bench/CanvasSaveRestoreBench.cpp +FILE: ../../../third_party/skia/bench/TriangulatorBench.cpp +FILE: ../../../third_party/skia/experimental/tskit/bindings/bindings.h +FILE: ../../../third_party/skia/experimental/tskit/bindings/core.cpp +FILE: ../../../third_party/skia/experimental/tskit/bindings/extension.cpp +FILE: ../../../third_party/skia/gm/composecolorfilter.cpp +FILE: ../../../third_party/skia/gm/crbug_1167277.cpp +FILE: ../../../third_party/skia/gm/crbug_1174186.cpp +FILE: ../../../third_party/skia/gm/crbug_1174354.cpp +FILE: ../../../third_party/skia/gm/crbug_1177833.cpp +FILE: ../../../third_party/skia/gm/lazytiling.cpp +FILE: ../../../third_party/skia/gm/particles.cpp +FILE: ../../../third_party/skia/gm/runtimeeffectimage.cpp +FILE: ../../../third_party/skia/include/core/SkBlender.h +FILE: ../../../third_party/skia/include/effects/SkBlenders.h +FILE: ../../../third_party/skia/include/gpu/gl/egl/GrGLMakeEGLInterface.h +FILE: ../../../third_party/skia/include/gpu/gl/glx/GrGLMakeGLXInterface.h +FILE: ../../../third_party/skia/include/sksl/DSLSymbols.h +FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings_gen.cpp +FILE: ../../../third_party/skia/src/core/SkBlendModeBlender.cpp +FILE: ../../../third_party/skia/src/core/SkBlendModeBlender.h +FILE: ../../../third_party/skia/src/core/SkBlenderBase.h +FILE: ../../../third_party/skia/src/core/SkMatrixInvert.cpp +FILE: ../../../third_party/skia/src/core/SkMatrixInvert.h +FILE: ../../../third_party/skia/src/core/SkVMBlitter.h +FILE: ../../../third_party/skia/src/core/SkYUVAInfoLocation.h +FILE: ../../../third_party/skia/src/effects/SkBlenders.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkRuntimeImageFilter.h +FILE: ../../../third_party/skia/src/gpu/BaseDevice.cpp +FILE: ../../../third_party/skia/src/gpu/BaseDevice.h +FILE: ../../../third_party/skia/src/gpu/GrDstProxyView.h +FILE: ../../../third_party/skia/src/gpu/GrEagerVertexAllocator.cpp +FILE: ../../../third_party/skia/src/gpu/GrMeshDrawTarget.cpp +FILE: ../../../third_party/skia/src/gpu/GrMeshDrawTarget.h +FILE: ../../../third_party/skia/src/gpu/GrOpsTypes.h +FILE: ../../../third_party/skia/src/gpu/GrPersistentCacheUtils.cpp +FILE: ../../../third_party/skia/src/gpu/GrSubRunAllocator.cpp +FILE: ../../../third_party/skia/src/gpu/GrSubRunAllocator.h +FILE: ../../../third_party/skia/src/gpu/GrWritePixelsRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrWritePixelsRenderTask.h +FILE: ../../../third_party/skia/src/gpu/GrYUVATextureProxies.cpp +FILE: ../../../third_party/skia/src/gpu/GrYUVATextureProxies.h +FILE: ../../../third_party/skia/src/gpu/SurfaceFillContext.cpp +FILE: ../../../third_party/skia/src/gpu/SurfaceFillContext.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrInnerFanTriangulator.h +FILE: ../../../third_party/skia/src/gpu/gl/egl/GrGLMakeNativeInterface_egl.cpp +FILE: ../../../third_party/skia/src/gpu/gl/glx/GrGLMakeNativeInterface_glx.cpp +FILE: ../../../third_party/skia/src/gpu/v2/Device.cpp +FILE: ../../../third_party/skia/src/gpu/v2/Device_v2.h +FILE: ../../../third_party/skia/src/gpu/v2/SurfaceDrawContext.cpp +FILE: ../../../third_party/skia/src/gpu/v2/SurfaceDrawContext_v2.h +FILE: ../../../third_party/skia/src/gpu/v2/SurfaceFillContext_v2.cpp +FILE: ../../../third_party/skia/src/gpu/v2/SurfaceFillContext_v2.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkBuffer.h +FILE: ../../../third_party/skia/src/sksl/SkSLBuiltinTypes.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLBuiltinTypes.h +FILE: ../../../third_party/skia/src/sksl/SkSLContext.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLIntrinsicList.h +FILE: ../../../third_party/skia/src/sksl/SkSLMangler.h +FILE: ../../../third_party/skia/src/sksl/SkSLOperators.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLOperators.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLBinaryExpression.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLBlock.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLChildCall.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLChildCall.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorArray.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorArray.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorArrayCast.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorArrayCast.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorCompound.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorCompound.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorCompoundCast.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorCompoundCast.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorDiagonalMatrix.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorDiagonalMatrix.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorMatrixResize.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorMatrixResize.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorScalarCast.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorScalarCast.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorSplat.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorSplat.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorStruct.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructorStruct.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLDoStatement.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLExpressionStatement.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFieldAccess.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLForStatement.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionCall.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLIfStatement.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLIndexExpression.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLPostfixExpression.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSwitchStatement.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSwizzle.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLTernaryExpression.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLVarDeclarations.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2021 Google LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -3281,7 +3133,7 @@ FILE: ../../../third_party/skia/include/core/SkCubicMap.h FILE: ../../../third_party/skia/include/core/SkFontMetrics.h FILE: ../../../third_party/skia/include/core/SkFontParameters.h FILE: ../../../third_party/skia/include/core/SkFontTypes.h -FILE: ../../../third_party/skia/include/core/SkYUVAIndex.h +FILE: ../../../third_party/skia/include/core/SkSpan.h FILE: ../../../third_party/skia/include/effects/SkOpPathEffect.h FILE: ../../../third_party/skia/include/effects/SkShaderMaskFilter.h FILE: ../../../third_party/skia/include/effects/SkTrimPathEffect.h @@ -3337,7 +3189,6 @@ FILE: ../../../third_party/skia/src/codec/SkEncodedInfo.cpp FILE: ../../../third_party/skia/src/codec/SkParseEncodedOrigin.cpp FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.cpp FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.h -FILE: ../../../third_party/skia/src/core/SkBlurPriv.h FILE: ../../../third_party/skia/src/core/SkCanvasPriv.cpp FILE: ../../../third_party/skia/src/core/SkColorSpaceXformSteps.cpp FILE: ../../../third_party/skia/src/core/SkColorSpaceXformSteps.h @@ -3358,13 +3209,11 @@ FILE: ../../../third_party/skia/src/core/SkRectPriv.h FILE: ../../../third_party/skia/src/core/SkRemoteGlyphCache.cpp FILE: ../../../third_party/skia/src/core/SkRemoteGlyphCache.h FILE: ../../../third_party/skia/src/core/SkSafeRange.h -FILE: ../../../third_party/skia/src/core/SkSpan.h FILE: ../../../third_party/skia/src/core/SkStrikeCache.cpp FILE: ../../../third_party/skia/src/core/SkSurfaceCharacterization.cpp FILE: ../../../third_party/skia/src/core/SkTextBlobPriv.h FILE: ../../../third_party/skia/src/core/SkTypeface_remote.cpp FILE: ../../../third_party/skia/src/core/SkTypeface_remote.h -FILE: ../../../third_party/skia/src/core/SkYUVASizeInfo.cpp FILE: ../../../third_party/skia/src/effects/SkOpPE.h FILE: ../../../third_party/skia/src/effects/SkOpPathEffect.cpp FILE: ../../../third_party/skia/src/effects/SkShaderMaskFilter.cpp @@ -3378,85 +3227,17 @@ FILE: ../../../third_party/skia/src/gpu/GrFPArgs.h FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.cpp FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.h FILE: ../../../third_party/skia/src/gpu/GrResourceProviderPriv.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCDrawPathsOp.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCDrawPathsOp.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathCache.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathCache.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerFlushResources.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerFlushResources.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPerOpsTaskPaths.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCSTLList.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStrokeGeometry.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStrokeGeometry.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStroker.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCStroker.h -FILE: ../../../third_party/skia/src/gpu/effects/GrAARectEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrCircleBlurFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrConfigConversionEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrConstColorProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrLumaColorFilterEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrMagnifierEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrRRectBlurEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrRectBlurEffect.fp FILE: ../../../third_party/skia/src/gpu/effects/GrSkSLFP.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrSkSLFP.h FILE: ../../../third_party/skia/src/gpu/effects/GrYUVtoRGBEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrYUVtoRGBEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAARectEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAARectEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConfigConversionEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConfigConversionEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConstColorProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrConstColorProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrLumaColorFilterEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMagnifierEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMagnifierEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRRectBlurEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRRectBlurEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRectBlurEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrRectBlurEffect.h FILE: ../../../third_party/skia/src/gpu/geometry/GrQuad.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/GrClampedGradientEffect.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrDualIntervalGradientColorizer.fp FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientBitmapCache.cpp FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientBitmapCache.h FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientShader.cpp FILE: ../../../third_party/skia/src/gpu/gradients/GrGradientShader.h -FILE: ../../../third_party/skia/src/gpu/gradients/GrLinearGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrRadialGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrSingleIntervalGradientColorizer.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrSweepGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrTiledGradientEffect.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrTwoPointConicalGradientLayout.fp -FILE: ../../../third_party/skia/src/gpu/gradients/GrUnrolledBinaryGradientColorizer.fp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrClampedGradientEffect.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrClampedGradientEffect.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrLinearGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrLinearGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrRadialGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrRadialGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSweepGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrSweepGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTiledGradientEffect.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTiledGradientEffect.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp -FILE: ../../../third_party/skia/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlAttachment.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlAttachment.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlBuffer.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlBuffer.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCppUtil.h @@ -3473,8 +3254,6 @@ FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlResourceProvider.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlResourceProvider.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSampler.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSampler.mm -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlStencilAttachment.h -FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlStencilAttachment.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlTextureRenderTarget.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlTextureRenderTarget.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUniformHandler.h @@ -3491,8 +3270,6 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrQuadPerEdgeAA.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrQuadPerEdgeAA.h FILE: ../../../third_party/skia/src/gpu/ops/GrStrokeRectOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrStrokeRectOp.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokePatchBuilder.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokePatchBuilder.h FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.cpp FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.h FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.cpp @@ -3520,10 +3297,7 @@ FILE: ../../../third_party/skia/src/pdf/SkClusterator.h FILE: ../../../third_party/skia/src/pdf/SkPDFTag.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFTag.h FILE: ../../../third_party/skia/src/ports/SkFontMgr_fuchsia.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLByteCode.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.h -FILE: ../../../third_party/skia/src/sksl/SkSLPipelineStageCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariableReference.cpp FILE: ../../../third_party/skia/src/utils/SkAnimCodecPlayer.cpp FILE: ../../../third_party/skia/src/utils/SkCallableTraits.h @@ -3570,22 +3344,11 @@ ORIGIN: ../../../third_party/skia/bench/ClipMaskBench.cpp + ../../../third_party TYPE: LicenseType.bsd FILE: ../../../third_party/skia/bench/ClipMaskBench.cpp FILE: ../../../third_party/skia/bench/ClipStrategyBench.cpp -FILE: ../../../third_party/skia/bench/CubicKLMBench.cpp FILE: ../../../third_party/skia/bench/PathTextBench.cpp FILE: ../../../third_party/skia/bench/ShadowBench.cpp -FILE: ../../../third_party/skia/bin/gerrit-number -FILE: ../../../third_party/skia/bin/sysopen FILE: ../../../third_party/skia/dm/DMGpuTestProcs.cpp FILE: ../../../third_party/skia/example/HelloWorld.cpp FILE: ../../../third_party/skia/example/HelloWorld.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGGradient.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGGradient.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGPattern.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGPattern.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGRadialGradient.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGRadialGradient.h -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGUse.cpp -FILE: ../../../third_party/skia/experimental/svg/model/SkSVGUse.h FILE: ../../../third_party/skia/fuzz/FuzzCanvas.cpp FILE: ../../../third_party/skia/gm/alpha_image.cpp FILE: ../../../third_party/skia/gm/bitmaptiled.cpp @@ -3640,9 +3403,9 @@ FILE: ../../../third_party/skia/include/gpu/GrBackendSurface.h FILE: ../../../third_party/skia/include/gpu/mock/GrMockTypes.h FILE: ../../../third_party/skia/include/gpu/mtl/GrMtlTypes.h FILE: ../../../third_party/skia/include/ports/SkFontMgr_mac_ct.h -FILE: ../../../third_party/skia/include/private/GrSharedEnums.h FILE: ../../../third_party/skia/include/private/SkImageInfoPriv.h FILE: ../../../third_party/skia/include/private/SkMalloc.h +FILE: ../../../third_party/skia/include/private/SkSLString.h FILE: ../../../third_party/skia/include/private/SkShadowFlags.h FILE: ../../../third_party/skia/include/utils/SkShadowUtils.h FILE: ../../../third_party/skia/modules/skottie/gm/ExternalProperties.cpp @@ -3674,7 +3437,14 @@ FILE: ../../../third_party/skia/modules/sksg/src/SkSGPath.cpp FILE: ../../../third_party/skia/modules/sksg/src/SkSGRect.cpp FILE: ../../../third_party/skia/modules/sksg/src/SkSGRenderNode.cpp FILE: ../../../third_party/skia/modules/sksg/src/SkSGTransform.cpp -FILE: ../../../third_party/skia/samplecode/SampleCCPRGeometry.cpp +FILE: ../../../third_party/skia/modules/svg/include/SkSVGGradient.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGPattern.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGRadialGradient.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGUse.h +FILE: ../../../third_party/skia/modules/svg/src/SkSVGGradient.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGPattern.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGRadialGradient.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGUse.cpp FILE: ../../../third_party/skia/samplecode/SampleChineseFling.cpp FILE: ../../../third_party/skia/samplecode/SampleCowboy.cpp FILE: ../../../third_party/skia/samplecode/SampleMixer.cpp @@ -3682,6 +3452,7 @@ FILE: ../../../third_party/skia/samplecode/SamplePathText.cpp FILE: ../../../third_party/skia/samplecode/SampleShadowColor.cpp FILE: ../../../third_party/skia/samplecode/SampleShadowReference.cpp FILE: ../../../third_party/skia/samplecode/SampleShadowUtils.cpp +FILE: ../../../third_party/skia/samplecode/SampleStrokeVerb.cpp FILE: ../../../third_party/skia/src/android/SkAndroidFrameworkUtils.cpp FILE: ../../../third_party/skia/src/c/sk_effects.cpp FILE: ../../../third_party/skia/src/codec/SkBmpBaseCodec.cpp @@ -3739,49 +3510,20 @@ FILE: ../../../third_party/skia/src/gpu/GrSurfaceProxyPriv.h FILE: ../../../third_party/skia/src/gpu/GrTextureProxyCacheAccess.h FILE: ../../../third_party/skia/src/gpu/GrTextureProxyPriv.h FILE: ../../../third_party/skia/src/gpu/SkGr.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCAtlas.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCAtlas.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipProcessor.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCCoverageProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCCoverageProcessor.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCCubicShader.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCCubicShader.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCFillGeometry.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCFillGeometry.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCFiller.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCFiller.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCPathProcessor.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCQuadraticShader.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCQuadraticShader.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCoverageCountingPathRenderer.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrGSCoverageProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrVSCoverageProcessor.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrAtlasedShaderHelpers.h -FILE: ../../../third_party/skia/src/gpu/effects/GrBlurredEdgeFragmentProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrCircleEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrEllipseEffect.fp FILE: ../../../third_party/skia/src/gpu/effects/GrTextureEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrTextureEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrCircleEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrEllipseEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrEllipseEffect.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLOpsRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLSemaphore.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLSemaphore.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLVertexGeoBuilder.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLVertexGeoBuilder.h +FILE: ../../../third_party/skia/src/gpu/mock/GrMockAttachment.h FILE: ../../../third_party/skia/src/gpu/mock/GrMockBuffer.h FILE: ../../../third_party/skia/src/gpu/mock/GrMockCaps.h FILE: ../../../third_party/skia/src/gpu/mock/GrMockGpu.cpp FILE: ../../../third_party/skia/src/gpu/mock/GrMockGpu.h FILE: ../../../third_party/skia/src/gpu/mock/GrMockOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/mock/GrMockStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/mock/GrMockTexture.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCaps.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCaps.mm @@ -3798,7 +3540,6 @@ FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUtil.mm FILE: ../../../third_party/skia/src/gpu/ops/GrClearOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelper.h -FILE: ../../../third_party/skia/src/gpu/ops/GrStencilPathOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrTextureOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrTextureOp.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkSemaphore.cpp @@ -3815,19 +3556,13 @@ FILE: ../../../third_party/skia/src/ports/SkFontMgr_mac_ct_factory.cpp FILE: ../../../third_party/skia/src/ports/SkOSFile_ios.h FILE: ../../../third_party/skia/src/sfnt/SkOTTable_fvar.h FILE: ../../../third_party/skia/src/shaders/SkShaderBase.h -FILE: ../../../third_party/skia/src/sksl/SkSLCPP.h -FILE: ../../../third_party/skia/src/sksl/SkSLCPPCodeGenerator.h FILE: ../../../third_party/skia/src/sksl/SkSLFileOutputStream.h -FILE: ../../../third_party/skia/src/sksl/SkSLHCodeGenerator.h FILE: ../../../third_party/skia/src/sksl/SkSLLexer.cpp FILE: ../../../third_party/skia/src/sksl/SkSLLexer.h FILE: ../../../third_party/skia/src/sksl/SkSLOutputStream.h -FILE: ../../../third_party/skia/src/sksl/SkSLPipelineStageCodeGenerator.h -FILE: ../../../third_party/skia/src/sksl/SkSLSectionAndParameterHelper.h FILE: ../../../third_party/skia/src/sksl/SkSLString.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLString.h FILE: ../../../third_party/skia/src/sksl/SkSLStringStream.h -FILE: ../../../third_party/skia/src/sksl/ir/SkSLEnum.h +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h FILE: ../../../third_party/skia/src/sksl/ir/SkSLSetting.cpp FILE: ../../../third_party/skia/src/sksl/ir/SkSLSwitchCase.h FILE: ../../../third_party/skia/src/sksl/ir/SkSLSwitchStatement.h @@ -3894,7 +3629,11 @@ FILE: ../../../third_party/skia/bench/CreateBackendTextureBench.cpp FILE: ../../../third_party/skia/bench/GrQuadBench.cpp FILE: ../../../third_party/skia/bench/SkSLBench.h FILE: ../../../third_party/skia/experimental/skottiekit/skottiekit_bindings.cpp +FILE: ../../../third_party/skia/gm/animated_image_orientation.cpp FILE: ../../../third_party/skia/gm/crbug_1041204.cpp +FILE: ../../../third_party/skia/gm/crbug_1139750.cpp +FILE: ../../../third_party/skia/gm/crbug_1156804.cpp +FILE: ../../../third_party/skia/gm/crbug_1162942.cpp FILE: ../../../third_party/skia/gm/crbug_224618.cpp FILE: ../../../third_party/skia/gm/encode_color_types.cpp FILE: ../../../third_party/skia/gm/userfont.cpp @@ -3902,6 +3641,7 @@ FILE: ../../../third_party/skia/gm/ycbcrimage.cpp FILE: ../../../third_party/skia/include/core/SkYUVAInfo.h FILE: ../../../third_party/skia/include/core/SkYUVAPixmaps.h FILE: ../../../third_party/skia/include/gpu/GrBackendSurfaceMutableState.h +FILE: ../../../third_party/skia/include/gpu/GrYUVABackendTextures.h FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DBackendContext.h FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DTypes.h FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DTypesMinimal.h @@ -3909,10 +3649,19 @@ FILE: ../../../third_party/skia/include/ports/SkImageGeneratorNDK.h FILE: ../../../third_party/skia/include/private/GrD3DTypesPriv.h FILE: ../../../third_party/skia/include/private/SkIDChangeListener.h FILE: ../../../third_party/skia/include/private/SkSLSampleUsage.h +FILE: ../../../third_party/skia/include/sksl/DSL.h +FILE: ../../../third_party/skia/include/sksl/DSLCore.h +FILE: ../../../third_party/skia/include/sksl/DSLExpression.h +FILE: ../../../third_party/skia/include/sksl/DSLModifiers.h +FILE: ../../../third_party/skia/include/sksl/DSLType.h +FILE: ../../../third_party/skia/include/sksl/DSLVar.h FILE: ../../../third_party/skia/include/utils/SkCustomTypeface.h +FILE: ../../../third_party/skia/modules/canvaskit/gm_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/viewer_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/wasm_tools/SIMD/simd_float_capabilities.cpp FILE: ../../../third_party/skia/modules/canvaskit/wasm_tools/SIMD/simd_int_capabilities.cpp +FILE: ../../../third_party/skia/samplecode/SampleMaterialShadows.cpp +FILE: ../../../third_party/skia/src/core/SkASAN.h FILE: ../../../third_party/skia/src/core/SkIDChangeListener.cpp FILE: ../../../third_party/skia/src/core/SkMatrixProvider.h FILE: ../../../third_party/skia/src/core/SkRuntimeEffectPriv.h @@ -3922,12 +3671,18 @@ FILE: ../../../third_party/skia/src/core/SkYUVAPixmaps.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendSemaphore.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendSurfaceMutableState.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendSurfaceMutableStateImpl.h +FILE: ../../../third_party/skia/src/gpu/GrBackendUtils.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendUtils.h FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.h FILE: ../../../third_party/skia/src/gpu/GrClipStack.cpp FILE: ../../../third_party/skia/src/gpu/GrClipStack.h +FILE: ../../../third_party/skia/src/gpu/GrDDLTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrDDLTask.h FILE: ../../../third_party/skia/src/gpu/GrManagedResource.cpp +FILE: ../../../third_party/skia/src/gpu/GrPixmap.h +FILE: ../../../third_party/skia/src/gpu/GrRefCnt.h +FILE: ../../../third_party/skia/src/gpu/GrRenderTargetContext.h FILE: ../../../third_party/skia/src/gpu/GrRingBuffer.cpp FILE: ../../../third_party/skia/src/gpu/GrRingBuffer.h FILE: ../../../third_party/skia/src/gpu/GrStagingBufferManager.cpp @@ -3936,10 +3691,12 @@ FILE: ../../../third_party/skia/src/gpu/GrStencilMaskHelper.cpp FILE: ../../../third_party/skia/src/gpu/GrStencilMaskHelper.h FILE: ../../../third_party/skia/src/gpu/GrUniformDataManager.cpp FILE: ../../../third_party/skia/src/gpu/GrUniformDataManager.h -FILE: ../../../third_party/skia/src/gpu/GrUnrefDDLTask.h FILE: ../../../third_party/skia/src/gpu/GrUtil.cpp +FILE: ../../../third_party/skia/src/gpu/GrYUVABackendTextures.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DAMDMemoryAllocator.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DAMDMemoryAllocator.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DAttachment.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DAttachment.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DBuffer.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DBuffer.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.cpp @@ -3958,6 +3715,7 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipeline.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineState.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineState.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineStateBuilder.cpp @@ -3972,8 +3730,6 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRootSignature.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRootSignature.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DSemaphore.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DSemaphore.h -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DStencilAttachment.cpp -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureRenderTarget.cpp @@ -3983,15 +3739,16 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureResource.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTypesPriv.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.h -FILE: ../../../third_party/skia/src/gpu/effects/GrDeviceSpaceEffect.fp FILE: ../../../third_party/skia/src/gpu/effects/GrMatrixEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrMatrixEffect.h -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/geometry/GrShape.cpp FILE: ../../../third_party/skia/src/gpu/geometry/GrShape.h FILE: ../../../third_party/skia/src/gpu/gl/webgl/GrGLMakeNativeInterface_webgl.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLUniformHandler.cpp +FILE: ../../../third_party/skia/src/gpu/v1/SurfaceFillContext_v1.cpp +FILE: ../../../third_party/skia/src/gpu/v1/SurfaceFillContext_v1.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkMSAALoadManager.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkMSAALoadManager.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkManagedResource.h FILE: ../../../third_party/skia/src/image/SkRescaleAndReadPixels.cpp FILE: ../../../third_party/skia/src/image/SkRescaleAndReadPixels.h @@ -4000,14 +3757,31 @@ FILE: ../../../third_party/skia/src/ports/SkImageGeneratorNDK.cpp FILE: ../../../third_party/skia/src/ports/SkNDKConversions.cpp FILE: ../../../third_party/skia/src/ports/SkNDKConversions.h FILE: ../../../third_party/skia/src/sksl/SkSLAnalysis.h +FILE: ../../../third_party/skia/src/sksl/SkSLConstantFolder.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLConstantFolder.h FILE: ../../../third_party/skia/src/sksl/SkSLDehydrator.cpp FILE: ../../../third_party/skia/src/sksl/SkSLDehydrator.h FILE: ../../../third_party/skia/src/sksl/SkSLInliner.cpp FILE: ../../../third_party/skia/src/sksl/SkSLInliner.h -FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVtoHLSL.cpp -FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVtoHLSL.h +FILE: ../../../third_party/skia/src/sksl/SkSLMemoryPool.h +FILE: ../../../third_party/skia/src/sksl/SkSLPool.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLPool.h FILE: ../../../third_party/skia/src/sksl/SkSLSampleUsage.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.h +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLVMCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/codegen/SkSLVMCodeGenerator.h +FILE: ../../../third_party/skia/src/sksl/dsl/DSLCore.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLExpression.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLType.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLVar.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/priv/DSLWriter.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/priv/DSLWriter.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionPrototype.h FILE: ../../../third_party/skia/src/sksl/ir/SkSLInlineMarker.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLPrefixExpression.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLStructDefinition.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLSymbolAlias.h FILE: ../../../third_party/skia/src/utils/SkCustomTypeface.cpp ---------------------------------------------------------------------------------------------------- Copyright 2020 Google LLC @@ -4046,6 +3820,7 @@ LIBRARY: skia ORIGIN: ../../../third_party/skia/bench/GlyphQuadFillBench.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/bench/GlyphQuadFillBench.cpp +FILE: ../../../third_party/skia/bench/GrPathUtilsBench.cpp FILE: ../../../third_party/skia/bench/TessellateBench.cpp FILE: ../../../third_party/skia/experimental/skrive/include/SkRive.h FILE: ../../../third_party/skia/experimental/skrive/src/Artboard.cpp @@ -4062,27 +3837,34 @@ FILE: ../../../third_party/skia/experimental/skrive/src/reader/BinaryReader.cpp FILE: ../../../third_party/skia/experimental/skrive/src/reader/JsonReader.cpp FILE: ../../../third_party/skia/experimental/skrive/src/reader/StreamReader.cpp FILE: ../../../third_party/skia/experimental/skrive/src/reader/StreamReader.h +FILE: ../../../third_party/skia/fuzz/FuzzSkParagraph.cpp FILE: ../../../third_party/skia/gm/3d.cpp FILE: ../../../third_party/skia/gm/bc1_transparency.cpp FILE: ../../../third_party/skia/gm/bicubic.cpp +FILE: ../../../third_party/skia/gm/colrv1.cpp FILE: ../../../third_party/skia/gm/compressed_textures.cpp FILE: ../../../third_party/skia/gm/crbug_1073670.cpp FILE: ../../../third_party/skia/gm/crbug_1086705.cpp FILE: ../../../third_party/skia/gm/crbug_1113794.cpp FILE: ../../../third_party/skia/gm/exoticformats.cpp +FILE: ../../../third_party/skia/gm/rsxtext.cpp FILE: ../../../third_party/skia/gm/skbug_9819.cpp FILE: ../../../third_party/skia/gm/strokerect_anisotropic.cpp FILE: ../../../third_party/skia/gm/verifiers/gmverifier.cpp FILE: ../../../third_party/skia/gm/verifiers/gmverifier.h FILE: ../../../third_party/skia/gm/widebuttcaps.cpp FILE: ../../../third_party/skia/include/core/SkM44.h +FILE: ../../../third_party/skia/include/core/SkSamplingOptions.h FILE: ../../../third_party/skia/include/effects/SkStrokeAndFillPathEffect.h FILE: ../../../third_party/skia/include/gpu/GrDirectContext.h +FILE: ../../../third_party/skia/include/gpu/mtl/GrMtlBackendContext.h FILE: ../../../third_party/skia/include/private/SkOpts_spi.h +FILE: ../../../third_party/skia/include/private/SkTPin.h FILE: ../../../third_party/skia/modules/audioplayer/SkAudioPlayer.cpp FILE: ../../../third_party/skia/modules/audioplayer/SkAudioPlayer.h FILE: ../../../third_party/skia/modules/audioplayer/SkAudioPlayer_mac.mm FILE: ../../../third_party/skia/modules/audioplayer/SkAudioPlayer_none.cpp +FILE: ../../../third_party/skia/modules/audioplayer/SkAudioPlayer_oboe.cpp FILE: ../../../third_party/skia/modules/audioplayer/SkAudioPlayer_sfml.cpp FILE: ../../../third_party/skia/modules/skottie/include/ExternalLayer.h FILE: ../../../third_party/skia/modules/skottie/src/Adapter.h @@ -4101,6 +3883,7 @@ FILE: ../../../third_party/skia/modules/skottie/src/animator/TextKeyframeAnimato FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h +FILE: ../../../third_party/skia/modules/skottie/src/effects/BlackAndWhiteEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/BrightnessContrastEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/CornerPinEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/effects/DisplacementMapEffect.cpp @@ -4123,61 +3906,83 @@ FILE: ../../../third_party/skia/modules/skparagraph/gm/simple_gm.cpp FILE: ../../../third_party/skia/modules/sksg/include/SkSGGeometryEffect.h FILE: ../../../third_party/skia/modules/sksg/src/SkSGGeometryEffect.cpp FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp -FILE: ../../../third_party/skia/modules/skshaper/src/SkUnicode.h -FILE: ../../../third_party/skia/modules/skshaper/src/SkUnicode_icu.cpp +FILE: ../../../third_party/skia/modules/skunicode/include/SkUnicode.h +FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_icu.cpp +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFe.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeBlend.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeColorMatrix.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeComposite.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeDisplacementMap.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeFlood.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeGaussianBlur.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeLighting.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeMorphology.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeOffset.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeTurbulence.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFilter.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFilterContext.h +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFe.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeBlend.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeColorMatrix.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeComposite.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeDisplacementMap.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeFlood.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeGaussianBlur.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeLighting.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeMorphology.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeOffset.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeTurbulence.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFilter.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFilterContext.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGTextPriv.h +FILE: ../../../third_party/skia/modules/svg/utils/SvgTool.cpp FILE: ../../../third_party/skia/samplecode/Sample3D.cpp FILE: ../../../third_party/skia/samplecode/SampleAudio.cpp FILE: ../../../third_party/skia/samplecode/SampleFitCubicToCircle.cpp FILE: ../../../third_party/skia/samplecode/SampleSimpleStroker.cpp FILE: ../../../third_party/skia/samplecode/SampleTiming.cpp +FILE: ../../../third_party/skia/samplecode/SampleVariableWidthStroker.cpp 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/SkPathView.h +FILE: ../../../third_party/skia/src/core/SkMipmapBuilder.h +FILE: ../../../third_party/skia/src/core/SkOpts_erms.cpp +FILE: ../../../third_party/skia/src/core/SkSamplingPriv.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 FILE: ../../../third_party/skia/src/gpu/GrEagerVertexAllocator.h FILE: ../../../third_party/skia/src/gpu/GrHashMapWithCache.h FILE: ../../../third_party/skia/src/gpu/GrRecordingContextPriv.cpp -FILE: ../../../third_party/skia/src/gpu/GrSTArenaList.h -FILE: ../../../third_party/skia/src/gpu/GrThreadSafeUniquelyKeyedProxyViewCache.cpp -FILE: ../../../third_party/skia/src/gpu/GrThreadSafeUniquelyKeyedProxyViewCache.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrAutoMapVertexBuffer.h -FILE: ../../../third_party/skia/src/gpu/effects/GrArithmeticProcessor.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrDitherEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/GrHighContrastFilterEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrArithmeticProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrArithmeticProcessor.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrDitherEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrDitherEffect.h -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrHighContrastFilterEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrHighContrastFilterEffect.h +FILE: ../../../third_party/skia/src/gpu/GrThreadSafeCache.cpp +FILE: ../../../third_party/skia/src/gpu/GrThreadSafeCache.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrAATriangulator.cpp +FILE: ../../../third_party/skia/src/gpu/geometry/GrAATriangulator.h +FILE: ../../../third_party/skia/src/gpu/geometry/GrWangsFormula.h +FILE: ../../../third_party/skia/src/gpu/mock/GrMockOpTarget.h +FILE: ../../../third_party/skia/src/gpu/ops/GrDrawAtlasPathOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrDrawAtlasPathOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathAtlasMgr.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathAtlasMgr.h FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathShapeData.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathShapeData.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrDrawAtlasPathOp.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrDrawAtlasPathOp.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrMidpointContourParser.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrResolveLevelCounter.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeIterator.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrVectorXform.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrWangsFormula.h -FILE: ../../../third_party/skia/src/gpu/text/GrSDFTOptions.cpp -FILE: ../../../third_party/skia/src/gpu/text/GrSDFTOptions.h +FILE: ../../../third_party/skia/src/gpu/text/GrSDFTControl.cpp +FILE: ../../../third_party/skia/src/gpu/text/GrSDFTControl.h FILE: ../../../third_party/skia/src/opts/SkOpts_skx.cpp FILE: ../../../third_party/skia/src/ports/SkScalerContext_mac_ct.h FILE: ../../../third_party/skia/src/ports/SkTypeface_mac_ct.h FILE: ../../../third_party/skia/src/utils/mac/SkCGBase.h FILE: ../../../third_party/skia/src/utils/mac/SkCGGeometry.h -FILE: ../../../third_party/skia/src/utils/mac/SkCTFontSmoothBehavior.cpp -FILE: ../../../third_party/skia/src/utils/mac/SkCTFontSmoothBehavior.h +FILE: ../../../third_party/skia/src/utils/mac/SkCTFont.cpp +FILE: ../../../third_party/skia/src/utils/mac/SkCTFont.h ---------------------------------------------------------------------------------------------------- Copyright 2020 Google Inc. @@ -4210,19 +4015,109 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: skia +ORIGIN: ../../../third_party/skia/bench/MSKPBench.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/bench/MSKPBench.cpp +FILE: ../../../third_party/skia/bench/MSKPBench.h +FILE: ../../../third_party/skia/fuzz/FuzzDDLThreading.cpp +FILE: ../../../third_party/skia/gm/aarecteffect.cpp +FILE: ../../../third_party/skia/gm/colorspace.cpp +FILE: ../../../third_party/skia/gm/drawglyphs.cpp +FILE: ../../../third_party/skia/gm/largeclippedpath.cpp +FILE: ../../../third_party/skia/gm/skbug_12212.cpp +FILE: ../../../third_party/skia/include/private/GrMtlTypesPriv.h +FILE: ../../../third_party/skia/include/utils/SkOrderedFontMgr.h +FILE: ../../../third_party/skia/infra/bots/task_drivers/recreate_skps/recreate_skps.go +FILE: ../../../third_party/skia/modules/androidkit/src/AndroidKit.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Canvas.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/ColorFilters.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Gradients.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Image.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/ImageFilter.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Matrix.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Paint.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Path.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/PathBuilder.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/RuntimeShaderBuilder.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Shader.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/SkottieAnimation.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Surface.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Surface.h +FILE: ../../../third_party/skia/modules/androidkit/src/SurfaceThread.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/SurfaceThread.h +FILE: ../../../third_party/skia/modules/androidkit/src/Utils.cpp +FILE: ../../../third_party/skia/modules/androidkit/src/Utils.h +FILE: ../../../third_party/skia/modules/skottie/src/effects/FractalNoiseEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/SphereEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/ThresholdEffect.cpp +FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_icu.h +FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_icu_builtin.cpp +FILE: ../../../third_party/skia/modules/skunicode/src/SkUnicode_icu_runtime.cpp +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeImage.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGFeLightSource.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGImage.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGMask.h +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeImage.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGFeLightSource.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGImage.cpp +FILE: ../../../third_party/skia/modules/svg/src/SkSVGMask.cpp +FILE: ../../../third_party/skia/src/gpu/GrDrawIndirectCommand.h +FILE: ../../../third_party/skia/src/gpu/GrRenderTaskCluster.cpp +FILE: ../../../third_party/skia/src/gpu/GrRenderTaskCluster.h +FILE: ../../../third_party/skia/src/gpu/GrThreadSafePipelineBuilder.cpp +FILE: ../../../third_party/skia/src/gpu/GrThreadSafePipelineBuilder.h +FILE: ../../../third_party/skia/src/gpu/mock/GrMockRenderTask.h +FILE: ../../../third_party/skia/src/gpu/mock/GrMockSurfaceProxy.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlFramebuffer.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlFramebuffer.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipeline.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlRenderCommandEncoder.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrAtlasRenderTask.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrAtlasRenderTask.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrCullTest.h +FILE: ../../../third_party/skia/src/shaders/SkTransformShader.cpp +FILE: ../../../third_party/skia/src/shaders/SkTransformShader.h +FILE: ../../../third_party/skia/src/utils/SkOrderedFontMgr.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2021 Google Inc. + +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 the copyright holder 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: skia ORIGIN: ../../../third_party/skia/bench/ReadPixBench.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/bench/ReadPixBench.cpp FILE: ../../../third_party/skia/bench/WriterBench.cpp -FILE: ../../../third_party/skia/include/effects/SkColorFilterImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkLightingImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkMagnifierImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkMatrixConvolutionImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkMergeImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkMorphologyImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkOffsetImageFilter.h FILE: ../../../third_party/skia/src/core/SkImageFilter.cpp FILE: ../../../third_party/skia/src/core/SkUtilsArm.cpp FILE: ../../../third_party/skia/src/effects/imagefilters/SkColorFilterImageFilter.cpp @@ -4286,9 +4181,6 @@ FILE: ../../../third_party/skia/docs/examples/Bitmap_HeapAllocator_allocPixelRef FILE: ../../../third_party/skia/docs/examples/Bitmap_allocN32Pixels.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_allocPixels.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_allocPixelsFlags.cpp -FILE: ../../../third_party/skia/docs/examples/Bitmap_allocPixels_2.cpp -FILE: ../../../third_party/skia/docs/examples/Bitmap_allocPixels_3.cpp -FILE: ../../../third_party/skia/docs/examples/Bitmap_allocPixels_4.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_bounds.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_bytesPerPixel.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_colorSpace.cpp @@ -4356,7 +4248,6 @@ FILE: ../../../third_party/skia/docs/examples/Bitmap_tryAllocPixels_4.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_width.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_writePixels.cpp FILE: ../../../third_party/skia/docs/examples/Bitmap_writePixels_2.cpp -FILE: ../../../third_party/skia/docs/examples/BlendMode_Name.cpp FILE: ../../../third_party/skia/docs/examples/Blend_Mode_Methods.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_129.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_MakeRasterDirect.cpp @@ -4390,8 +4281,6 @@ FILE: ../../../third_party/skia/docs/examples/Canvas_drawArc_a.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_drawArc_b.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_drawAtlas.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_drawAtlas_2.cpp -FILE: ../../../third_party/skia/docs/examples/Canvas_drawAtlas_3.cpp -FILE: ../../../third_party/skia/docs/examples/Canvas_drawAtlas_4.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmap.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmapRect.cpp FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmapRect_2.cpp @@ -4531,7 +4420,6 @@ FILE: ../../../third_party/skia/docs/examples/Dst_Out.cpp FILE: ../../../third_party/skia/docs/examples/Dst_Over.cpp FILE: ../../../third_party/skia/docs/examples/Exclusion.cpp FILE: ../../../third_party/skia/docs/examples/Fake_Bold.cpp -FILE: ../../../third_party/skia/docs/examples/Filter_Quality_Methods.cpp FILE: ../../../third_party/skia/docs/examples/Font_breakText.cpp FILE: ../../../third_party/skia/docs/examples/HSVToColor.cpp FILE: ../../../third_party/skia/docs/examples/HSVToColor_2.cpp @@ -4617,9 +4505,7 @@ FILE: ../../../third_party/skia/docs/examples/ImageInfo_gammaCloseToSRGB.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_height.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_isEmpty.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_isOpaque.cpp -FILE: ../../../third_party/skia/docs/examples/ImageInfo_makeAlphaType.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_makeColorSpace.cpp -FILE: ../../../third_party/skia/docs/examples/ImageInfo_makeColorType.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_makeWH.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_minRowBytes.cpp FILE: ../../../third_party/skia/docs/examples/ImageInfo_minRowBytes64.cpp @@ -4683,7 +4569,6 @@ FILE: ../../../third_party/skia/docs/examples/Matrix_InvalidMatrix.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_MakeAll.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_MakeRectToRect.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_MakeScale.cpp -FILE: ../../../third_party/skia/docs/examples/Matrix_MakeScale_2.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_MakeTrans.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_ScaleToFit.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_SetAffineIdentity.cpp @@ -4759,7 +4644,6 @@ FILE: ../../../third_party/skia/docs/examples/Matrix_setPerspX.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_setPerspY.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_setPolyToPoly.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_setRSXform.cpp -FILE: ../../../third_party/skia/docs/examples/Matrix_setRectToRect.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_setRotate.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_setRotate_2.cpp FILE: ../../../third_party/skia/docs/examples/Matrix_setScale.cpp @@ -4792,17 +4676,14 @@ FILE: ../../../third_party/skia/docs/examples/Paint_countText.cpp FILE: ../../../third_party/skia/docs/examples/Paint_empty_constructor.cpp FILE: ../../../third_party/skia/docs/examples/Paint_equal_operator.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getAlpha.cpp -FILE: ../../../third_party/skia/docs/examples/Paint_getBlendMode.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getColor.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getColor4f.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getColorFilter.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getFillPath.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getFillPath_2.cpp -FILE: ../../../third_party/skia/docs/examples/Paint_getFilterQuality.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getFlags.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getFontMetrics.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getFontSpacing.cpp -FILE: ../../../third_party/skia/docs/examples/Paint_getHash.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getHinting.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getImageFilter.cpp FILE: ../../../third_party/skia/docs/examples/Paint_getMaskFilter.cpp @@ -4853,7 +4734,6 @@ FILE: ../../../third_party/skia/docs/examples/Paint_setColorFilter.cpp FILE: ../../../third_party/skia/docs/examples/Paint_setDither.cpp FILE: ../../../third_party/skia/docs/examples/Paint_setEmbeddedBitmapText.cpp FILE: ../../../third_party/skia/docs/examples/Paint_setFakeBoldText.cpp -FILE: ../../../third_party/skia/docs/examples/Paint_setFilterQuality.cpp FILE: ../../../third_party/skia/docs/examples/Paint_setFlags.cpp FILE: ../../../third_party/skia/docs/examples/Paint_setHinting.cpp FILE: ../../../third_party/skia/docs/examples/Paint_setImageFilter.cpp @@ -4939,7 +4819,6 @@ FILE: ../../../third_party/skia/docs/examples/Path_dump_2.cpp FILE: ../../../third_party/skia/docs/examples/Path_empty_constructor.cpp FILE: ../../../third_party/skia/docs/examples/Path_equal_operator.cpp FILE: ../../../third_party/skia/docs/examples/Path_getBounds.cpp -FILE: ../../../third_party/skia/docs/examples/Path_getConvexityOrUnknown.cpp FILE: ../../../third_party/skia/docs/examples/Path_getFillType.cpp FILE: ../../../third_party/skia/docs/examples/Path_getGenerationID.cpp FILE: ../../../third_party/skia/docs/examples/Path_getLastPt.cpp @@ -5323,6 +5202,7 @@ FILE: ../../../third_party/skia/docs/examples/Typeface_Methods.cpp FILE: ../../../third_party/skia/docs/examples/Xor.cpp FILE: ../../../third_party/skia/docs/examples/incomplete.cpp FILE: ../../../third_party/skia/experimental/minimal_ios_mtl_skia_app/main.mm +FILE: ../../../third_party/skia/experimental/sktext/editor/App.cpp FILE: ../../../third_party/skia/experimental/xform/SkShape.cpp FILE: ../../../third_party/skia/experimental/xform/SkShape.h FILE: ../../../third_party/skia/experimental/xform/SkXform.cpp @@ -5333,9 +5213,8 @@ FILE: ../../../third_party/skia/gm/fp_sample_chaining.cpp 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/manypathatlases.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/swizzle.cpp FILE: ../../../third_party/skia/gm/tilemodes_alpha.cpp FILE: ../../../third_party/skia/include/core/SkPathTypes.h @@ -5350,29 +5229,25 @@ FILE: ../../../third_party/skia/modules/skplaintexteditor/src/shape.h FILE: ../../../third_party/skia/modules/skplaintexteditor/src/stringslice.cpp FILE: ../../../third_party/skia/modules/skplaintexteditor/src/word_boundaries.cpp FILE: ../../../third_party/skia/modules/skplaintexteditor/src/word_boundaries.h -FILE: ../../../third_party/skia/samplecode/SampleTessellatedWedge.cpp +FILE: ../../../third_party/skia/samplecode/SamplePathTessellators.cpp FILE: ../../../third_party/skia/src/core/SkMalloc.cpp FILE: ../../../third_party/skia/src/core/SkPixelRefPriv.h FILE: ../../../third_party/skia/src/core/SkTextBlobTrace.cpp FILE: ../../../third_party/skia/src/core/SkTextBlobTrace.h -FILE: ../../../third_party/skia/src/gpu/GrPersistentCacheUtils.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrSampleMaskProcessor.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrSampleMaskProcessor.h -FILE: ../../../third_party/skia/src/gpu/ccpr/GrStencilAtlasOp.h -FILE: ../../../third_party/skia/src/gpu/effects/GrComposeLerpEffect.fp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrComposeLerpEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/generated/GrComposeLerpEffect.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathTessellateOp.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathTessellateOp.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrStencilPathShader.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrStencilPathShader.h +FILE: ../../../third_party/skia/src/gpu/ops/GrAtlasPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrAtlasPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathInnerTriangulateOp.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrTessellationPathRenderer.cpp FILE: ../../../third_party/skia/src/gpu/tessellate/GrTessellationPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrPathTessellationShader.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrPathTessellationShader.h +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFGraphicStackState.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFGraphicStackState.h FILE: ../../../third_party/skia/src/pdf/SkPDFType1Font.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFType1Font.h -FILE: ../../../third_party/skia/src/sksl/SkSLSectionAndParameterHelper.cpp ---------------------------------------------------------------------------------------------------- Copyright 2019 Google LLC. @@ -5501,7 +5376,6 @@ FILE: ../../../third_party/skia/docs/examples/image_subsets_get_different_uids.c FILE: ../../../third_party/skia/docs/examples/image_to_bitmap_does_not_preserve_genid.cpp FILE: ../../../third_party/skia/docs/examples/inlinepixmapconstructor.cpp FILE: ../../../third_party/skia/docs/examples/issue640176.cpp -FILE: ../../../third_party/skia/docs/examples/kLow_SkFilterQuality.cpp FILE: ../../../third_party/skia/docs/examples/l_system_plant.cpp FILE: ../../../third_party/skia/docs/examples/maddash.cpp FILE: ../../../third_party/skia/docs/examples/makeRasterImage_fail.cpp @@ -5577,24 +5451,316 @@ FILE: ../../../third_party/skia/docs/examples/unexpected_setAlphaType.cpp FILE: ../../../third_party/skia/docs/examples/upscale_checkerboard.cpp FILE: ../../../third_party/skia/docs/examples/weird_RRect_bug.cpp FILE: ../../../third_party/skia/docs/examples/zero_off_dashing.cpp +FILE: ../../../third_party/skia/gm/clear_swizzle.cpp FILE: ../../../third_party/skia/gm/gpu_blur_utils.cpp FILE: ../../../third_party/skia/src/gpu/GrFinishCallbacks.cpp FILE: ../../../third_party/skia/src/gpu/GrFinishCallbacks.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrFillPathShader.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrFillPathShader.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathShader.h +FILE: ../../../third_party/skia/src/gpu/GrVx.h +FILE: ../../../third_party/skia/src/gpu/ops/GrAtlasInstancedHelper.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeHardwareTessellator.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeTessellateOp.cpp FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeTessellateOp.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeTessellateShader.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeTessellateShader.h +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrStrokeTessellationShader_HardwareImpl.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrTessellationShader.h FILE: ../../../third_party/skia/src/opts/SkVM_opts.h FILE: ../../../third_party/skia/src/sksl/SkSLAnalysis.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLModifiersPool.h FILE: ../../../third_party/skia/src/sksl/SkSLRehydrator.cpp FILE: ../../../third_party/skia/src/sksl/SkSLRehydrator.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLCodeStringExpression.h FILE: ../../../third_party/skia/src/sksl/ir/SkSLConstructor.cpp -FILE: ../../../third_party/skia/src/sksl/ir/SkSLIRNode.cpp ---------------------------------------------------------------------------------------------------- -Copyright 2020 Google LLC. +Copyright 2020 Google LLC. + +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 the copyright holder 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: skia +ORIGIN: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.cpp +FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.h +FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoEncoder.cpp +FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoEncoder.h +FILE: ../../../third_party/skia/gm/backdrop.cpp +FILE: ../../../third_party/skia/gm/backdrop_imagefilter_croprect.cpp +FILE: ../../../third_party/skia/gm/bug9331.cpp +FILE: ../../../third_party/skia/gm/clip_sierpinski_region.cpp +FILE: ../../../third_party/skia/gm/collapsepaths.cpp +FILE: ../../../third_party/skia/gm/compositor_quads.cpp +FILE: ../../../third_party/skia/gm/crbug_913349.cpp +FILE: ../../../third_party/skia/gm/crbug_938592.cpp +FILE: ../../../third_party/skia/gm/crbug_947055.cpp +FILE: ../../../third_party/skia/gm/crbug_996140.cpp +FILE: ../../../third_party/skia/gm/ducky_yuv_blend.cpp +FILE: ../../../third_party/skia/gm/fiddle.cpp +FILE: ../../../third_party/skia/gm/mac_aa_explorer.cpp +FILE: ../../../third_party/skia/gm/mixercolorfilter.cpp +FILE: ../../../third_party/skia/gm/overdrawcanvas.cpp +FILE: ../../../third_party/skia/gm/postercircle.cpp +FILE: ../../../third_party/skia/gm/skbug_8664.cpp +FILE: ../../../third_party/skia/gm/skbug_8955.cpp +FILE: ../../../third_party/skia/gm/tessellation.cpp +FILE: ../../../third_party/skia/gm/video_decoder.cpp +FILE: ../../../third_party/skia/gm/yuv420_odd_dim.cpp +FILE: ../../../third_party/skia/include/core/SkTileMode.h +FILE: ../../../third_party/skia/include/gpu/GrContextThreadSafeProxy.h +FILE: ../../../third_party/skia/include/gpu/GrRecordingContext.h +FILE: ../../../third_party/skia/include/gpu/dawn/GrDawnTypes.h +FILE: ../../../third_party/skia/include/ports/SkCFObject.h +FILE: ../../../third_party/skia/include/private/GrContext_Base.h +FILE: ../../../third_party/skia/include/private/GrImageContext.h +FILE: ../../../third_party/skia/include/private/SkSLDefines.h +FILE: ../../../third_party/skia/include/private/SkVx.h +FILE: ../../../third_party/skia/modules/skottie/src/Composition.cpp +FILE: ../../../third_party/skia/modules/skottie/src/Composition.h +FILE: ../../../third_party/skia/modules/skottie/src/Layer.cpp +FILE: ../../../third_party/skia/modules/skottie/src/Layer.h +FILE: ../../../third_party/skia/modules/skottie/src/effects/DropShadowEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/Effects.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/Effects.h +FILE: ../../../third_party/skia/modules/skottie/src/effects/FillEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/GaussianBlurEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/GradientEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/HueSaturationEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/InvertEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/LevelsEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/LinearWipeEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionBlurEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionBlurEffect.h +FILE: ../../../third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/RadialWipeEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/ShiftChannelsEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/TintEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/TransformEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/TritoneEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/effects/VenetianBlindsEffect.cpp +FILE: ../../../third_party/skia/modules/skottie/src/layers/FootageLayer.cpp +FILE: ../../../third_party/skia/modules/skottie/src/layers/NullLayer.cpp +FILE: ../../../third_party/skia/modules/skottie/src/layers/SolidLayer.cpp +FILE: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.cpp +FILE: ../../../third_party/skia/modules/skottie/src/text/RangeSelector.h +FILE: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.cpp +FILE: ../../../third_party/skia/modules/skottie/src/text/SkottieShaper.h +FILE: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.cpp +FILE: ../../../third_party/skia/modules/skottie/src/text/TextAdapter.h +FILE: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/text/TextAnimator.h +FILE: ../../../third_party/skia/modules/skottie/src/text/TextValue.cpp +FILE: ../../../third_party/skia/modules/skottie/src/text/TextValue.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGRenderEffect.h +FILE: ../../../third_party/skia/modules/sksg/src/SkSGNodePriv.h +FILE: ../../../third_party/skia/modules/sksg/src/SkSGRenderEffect.cpp +FILE: ../../../third_party/skia/modules/sksg/src/SkSGTransformPriv.h +FILE: ../../../third_party/skia/modules/svg/include/SkSVGText.h +FILE: ../../../third_party/skia/modules/svg/src/SkSVGText.cpp +FILE: ../../../third_party/skia/samplecode/SampleDegenerateQuads.cpp +FILE: ../../../third_party/skia/samplecode/SampleSG.cpp +FILE: ../../../third_party/skia/samplecode/SampleThinAA.cpp +FILE: ../../../third_party/skia/src/codec/SkScalingCodec.h +FILE: ../../../third_party/skia/src/core/SkDescriptor.cpp +FILE: ../../../third_party/skia/src/core/SkDraw_atlas.cpp +FILE: ../../../third_party/skia/src/core/SkEffectPriv.h +FILE: ../../../third_party/skia/src/core/SkEnumerate.h +FILE: ../../../third_party/skia/src/core/SkGlyphBuffer.cpp +FILE: ../../../third_party/skia/src/core/SkGlyphBuffer.h +FILE: ../../../third_party/skia/src/core/SkPathMakers.h +FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.cpp +FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.h +FILE: ../../../third_party/skia/src/core/SkStrikeSpec.h +FILE: ../../../third_party/skia/src/core/SkVMBlitter.cpp +FILE: ../../../third_party/skia/src/core/SkYUVMath.cpp +FILE: ../../../third_party/skia/src/core/SkYUVMath.h +FILE: ../../../third_party/skia/src/core/SkZip.h +FILE: ../../../third_party/skia/src/gpu/GrAHardwareBufferUtils.cpp +FILE: ../../../third_party/skia/src/gpu/GrAHardwareBufferUtils.h +FILE: ../../../third_party/skia/src/gpu/GrBaseContextPriv.h +FILE: ../../../third_party/skia/src/gpu/GrBuffer.h +FILE: ../../../third_party/skia/src/gpu/GrContextThreadSafeProxy.cpp +FILE: ../../../third_party/skia/src/gpu/GrContext_Base.cpp +FILE: ../../../third_party/skia/src/gpu/GrCpuBuffer.h +FILE: ../../../third_party/skia/src/gpu/GrDataUtils.cpp +FILE: ../../../third_party/skia/src/gpu/GrDataUtils.h +FILE: ../../../third_party/skia/src/gpu/GrDirectContextPriv.cpp +FILE: ../../../third_party/skia/src/gpu/GrGpuBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/GrGpuBuffer.h +FILE: ../../../third_party/skia/src/gpu/GrImageContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrImageContextPriv.h +FILE: ../../../third_party/skia/src/gpu/GrOpsTask.cpp +FILE: ../../../third_party/skia/src/gpu/GrOpsTask.h +FILE: ../../../third_party/skia/src/gpu/GrRecordingContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrRecordingContextPriv.h +FILE: ../../../third_party/skia/src/gpu/GrSPIRVUniformHandler.cpp +FILE: ../../../third_party/skia/src/gpu/GrSPIRVUniformHandler.h +FILE: ../../../third_party/skia/src/gpu/GrSPIRVVaryingHandler.cpp +FILE: ../../../third_party/skia/src/gpu/GrSPIRVVaryingHandler.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnAttachment.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnAttachment.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnBuffer.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnCaps.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnCaps.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnGpu.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnGpu.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnOpsRenderPass.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramBuilder.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramBuilder.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramDataManager.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnProgramDataManager.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRenderTarget.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRenderTarget.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRingBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRingBuffer.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTexture.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTexture.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTextureRenderTarget.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTextureRenderTarget.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnUtil.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnUtil.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCommandBuffer.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCommandBuffer.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlDepthStencil.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSemaphore.h +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlSemaphore.mm +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.h +FILE: ../../../third_party/skia/src/image/SkSurface_GpuMtl.mm +FILE: ../../../third_party/skia/src/sksl/SkSLOutputStream.cpp +FILE: ../../../third_party/skia/src/utils/SkCharToGlyphCache.cpp +FILE: ../../../third_party/skia/src/utils/SkCharToGlyphCache.h +FILE: ../../../third_party/skia/src/utils/SkClipStackUtils.cpp +FILE: ../../../third_party/skia/src/utils/SkClipStackUtils.h +FILE: ../../../third_party/skia/src/utils/SkShaperJSONWriter.cpp +FILE: ../../../third_party/skia/src/utils/SkShaperJSONWriter.h +FILE: ../../../third_party/skia/src/utils/win/SkObjBase.h +---------------------------------------------------------------------------------------------------- +Copyright 2019 Google Inc. + +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 the copyright holder 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: skia +ORIGIN: ../../../third_party/skia/experimental/sorttoy/Cmds.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/experimental/sorttoy/Cmds.cpp +FILE: ../../../third_party/skia/experimental/sorttoy/Cmds.h +FILE: ../../../third_party/skia/experimental/sorttoy/Fake.cpp +FILE: ../../../third_party/skia/experimental/sorttoy/Fake.h +FILE: ../../../third_party/skia/experimental/sorttoy/SortKey.h +FILE: ../../../third_party/skia/experimental/sorttoy/sorttoy.cpp +FILE: ../../../third_party/skia/experimental/sorttoy/sorttypes.h +FILE: ../../../third_party/skia/gm/destcolor.cpp +FILE: ../../../third_party/skia/gm/dsl_processor_test.cpp +FILE: ../../../third_party/skia/include/core/SkStringView.h +FILE: ../../../third_party/skia/include/private/SkSLProgramKind.h +FILE: ../../../third_party/skia/include/private/SkTOptional.h +FILE: ../../../third_party/skia/include/sksl/DSLBlock.h +FILE: ../../../third_party/skia/include/sksl/DSLCase.h +FILE: ../../../third_party/skia/include/sksl/DSLFunction.h +FILE: ../../../third_party/skia/include/sksl/DSLLayout.h +FILE: ../../../third_party/skia/include/sksl/DSLRuntimeEffects.h +FILE: ../../../third_party/skia/include/sksl/DSLStatement.h +FILE: ../../../third_party/skia/include/sksl/DSLWrapper.h +FILE: ../../../third_party/skia/include/sksl/SkSLErrorReporter.h +FILE: ../../../third_party/skia/src/core/SkStringView.cpp +FILE: ../../../third_party/skia/src/gpu/GrVertexChunkArray.cpp +FILE: ../../../third_party/skia/src/gpu/GrVertexChunkArray.h +FILE: ../../../third_party/skia/src/gpu/effects/GrModulateAtlasCoverageEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrModulateAtlasCoverageEffect.h +FILE: ../../../third_party/skia/src/gpu/ops/GrAtlasInstancedHelper.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathCurveTessellator.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathCurveTessellator.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathStencilCoverOp.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathStencilCoverOp.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathTessellateOp.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathTessellateOp.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathTessellator.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathWedgeTessellator.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathWedgeTessellator.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrPathXform.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeFixedCountTessellator.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrStrokeTessellator.h +FILE: ../../../third_party/skia/src/gpu/tessellate/shaders/GrTessellationShader.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLDSLParser.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLDSLParser.h +FILE: ../../../third_party/skia/src/sksl/SkSLErrorReporter.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLMangler.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLParsedModule.h +FILE: ../../../third_party/skia/src/sksl/dsl/DSLBlock.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLCase.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLFunction.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLLayout.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLRuntimeEffects.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLStatement.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/DSLSymbols.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/priv/DSLFPs.cpp +FILE: ../../../third_party/skia/src/sksl/dsl/priv/DSLFPs.h +FILE: ../../../third_party/skia/src/sksl/dsl/priv/DSL_priv.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLFunctionDeclaration.cpp +FILE: ../../../third_party/skia/src/sksl/ir/SkSLPoison.h +FILE: ../../../third_party/skia/src/sksl/ir/SkSLVariable.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2021 Google LLC. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -5625,6 +5791,44 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: skia +ORIGIN: ../../../third_party/skia/experimental/tskit/go/gen_types/gen_types.go + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/experimental/tskit/go/gen_types/gen_types.go +FILE: ../../../third_party/skia/experimental/tskit/go/gen_types/gen_types_test.go +FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/skpbench_flags.go +FILE: ../../../third_party/skia/infra/bots/task_drivers/perf_puppeteer_skottie_frames/make_lotties_with_assets/make_lotties_with_assets.go +---------------------------------------------------------------------------------------------------- +Copyright 2021 The Chromium 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: skia ORIGIN: ../../../third_party/skia/fuzz/FuzzDrawFunctions.cpp + ../../../third_party/skia/LICENSE @@ -5670,7 +5874,6 @@ FILE: ../../../third_party/skia/fuzz/FuzzEncoders.cpp FILE: ../../../third_party/skia/fuzz/FuzzPolyUtils.cpp FILE: ../../../third_party/skia/modules/canvaskit/canvaskit_bindings.cpp FILE: ../../../third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCoverageCountingPathRenderer_none.cpp ---------------------------------------------------------------------------------------------------- Copyright 2018 Google LLC @@ -5805,15 +6008,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2GLSL.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzDDLThreading.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2GLSL.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2Metal.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2SPIRV.cpp -FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp +FILE: ../../../third_party/skia/fuzz/FuzzTriangulation.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzDDLThreading.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzRegionOp.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkParagraph.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzTriangulation.cpp ---------------------------------------------------------------------------------------------------- -Copyright 2019 Google, LLC +Copyright 2021 Google, LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -5846,11 +6049,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/gm/circles.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2GLSL.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/gm/circles.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2GLSL.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2Metal.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSKSL2SPIRV.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp ---------------------------------------------------------------------------------------------------- -Copyright 2012 Intel Inc. +Copyright 2019 Google, LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -5927,7 +6134,6 @@ FILE: ../../../third_party/skia/include/private/SkTDArray.h FILE: ../../../third_party/skia/include/private/SkTemplates.h FILE: ../../../third_party/skia/include/utils/SkBase64.h FILE: ../../../third_party/skia/include/utils/SkCamera.h -FILE: ../../../third_party/skia/include/utils/SkInterpolator.h FILE: ../../../third_party/skia/include/utils/SkParse.h FILE: ../../../third_party/skia/include/utils/SkParsePath.h FILE: ../../../third_party/skia/include/utils/SkRandom.h @@ -5971,6 +6177,7 @@ FILE: ../../../third_party/skia/src/core/SkOSFile.h FILE: ../../../third_party/skia/src/core/SkPaint.cpp FILE: ../../../third_party/skia/src/core/SkPath.cpp FILE: ../../../third_party/skia/src/core/SkPathEffect.cpp +FILE: ../../../third_party/skia/src/core/SkPathEffectBase.h FILE: ../../../third_party/skia/src/core/SkPointPriv.h FILE: ../../../third_party/skia/src/core/SkRect.cpp FILE: ../../../third_party/skia/src/core/SkRegion.cpp @@ -6071,7 +6278,6 @@ LIBRARY: skia ORIGIN: ../../../third_party/skia/include/core/SkDrawLooper.h + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/include/core/SkDrawLooper.h -FILE: ../../../third_party/skia/include/effects/SkBlurImageFilter.h FILE: ../../../third_party/skia/src/core/SkScan.h FILE: ../../../third_party/skia/src/core/SkScan_Antihair.cpp FILE: ../../../third_party/skia/src/core/SkTypeface.cpp @@ -6127,10 +6333,8 @@ FILE: ../../../third_party/skia/src/core/SkPoint.cpp FILE: ../../../third_party/skia/src/core/SkPtrRecorder.h FILE: ../../../third_party/skia/src/core/SkStroke.cpp FILE: ../../../third_party/skia/src/core/SkWriter32.h -FILE: ../../../third_party/skia/src/effects/SkPackBits.h FILE: ../../../third_party/skia/src/ports/SkFontMgr_empty_factory.cpp FILE: ../../../third_party/skia/src/ports/SkImageEncoder_CG.cpp -FILE: ../../../third_party/skia/src/utils/SkInterpolator.cpp ---------------------------------------------------------------------------------------------------- Copyright 2008 The Android Open Source Project @@ -6249,7 +6453,6 @@ LIBRARY: skia ORIGIN: ../../../third_party/skia/include/docs/SkPDFDocument.h + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/include/docs/SkPDFDocument.h -FILE: ../../../third_party/skia/src/gpu/GrPathRendering_none.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFGlyphUse.h FILE: ../../../third_party/skia/src/pdf/SkPDFSubsetFont.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFSubsetFont.h @@ -6289,52 +6492,11 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: skia -ORIGIN: ../../../third_party/skia/include/effects/SkPictureImageFilter.h + ../../../third_party/skia/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/include/effects/SkPictureImageFilter.h -FILE: ../../../third_party/skia/include/effects/SkXfermodeImageFilter.h -FILE: ../../../third_party/skia/src/effects/imagefilters/SkPictureImageFilter.cpp -FILE: ../../../third_party/skia/src/effects/imagefilters/SkXfermodeImageFilter.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2013 The Android Open Source Project - -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 the copyright holder 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: skia ORIGIN: ../../../third_party/skia/include/gpu/GrConfig.h + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/include/gpu/GrConfig.h -FILE: ../../../third_party/skia/include/gpu/GrContext.h FILE: ../../../third_party/skia/include/gpu/GrTypes.h FILE: ../../../third_party/skia/src/core/SkImageInfo.cpp FILE: ../../../third_party/skia/src/core/SkRasterClip.cpp @@ -6351,9 +6513,9 @@ FILE: ../../../third_party/skia/src/gpu/GrRectanizer.h FILE: ../../../third_party/skia/src/gpu/GrRectanizerPow2.cpp FILE: ../../../third_party/skia/src/gpu/GrTBlockList.h FILE: ../../../third_party/skia/src/gpu/GrVertexWriter.h -FILE: ../../../third_party/skia/src/gpu/SkGpuDevice.h FILE: ../../../third_party/skia/src/gpu/SkGr.cpp FILE: ../../../third_party/skia/src/gpu/geometry/GrRect.h +FILE: ../../../third_party/skia/src/gpu/v1/Device_v1.h FILE: ../../../third_party/skia/src/ports/SkDebug_win.cpp ---------------------------------------------------------------------------------------------------- Copyright 2010 Google Inc. @@ -6465,16 +6627,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/infra/bots/gen_tasks_logic/dm_flags.go + ../../../LICENSE +ORIGIN: ../../../third_party/skia/infra/bots/gen_tasks_logic/compile_cas.go + ../../../LICENSE TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/compile_cas.go FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/dm_flags.go FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/job_builder.go FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/nano_flags.go FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/schema.go FILE: ../../../third_party/skia/infra/bots/gen_tasks_logic/task_builder.go FILE: ../../../third_party/skia/infra/bots/task_drivers/canary/canary.go -FILE: ../../../third_party/skia/infra/bots/task_drivers/cifuzz/cifuzz.go -FILE: ../../../third_party/skia/infra/bots/task_drivers/cifuzz/cifuzz_test.go +FILE: ../../../third_party/skia/infra/bots/task_drivers/compile_wasm_gm_tests/compile_wasm_gm_tests.go FILE: ../../../third_party/skia/infra/bots/task_drivers/fm_driver/fm_driver.go FILE: ../../../third_party/skia/infra/bots/task_drivers/g3_canary/g3_canary.go FILE: ../../../third_party/skia/infra/bots/task_drivers/perf_puppeteer_canvas/perf_puppeteer_canvas.go @@ -6484,6 +6646,7 @@ FILE: ../../../third_party/skia/infra/bots/task_drivers/perf_puppeteer_render_sk FILE: ../../../third_party/skia/infra/bots/task_drivers/perf_puppeteer_skottie_frames/perf_puppeteer_skottie_frames.go FILE: ../../../third_party/skia/infra/bots/task_drivers/perf_puppeteer_skottie_frames/perf_puppeteer_skottie_frames_test.go FILE: ../../../third_party/skia/infra/bots/task_drivers/run_gn_to_bp/run_gn_to_bp.go +FILE: ../../../third_party/skia/infra/bots/task_drivers/run_wasm_gm_tests/run_wasm_gm_tests.go ---------------------------------------------------------------------------------------------------- Copyright 2020 The Chromium Authors. All rights reserved. @@ -6587,17 +6750,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/modules/canvaskit/canvaskit/LICENSE +ORIGIN: ../../../third_party/skia/modules/canvaskit/npm_build/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/example.html -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/extra.html -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/node.example.js -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/package.json -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/shaping.html -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/types/bin/canvaskit.d.ts -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/types/index.d.ts -FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/viewer.html +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/example.html +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/extra.html +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/node.example.js +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/package-lock.json +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/package.json +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/shaping.html +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/textapi_utils.js +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/types/canvaskit-wasm-tests.ts +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/types/index.d.ts +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/types/tsconfig.json +FILE: ../../../third_party/skia/modules/canvaskit/npm_build/types/tslint.json FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/example.html FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/package.json FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/example.html @@ -6718,7 +6883,6 @@ FILE: ../../../third_party/skia/src/core/SkCubicClipper.cpp FILE: ../../../third_party/skia/src/core/SkCubicClipper.h FILE: ../../../third_party/skia/src/core/SkEdgeClipper.cpp FILE: ../../../third_party/skia/src/core/SkEdgeClipper.h -FILE: ../../../third_party/skia/src/core/SkFontLCDConfig.cpp FILE: ../../../third_party/skia/src/core/SkQuadClipper.cpp FILE: ../../../third_party/skia/src/core/SkQuadClipper.h FILE: ../../../third_party/skia/src/images/SkImageEncoder.cpp @@ -7029,6 +7193,44 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: skia +ORIGIN: ../../../third_party/skia/src/effects/imagefilters/SkBlendImageFilter.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/src/effects/imagefilters/SkBlendImageFilter.cpp +FILE: ../../../third_party/skia/src/effects/imagefilters/SkPictureImageFilter.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2013 The Android Open Source Project + +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 the copyright holder 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: skia ORIGIN: ../../../third_party/skia/src/gpu/GrDistanceFieldGenFromVector.cpp + ../../../third_party/skia/LICENSE @@ -7069,9 +7271,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/src/gpu/ops/GrSmallPathRenderer.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/gpu/ops/SmallPathRenderer.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/SmallPathRenderer.cpp ---------------------------------------------------------------------------------------------------- Copyright 2014 Google Inc. Copyright 2017 ARM Ltd. @@ -7267,4 +7469,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -Total license count: 59 +Total license count: 65 diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 0278bb4a095b0..974febaa0485e 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f7f2dde13633b0c56c6d8b79c881dace +Signature: 6ed52edf364f1002cff8c616b68cabe1 UNUSED LICENSES: @@ -32,6 +32,31 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE +TYPE: LicenseType.mit +---------------------------------------------------------------------------------------------------- +Copyright (c) 2020 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + ==================================================================================================== ORIGIN: ../../../third_party/angle/util/windows/third_party/StackWalker/LICENSE TYPE: LicenseType.bsd @@ -67,6 +92,39 @@ 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. ==================================================================================================== +==================================================================================================== +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/LICENSE +TYPE: LicenseType.bsd +---------------------------------------------------------------------------------------------------- +Copyright 2006-2011, the V8 project authors. + +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 LLC 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. +==================================================================================================== + ==================================================================================================== ORIGIN: ../../../third_party/harfbuzz/src/ms-use/COPYING TYPE: LicenseType.mit @@ -193,384 +251,662 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== -LIBRARY: angle -LIBRARY: base -ORIGIN: ../../../third_party/angle/include/EGL/eglext_angle.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/EGL/eglext_angle.h -FILE: ../../../third_party/angle/include/GLES2/gl2ext_angle.h -FILE: ../../../third_party/angle/samples/multiview/Multiview.cpp -FILE: ../../../third_party/angle/src/common/PackedEnums.h -FILE: ../../../third_party/angle/src/common/aligned_memory.cpp -FILE: ../../../third_party/angle/src/common/aligned_memory.h -FILE: ../../../third_party/angle/src/common/aligned_memory_unittest.cpp -FILE: ../../../third_party/angle/src/common/angleutils_unittest.cpp -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/base_export.h -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/macros.h -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/sys_byteorder.h -FILE: ../../../third_party/angle/src/compiler/translator/Declarator.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Declarator.h -FILE: ../../../third_party/angle/src/compiler/translator/ExtensionBehavior.cpp -FILE: ../../../third_party/angle/src/compiler/translator/HashNames.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ImageFunctionHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ImageFunctionHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp -FILE: ../../../third_party/angle/src/compiler/translator/IsASTDepthBelowLimit.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputTree.h -FILE: ../../../third_party/angle/src/compiler/translator/StaticType.h -FILE: ../../../third_party/angle/src/compiler/translator/Symbol.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Symbol.h -FILE: ../../../third_party/angle/src/compiler/translator/SymbolUniqueId.cpp -FILE: ../../../third_party/angle/src/compiler/translator/SymbolUniqueId.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ClampFragDepth.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ClampFragDepth.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ClampPointSize.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ClampPointSize.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveArrayLengthMethod.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveArrayLengthMethod.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveUnreferencedVariables.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveUnreferencedVariables.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/VectorizeVectorScalarArithmetic.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/VectorizeVectorScalarArithmetic.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/WrapSwitchStatementsInBlocks.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/WrapSwitchStatementsInBlocks.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindMain.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindMain.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNode_util.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNode_util.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermTraverse.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RunAtTheEndOfShader.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RunAtTheEndOfShader.h -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_macos.mm -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/ErrorStrings.h -FILE: ../../../third_party/angle/src/libANGLE/LoggingAnnotator.cpp -FILE: ../../../third_party/angle/src/libANGLE/LoggingAnnotator.h -FILE: ../../../third_party/angle/src/libANGLE/MemoryProgramCache.cpp -FILE: ../../../third_party/angle/src/libANGLE/MemoryProgramCache.h -FILE: ../../../third_party/angle/src/libANGLE/ProgramLinkedResources.cpp -FILE: ../../../third_party/angle/src/libANGLE/ProgramLinkedResources.h -FILE: ../../../third_party/angle/src/libANGLE/ProgramPipeline.cpp -FILE: ../../../third_party/angle/src/libANGLE/ProgramPipeline.h -FILE: ../../../third_party/angle/src/libANGLE/ResourceMap.h -FILE: ../../../third_party/angle/src/libANGLE/SizedMRUCache.h -FILE: ../../../third_party/angle/src/libANGLE/SizedMRUCache_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/VertexArray_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramPipelineImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ProgramPipeline11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ProgramPipeline11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Clear11.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ClearMultiviewGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ClearMultiviewGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramPipelineGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramPipelineGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/egl_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/egl_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/SurfaceWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramPipelineNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramPipelineNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramPipelineVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramPipelineVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ResourceVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ResourceVk.h -FILE: ../../../third_party/angle/src/libGL/proc_table_wgl.h -FILE: ../../../third_party/angle/src/libGLESv2/proc_table_egl.h ----------------------------------------------------------------------------------------------------- -Copyright 2017 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -LIBRARY: base -ORIGIN: ../../../third_party/angle/include/platform/FrontendFeatures.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/platform/FrontendFeatures.h -FILE: ../../../third_party/angle/src/common/Color.h -FILE: ../../../third_party/angle/src/common/Color.inc -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/logging.h -FILE: ../../../third_party/angle/src/common/vector_utils.h -FILE: ../../../third_party/angle/src/common/vector_utils_unittest.cpp -FILE: ../../../third_party/angle/src/compiler/fuzz/translator_fuzzer.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ConstantUnion.cpp -FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/Severity.h -FILE: ../../../third_party/angle/src/compiler/translator/TextureFunctionHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/TextureFunctionHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorVulkan.cpp -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorVulkan.h -FILE: ../../../third_party/angle/src/compiler/translator/ValidateMaxParameters.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateMaxParameters.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/AddAndTrueToLoopCondition.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/AddAndTrueToLoopCondition.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/AddDefaultReturnStatements.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/AddDefaultReturnStatements.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/BreakVariableAliasingInInnerLoops.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/BreakVariableAliasingInInnerLoops.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeferGlobalInitializers.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ExpandIntegerPowExpressions.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ExpandIntegerPowExpressions.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveInvariantDeclaration.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveInvariantDeclaration.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteTexelFetchOffset.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteTexelFetchOffset.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteUnaryMinusOperatorFloat.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteUnaryMinusOperatorFloat.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteUnaryMinusOperatorInt.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteUnaryMinusOperatorInt.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SimplifyLoopConditions.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SimplifyLoopConditions.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SplitSequenceOperator.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SplitSequenceOperator.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/UseInterfaceBlockFields.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/UseInterfaceBlockFields.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNodePatternMatcher.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNodePatternMatcher.h -FILE: ../../../third_party/angle/src/image_util/imageformats.cpp -FILE: ../../../third_party/angle/src/libANGLE/Observer.h -FILE: ../../../third_party/angle/src/libANGLE/Observer_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/Stream.cpp -FILE: ../../../third_party/angle/src/libANGLE/Stream.h -FILE: ../../../third_party/angle/src/libANGLE/Thread.cpp -FILE: ../../../third_party/angle/src/libANGLE/Thread.h -FILE: ../../../third_party/angle/src/libANGLE/VaryingPacking_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/WorkerThread.cpp -FILE: ../../../third_party/angle/src/libANGLE/WorkerThread.h -FILE: ../../../third_party/angle/src/libANGLE/WorkerThread_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/queryutils.cpp -FILE: ../../../third_party/angle/src/libANGLE/queryutils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ContextImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/ContextImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLImplFactory.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/Format.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/FramebufferAttachmentObjectImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/StreamProducerImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/TextureImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/HLSLCompiler.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/NativeWindowD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/NativeWindowD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SwapChainD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Context11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Context11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/NativeWindow11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerD3DTexture.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerD3DTexture.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Context9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Context9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/NativeWindow9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/NativeWindow9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/driver_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/driver_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ContextGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ContextGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DisplayEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DisplayEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGLDL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGLDL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/PbufferSurfaceEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/PbufferSurfaceEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SurfaceEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SurfaceEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/WindowSurfaceEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/WindowSurfaceEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/DisplayAndroid.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/DisplayAndroid.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/functionsegl_typedefs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/DisplayGbm.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/DisplayGbm.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/SurfaceGbm.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/SurfaceGbm.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/SurfaceGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/BufferNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/BufferNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/CompilerNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/CompilerNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ContextNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ContextNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DeviceNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DeviceNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DisplayNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DisplayNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FenceNVNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FenceNVNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FramebufferNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FramebufferNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ImageNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ImageNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/QueryNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/QueryNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/RenderbufferNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/RenderbufferNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SamplerNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SamplerNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ShaderNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ShaderNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SurfaceNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SurfaceNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SyncNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SyncNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TextureNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TextureNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TransformFeedbackNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TransformFeedbackNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/VertexArrayNULL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/null/VertexArrayNULL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/renderer_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/renderer_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/BufferVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/BufferVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CompilerVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CompilerVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DeviceVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DeviceVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DisplayVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DisplayVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FenceNVVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FenceNVVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FramebufferVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FramebufferVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/GlslangWrapperVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/GlslangWrapperVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ImageVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ImageVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/QueryVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/QueryVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderTargetVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderbufferVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RendererVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RendererVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SamplerVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SamplerVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShaderVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShaderVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SurfaceVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SurfaceVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SyncVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SyncVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TextureVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TextureVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TransformFeedbackVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TransformFeedbackVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/VertexArrayVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_headers.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/DisplayVkWin32.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/DisplayVkWin32.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/WindowSurfaceVkWin32.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/WindowSurfaceVkWin32.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/DisplayVkXcb.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/DisplayVkXcb.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/WindowSurfaceVkXcb.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/WindowSurfaceVkXcb.h -FILE: ../../../third_party/angle/src/libANGLE/validationEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationES31.cpp -FILE: ../../../third_party/angle/util/android/AndroidPixmap.cpp -FILE: ../../../third_party/angle/util/android/AndroidWindow.cpp -FILE: ../../../third_party/angle/util/android/AndroidWindow.h -FILE: ../../../third_party/angle/util/ozone/OzonePixmap.cpp -FILE: ../../../third_party/angle/util/ozone/OzoneWindow.cpp -FILE: ../../../third_party/angle/util/ozone/OzoneWindow.h ----------------------------------------------------------------------------------------------------- -Copyright 2016 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: abseil-cpp LIBRARY: angle LIBRARY: boringssl LIBRARY: khronos +LIBRARY: libwebp LIBRARY: vulkan +LIBRARY: vulkan-deps LIBRARY: wuffs ORIGIN: ../../../flutter/third_party/txt/LICENSE TYPE: LicenseType.apache +FILE: ../../../third_party/abseil-cpp/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/CMake/install_test_project/simple.cc +FILE: ../../../third_party/abseil-cpp/WORKSPACE +FILE: ../../../third_party/abseil-cpp/absl/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/algorithm/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/algorithm/algorithm.h +FILE: ../../../third_party/abseil-cpp/absl/algorithm/algorithm_test.cc +FILE: ../../../third_party/abseil-cpp/absl/algorithm/container.h +FILE: ../../../third_party/abseil-cpp/absl/algorithm/container_test.cc +FILE: ../../../third_party/abseil-cpp/absl/algorithm/equal_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/base/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/base/attributes.h +FILE: ../../../third_party/abseil-cpp/absl/base/bit_cast_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/call_once.h +FILE: ../../../third_party/abseil-cpp/absl/base/call_once_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/casts.h +FILE: ../../../third_party/abseil-cpp/absl/base/config.h +FILE: ../../../third_party/abseil-cpp/absl/base/config_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/const_init.h +FILE: ../../../third_party/abseil-cpp/absl/base/dynamic_annotations.h +FILE: ../../../third_party/abseil-cpp/absl/base/exception_safety_testing_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/inline_variable_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/inline_variable_test_a.cc +FILE: ../../../third_party/abseil-cpp/absl/base/inline_variable_test_b.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/atomic_hook.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/atomic_hook_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/atomic_hook_test_helper.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/atomic_hook_test_helper.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/bits.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/bits_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/cmake_thread_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/cycleclock.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/cycleclock.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/direct_mmap.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/endian.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/endian_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/errno_saver.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/errno_saver_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/exception_safety_testing.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/exception_safety_testing.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/exception_testing.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/exponential_biased.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/exponential_biased.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/exponential_biased_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/fast_type_id.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/fast_type_id_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/hide_ptr.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/identity.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/inline_variable.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/inline_variable_testing.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/invoke.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/low_level_alloc.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/low_level_alloc_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/per_thread_tls.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/periodic_sampler.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/periodic_sampler.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/periodic_sampler_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/periodic_sampler_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/pretty_function.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/raw_logging.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/raw_logging.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/scheduling_mode.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/scoped_set_env.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/scoped_set_env.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/scoped_set_env_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_wait.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_wait.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/strerror.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/strerror.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/strerror_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/strerror_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/sysinfo.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/sysinfo.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/sysinfo_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/thread_identity.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/thread_identity.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/thread_identity_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/thread_identity_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/throw_delegate.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/throw_delegate.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/unaligned_access.h +FILE: ../../../third_party/abseil-cpp/absl/base/internal/unique_small_name_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc +FILE: ../../../third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h +FILE: ../../../third_party/abseil-cpp/absl/base/invoke_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/log_severity.cc +FILE: ../../../third_party/abseil-cpp/absl/base/log_severity.h +FILE: ../../../third_party/abseil-cpp/absl/base/log_severity_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/macros.h +FILE: ../../../third_party/abseil-cpp/absl/base/optimization.h +FILE: ../../../third_party/abseil-cpp/absl/base/optimization_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/options.h +FILE: ../../../third_party/abseil-cpp/absl/base/policy_checks.h +FILE: ../../../third_party/abseil-cpp/absl/base/port.h +FILE: ../../../third_party/abseil-cpp/absl/base/raw_logging_test.cc +FILE: ../../../third_party/abseil-cpp/absl/base/spinlock_test_common.cc +FILE: ../../../third_party/abseil-cpp/absl/base/thread_annotations.h +FILE: ../../../third_party/abseil-cpp/absl/base/throw_delegate_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/container/btree_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/container/btree_map.h +FILE: ../../../third_party/abseil-cpp/absl/container/btree_set.h +FILE: ../../../third_party/abseil-cpp/absl/container/btree_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/btree_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/fixed_array.h +FILE: ../../../third_party/abseil-cpp/absl/container/fixed_array_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/container/fixed_array_exception_safety_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/fixed_array_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/flat_hash_map.h +FILE: ../../../third_party/abseil-cpp/absl/container/flat_hash_map_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/flat_hash_set.h +FILE: ../../../third_party/abseil-cpp/absl/container/flat_hash_set_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/inlined_vector.h +FILE: ../../../third_party/abseil-cpp/absl/container/inlined_vector_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/container/inlined_vector_exception_safety_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/inlined_vector_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/btree.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/btree_container.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/common.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/compressed_tuple.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/container_memory.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/container_memory_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/counting_allocator.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_generator_testing.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_generator_testing.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_policy_testing.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_policy_testing_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hash_policy_traits_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hashtable_debug.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hashtable_debug_hooks.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/have_sse.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/inlined_vector.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/layout.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/layout_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/node_hash_policy.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/node_hash_policy_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/raw_hash_map.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/raw_hash_set.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/test_instance_tracker.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/test_instance_tracker.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/test_instance_tracker_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/tracked.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_map_lookup_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_map_members_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_map_modifiers_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_map_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_set_constructor_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_set_lookup_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_set_members_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h +FILE: ../../../third_party/abseil-cpp/absl/container/internal/unordered_set_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/node_hash_map.h +FILE: ../../../third_party/abseil-cpp/absl/container/node_hash_map_test.cc +FILE: ../../../third_party/abseil-cpp/absl/container/node_hash_set.h +FILE: ../../../third_party/abseil-cpp/absl/container/node_hash_set_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/failure_signal_handler.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/failure_signal_handler_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/address_is_readable.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/demangle.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/demangle.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/examine_stack.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stack_consumption.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stack_consumption.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stack_consumption_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/symbolize.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/vdso_support.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/leak_check.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/leak_check.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/leak_check_disable.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/leak_check_fail_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/leak_check_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/stacktrace.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/stacktrace.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize.h +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize_darwin.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize_elf.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize_test.cc +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize_unimplemented.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/symbolize_win32.inc +FILE: ../../../third_party/abseil-cpp/absl/flags/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/flags/commandlineflag.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/commandlineflag.h +FILE: ../../../third_party/abseil-cpp/absl/flags/commandlineflag_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/config.h +FILE: ../../../third_party/abseil-cpp/absl/flags/config_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/declare.h +FILE: ../../../third_party/abseil-cpp/absl/flags/flag.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/flag.h +FILE: ../../../third_party/abseil-cpp/absl/flags/flag_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/flag_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/flag_test_defs.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/commandlineflag.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/commandlineflag.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/flag.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/flag.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/parse.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/path_util.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/path_util_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/program_name.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/program_name.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/program_name_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/registry.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/usage.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/usage.h +FILE: ../../../third_party/abseil-cpp/absl/flags/internal/usage_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/marshalling.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/marshalling.h +FILE: ../../../third_party/abseil-cpp/absl/flags/marshalling_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/parse.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/parse.h +FILE: ../../../third_party/abseil-cpp/absl/flags/parse_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/reflection.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/reflection.h +FILE: ../../../third_party/abseil-cpp/absl/flags/reflection_test.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/usage.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/usage.h +FILE: ../../../third_party/abseil-cpp/absl/flags/usage_config.cc +FILE: ../../../third_party/abseil-cpp/absl/flags/usage_config.h +FILE: ../../../third_party/abseil-cpp/absl/flags/usage_config_test.cc +FILE: ../../../third_party/abseil-cpp/absl/functional/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/functional/bind_front.h +FILE: ../../../third_party/abseil-cpp/absl/functional/bind_front_test.cc +FILE: ../../../third_party/abseil-cpp/absl/functional/function_ref.h +FILE: ../../../third_party/abseil-cpp/absl/functional/function_ref_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/functional/function_ref_test.cc +FILE: ../../../third_party/abseil-cpp/absl/functional/internal/front_binder.h +FILE: ../../../third_party/abseil-cpp/absl/functional/internal/function_ref.h +FILE: ../../../third_party/abseil-cpp/absl/hash/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/hash/hash.h +FILE: ../../../third_party/abseil-cpp/absl/hash/hash_test.cc +FILE: ../../../third_party/abseil-cpp/absl/hash/hash_testing.h +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/city.cc +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/city.h +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/city_test.cc +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/hash.cc +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/hash.h +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/print_hash_of.cc +FILE: ../../../third_party/abseil-cpp/absl/hash/internal/spy_hash_state.h +FILE: ../../../third_party/abseil-cpp/absl/memory/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/memory/memory.h +FILE: ../../../third_party/abseil-cpp/absl/memory/memory_exception_safety_test.cc +FILE: ../../../third_party/abseil-cpp/absl/memory/memory_test.cc +FILE: ../../../third_party/abseil-cpp/absl/meta/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/meta/type_traits.h +FILE: ../../../third_party/abseil-cpp/absl/meta/type_traits_test.cc +FILE: ../../../third_party/abseil-cpp/absl/numeric/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128.cc +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128.h +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128_stream_test.cc +FILE: ../../../third_party/abseil-cpp/absl/numeric/int128_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/random/benchmarks.cc +FILE: ../../../third_party/abseil-cpp/absl/random/bernoulli_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/bernoulli_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/beta_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/beta_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/bit_gen_ref.h +FILE: ../../../third_party/abseil-cpp/absl/random/bit_gen_ref_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/discrete_distribution.cc +FILE: ../../../third_party/abseil-cpp/absl/random/discrete_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/discrete_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/distributions.h +FILE: ../../../third_party/abseil-cpp/absl/random/distributions_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/examples_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/exponential_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/exponential_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/gaussian_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/gaussian_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/generators_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/random/internal/chi_square.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/chi_square.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/chi_square_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/distribution_caller.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/distribution_test_util.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/distribution_test_util.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/distribution_test_util_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/explicit_seed_seq.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/explicit_seed_seq_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/fast_uniform_bits_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/fastmath.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/fastmath_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/gaussian_distribution_gentables.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/generate_real.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/generate_real_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/iostream_state_saver_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/mock_helpers.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/mock_overload_set.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/nanobenchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/nanobenchmark.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/nanobenchmark_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/nonsecure_base.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/nonsecure_base_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/pcg_engine.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/pcg_engine_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/platform.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/pool_urbg.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/pool_urbg.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/pool_urbg_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_benchmarks.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_detect.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_detect.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_engine.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_engine_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_hwaes.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_hwaes_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_round_keys.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_slow.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_slow.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_slow_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/randen_traits.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/salted_seed_seq.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/salted_seed_seq_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/seed_material.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/seed_material.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/seed_material_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/sequence_urbg.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/traits.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/traits_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/uniform_helper.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/uniform_helper_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/internal/wide_multiply.h +FILE: ../../../third_party/abseil-cpp/absl/random/internal/wide_multiply_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/log_uniform_int_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/mock_distributions.h +FILE: ../../../third_party/abseil-cpp/absl/random/mock_distributions_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/mocking_bit_gen.h +FILE: ../../../third_party/abseil-cpp/absl/random/mocking_bit_gen_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/poisson_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/poisson_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/random.h +FILE: ../../../third_party/abseil-cpp/absl/random/seed_gen_exception.cc +FILE: ../../../third_party/abseil-cpp/absl/random/seed_gen_exception.h +FILE: ../../../third_party/abseil-cpp/absl/random/seed_sequences.cc +FILE: ../../../third_party/abseil-cpp/absl/random/seed_sequences.h +FILE: ../../../third_party/abseil-cpp/absl/random/seed_sequences_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/uniform_int_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/uniform_int_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/uniform_real_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/uniform_real_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/random/zipf_distribution.h +FILE: ../../../third_party/abseil-cpp/absl/random/zipf_distribution_test.cc +FILE: ../../../third_party/abseil-cpp/absl/status/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/status/internal/status_internal.h +FILE: ../../../third_party/abseil-cpp/absl/status/internal/statusor_internal.h +FILE: ../../../third_party/abseil-cpp/absl/status/status.cc +FILE: ../../../third_party/abseil-cpp/absl/status/status.h +FILE: ../../../third_party/abseil-cpp/absl/status/status_payload_printer.cc +FILE: ../../../third_party/abseil-cpp/absl/status/status_payload_printer.h +FILE: ../../../third_party/abseil-cpp/absl/status/status_test.cc +FILE: ../../../third_party/abseil-cpp/absl/status/statusor.cc +FILE: ../../../third_party/abseil-cpp/absl/status/statusor.h +FILE: ../../../third_party/abseil-cpp/absl/status/statusor_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/strings/ascii.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/ascii.h +FILE: ../../../third_party/abseil-cpp/absl/strings/ascii_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/ascii_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/charconv.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/charconv.h +FILE: ../../../third_party/abseil-cpp/absl/strings/charconv_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/charconv_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/cord.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/cord.h +FILE: ../../../third_party/abseil-cpp/absl/strings/cord_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/cord_test_helpers.h +FILE: ../../../third_party/abseil-cpp/absl/strings/escaping.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/escaping.h +FILE: ../../../third_party/abseil-cpp/absl/strings/escaping_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/escaping_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/char_map.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/char_map_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/char_map_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/charconv_bigint_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/charconv_parse.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/charconv_parse_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/cord_internal.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/escaping.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/escaping.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/escaping_test_common.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/memutil.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/memutil.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/memutil_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/memutil_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/numbers_test_common.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/ostringstream.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/ostringstream.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/ostringstream_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/ostringstream_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/pow10_helper.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/pow10_helper.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/pow10_helper_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/resize_uninitialized_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/stl_type_traits.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/arg.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/arg_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/bind.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/bind_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/checker.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/checker_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/extension.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/extension_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/output.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/output.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/output_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/parser.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_format/parser_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_join_internal.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/str_split_internal.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/string_constant.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/string_constant_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/utf8.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/utf8.h +FILE: ../../../third_party/abseil-cpp/absl/strings/internal/utf8_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/match.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/match.h +FILE: ../../../third_party/abseil-cpp/absl/strings/match_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/numbers.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/numbers.h +FILE: ../../../third_party/abseil-cpp/absl/strings/numbers_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/numbers_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_cat.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_cat.h +FILE: ../../../third_party/abseil-cpp/absl/strings/str_cat_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_cat_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_format.h +FILE: ../../../third_party/abseil-cpp/absl/strings/str_format_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_join.h +FILE: ../../../third_party/abseil-cpp/absl/strings/str_join_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_join_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_replace.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_replace.h +FILE: ../../../third_party/abseil-cpp/absl/strings/str_replace_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_replace_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_split.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_split.h +FILE: ../../../third_party/abseil-cpp/absl/strings/str_split_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/str_split_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/string_view.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/string_view.h +FILE: ../../../third_party/abseil-cpp/absl/strings/string_view_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/string_view_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/strip.h +FILE: ../../../third_party/abseil-cpp/absl/strings/strip_test.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/substitute.cc +FILE: ../../../third_party/abseil-cpp/absl/strings/substitute.h +FILE: ../../../third_party/abseil-cpp/absl/strings/substitute_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/synchronization/barrier.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/barrier.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/barrier_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/blocking_counter.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/blocking_counter.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/blocking_counter_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/futex.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/graphcycles.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/graphcycles_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/graphcycles_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/thread_pool.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/waiter.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/internal/waiter.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/lifetime_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/mutex.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/mutex.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/mutex_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/mutex_test.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/notification.cc +FILE: ../../../third_party/abseil-cpp/absl/synchronization/notification.h +FILE: ../../../third_party/abseil-cpp/absl/synchronization/notification_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/time/civil_time.cc +FILE: ../../../third_party/abseil-cpp/absl/time/civil_time.h +FILE: ../../../third_party/abseil-cpp/absl/time/civil_time_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/time/civil_time_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/clock.cc +FILE: ../../../third_party/abseil-cpp/absl/time/clock.h +FILE: ../../../third_party/abseil-cpp/absl/time/clock_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/time/clock_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/duration.cc +FILE: ../../../third_party/abseil-cpp/absl/time/duration_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/time/duration_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/format.cc +FILE: ../../../third_party/abseil-cpp/absl/time/format_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/time/format_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_detail.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/get_current_time_chrono.inc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/test_util.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/test_util.h +FILE: ../../../third_party/abseil-cpp/absl/time/time.cc +FILE: ../../../third_party/abseil-cpp/absl/time/time.h +FILE: ../../../third_party/abseil-cpp/absl/time/time_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/time/time_test.cc +FILE: ../../../third_party/abseil-cpp/absl/time/time_zone_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/types/any.h +FILE: ../../../third_party/abseil-cpp/absl/types/any_exception_safety_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/any_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/bad_any_cast.cc +FILE: ../../../third_party/abseil-cpp/absl/types/bad_any_cast.h +FILE: ../../../third_party/abseil-cpp/absl/types/bad_optional_access.cc +FILE: ../../../third_party/abseil-cpp/absl/types/bad_optional_access.h +FILE: ../../../third_party/abseil-cpp/absl/types/bad_variant_access.cc +FILE: ../../../third_party/abseil-cpp/absl/types/bad_variant_access.h +FILE: ../../../third_party/abseil-cpp/absl/types/compare.h +FILE: ../../../third_party/abseil-cpp/absl/types/compare_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/internal/conformance_aliases.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/conformance_archetype.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/conformance_profile.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/conformance_testing.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/conformance_testing_helpers.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/conformance_testing_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/internal/optional.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/parentheses.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/span.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/transform_args.h +FILE: ../../../third_party/abseil-cpp/absl/types/internal/variant.h +FILE: ../../../third_party/abseil-cpp/absl/types/optional.h +FILE: ../../../third_party/abseil-cpp/absl/types/optional_exception_safety_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/optional_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/span.h +FILE: ../../../third_party/abseil-cpp/absl/types/span_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/variant.h +FILE: ../../../third_party/abseil-cpp/absl/types/variant_benchmark.cc +FILE: ../../../third_party/abseil-cpp/absl/types/variant_exception_safety_test.cc +FILE: ../../../third_party/abseil-cpp/absl/types/variant_test.cc +FILE: ../../../third_party/abseil-cpp/absl/utility/BUILD.bazel +FILE: ../../../third_party/abseil-cpp/absl/utility/utility.h +FILE: ../../../third_party/abseil-cpp/absl/utility/utility_test.cc +FILE: ../../../third_party/abseil-cpp/ci/absl_alternate_options.h +FILE: ../../../third_party/angle/include/CL/cl.h +FILE: ../../../third_party/angle/include/CL/cl_d3d10.h +FILE: ../../../third_party/angle/include/CL/cl_d3d11.h +FILE: ../../../third_party/angle/include/CL/cl_dx9_media_sharing.h +FILE: ../../../third_party/angle/include/CL/cl_dx9_media_sharing_intel.h +FILE: ../../../third_party/angle/include/CL/cl_egl.h +FILE: ../../../third_party/angle/include/CL/cl_ext.h +FILE: ../../../third_party/angle/include/CL/cl_ext_intel.h +FILE: ../../../third_party/angle/include/CL/cl_gl.h +FILE: ../../../third_party/angle/include/CL/cl_gl_ext.h +FILE: ../../../third_party/angle/include/CL/cl_half.h +FILE: ../../../third_party/angle/include/CL/cl_icd.h +FILE: ../../../third_party/angle/include/CL/cl_layer.h +FILE: ../../../third_party/angle/include/CL/cl_platform.h +FILE: ../../../third_party/angle/include/CL/cl_va_api_media_sharing_intel.h +FILE: ../../../third_party/angle/include/CL/cl_version.h +FILE: ../../../third_party/angle/include/CL/opencl.h +FILE: ../../../third_party/angle/include/EGL/egl.h +FILE: ../../../third_party/angle/include/EGL/eglext.h +FILE: ../../../third_party/angle/include/EGL/eglplatform.h FILE: ../../../third_party/angle/include/GLES/egl.h FILE: ../../../third_party/angle/include/GLES/glplatform.h +FILE: ../../../third_party/angle/include/GLES2/gl2platform.h +FILE: ../../../third_party/angle/include/GLES3/gl3platform.h +FILE: ../../../third_party/angle/scripts/cl.xml +FILE: ../../../third_party/angle/scripts/egl.xml FILE: ../../../third_party/angle/scripts/gl.xml FILE: ../../../third_party/angle/scripts/wgl.xml FILE: ../../../third_party/angle/src/android_system_settings/res/layout/fragment.xml @@ -671,11 +1007,39 @@ FILE: ../../../third_party/angle/src/android_system_settings/src/com/android/ang FILE: ../../../third_party/angle/src/android_system_settings/src/com/android/angle/common/MainFragment.java FILE: ../../../third_party/angle/src/android_system_settings/src/com/android/angle/common/Receiver.java FILE: ../../../third_party/angle/src/android_system_settings/src/com/android/angle/common/SearchProvider.java -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_ext_provoking_vertex.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_google_filtering_precision.h +FILE: ../../../third_party/angle/src/common/vulkan/vk_ext_provoking_vertex.h +FILE: ../../../third_party/angle/src/common/vulkan/vk_google_filtering_precision.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/cmake/cmake_uninstall.cmake.in +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vk_icd.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vk_layer.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vk_platform.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vk_sdk_platform.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan.hpp +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_android.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_beta.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_core.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_directfb.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_fuchsia.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_ggp.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_ios.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_macos.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_metal.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_vi.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_wayland.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_win32.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_xcb.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_xlib.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/include/vulkan/vulkan_xlib_xrandr.h +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/registry/validusage.json +FILE: ../../../third_party/angle/third_party/vulkan-deps/vulkan-headers/src/registry/vk.xml FILE: ../../../third_party/angle/util/android/third_party/android_native_app_glue.c FILE: ../../../third_party/angle/util/android/third_party/android_native_app_glue.h FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/METADATA +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aead_aes_siv_cmac_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aegis128L_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aegis128_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aegis256_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_cbc_pkcs5_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_ccm_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_cmac_test.json @@ -684,6 +1048,14 @@ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_gcm_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_siv_cmac_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/chacha20_poly1305_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_2048_224_sha224_p1363_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_2048_224_sha224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_2048_224_sha256_p1363_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_2048_224_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_2048_256_sha256_p1363_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_2048_256_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_3072_256_sha256_p1363_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_3072_256_sha256_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/dsa_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_brainpoolP224r1_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_brainpoolP256r1_test.json @@ -701,45 +1073,133 @@ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp521r1_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_webcrypto_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP224r1_sha224_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP224r1_sha224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP256r1_sha256_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP256r1_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP320r1_sha384_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP320r1_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP384r1_sha384_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP384r1_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP512r1_sha512_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_brainpoolP512r1_sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha224_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha256_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha3_224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha3_256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha512_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp224r1_sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256k1_sha256_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256k1_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256k1_sha3_256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256k1_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256k1_sha512_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256k1_sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256r1_sha256_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256r1_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256r1_sha3_256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256r1_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256r1_sha512_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp256r1_sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp384r1_sha384_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp384r1_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp384r1_sha3_384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp384r1_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp384r1_sha512_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp384r1_sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp521r1_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp521r1_sha512_p1363_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp521r1_sha512_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_webcrypto_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ed448_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/eddsa_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/gmac_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hkdf_sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hkdf_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hkdf_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hkdf_sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha3_224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha3_256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha3_384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/hmac_sha512_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/kw_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/kwp_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/primality_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha1_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha224_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha224_mgf1sha224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha256_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha256_mgf1sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha384_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha384_mgf1sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha512_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_2048_sha512_mgf1sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_3072_sha256_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_3072_sha256_mgf1sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_3072_sha512_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_3072_sha512_mgf1sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_4096_sha256_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_4096_sha256_mgf1sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_4096_sha512_mgf1sha1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_4096_sha512_mgf1sha512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_oaep_misc_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pkcs1_2048_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pkcs1_3072_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pkcs1_4096_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_2048_sha1_mgf1_20_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_2048_sha256_mgf1_0_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_2048_sha256_mgf1_32_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_2048_sha512_256_mgf1_28_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_2048_sha512_256_mgf1_32_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_3072_sha256_mgf1_32_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_4096_sha256_mgf1_32_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_4096_sha512_mgf1_32_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_pss_misc_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_sig_gen_misc_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha224_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha3_224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha3_256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha3_384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha512_224_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha512_256_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_2048_sha512_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha256_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha3_256_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha3_384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha3_512_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha512_256_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_3072_sha512_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_4096_sha384_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_4096_sha512_256_test.json 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/vmac_128_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/vmac_64_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x25519_asn_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x25519_jwk_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x25519_pem_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x25519_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x448_asn_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x448_jwk_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x448_pem_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x448_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/xchacha20_poly1305_test.json FILE: ../../../third_party/khronos/GLES2/gl2platform.h FILE: ../../../third_party/khronos/GLES3/gl3platform.h +FILE: ../../../third_party/libwebp/gradlew FILE: ../../../third_party/vulkan/include/vulkan/vk_platform.h FILE: ../../../third_party/vulkan/include/vulkan/vulkan.h FILE: ../../../third_party/vulkan/include/vulkan/vulkan_android.h @@ -767,27 +1227,41 @@ FILE: ../../../third_party/wuffs/cmd/wuffs/main.go FILE: ../../../third_party/wuffs/cmd/wuffs/release.go FILE: ../../../third_party/wuffs/cmd/wuffs/test.go FILE: ../../../third_party/wuffs/cmd/wuffsfmt/main.go +FILE: ../../../third_party/wuffs/example/cbor-to-json/cbor-to-json.cc FILE: ../../../third_party/wuffs/example/convert-to-nia/convert-to-nia.c FILE: ../../../third_party/wuffs/example/crc32/crc32.cc FILE: ../../../third_party/wuffs/example/gifplayer/gifplayer.c FILE: ../../../third_party/wuffs/example/imageviewer/imageviewer.c +FILE: ../../../third_party/wuffs/example/json-to-cbor/json-to-cbor.cc FILE: ../../../third_party/wuffs/example/jsonfindptrs/jsonfindptrs.cc FILE: ../../../third_party/wuffs/example/jsonptr/jsonptr.cc -FILE: ../../../third_party/wuffs/example/library/library.c +FILE: ../../../third_party/wuffs/example/toy-genlib/toy-genlib.c FILE: ../../../third_party/wuffs/example/zcat/zcat.c FILE: ../../../third_party/wuffs/fuzz/c/fuzzlib/fuzzlib.c +FILE: ../../../third_party/wuffs/fuzz/c/fuzzlib/fuzzlib_image_decoder.c +FILE: ../../../third_party/wuffs/fuzz/c/std/bmp_fuzzer.c +FILE: ../../../third_party/wuffs/fuzz/c/std/cbor_fuzzer.c FILE: ../../../third_party/wuffs/fuzz/c/std/gif_fuzzer.c FILE: ../../../third_party/wuffs/fuzz/c/std/json_fuzzer.c +FILE: ../../../third_party/wuffs/fuzz/c/std/png_fuzzer.c FILE: ../../../third_party/wuffs/fuzz/c/std/zlib_fuzzer.c FILE: ../../../third_party/wuffs/hello-wuffs-c/main.c FILE: ../../../third_party/wuffs/hello-wuffs-c/parse.wuffs FILE: ../../../third_party/wuffs/hello-wuffs-c/wuffs-base.c +FILE: ../../../third_party/wuffs/internal/cgen/auxiliary/base.cc +FILE: ../../../third_party/wuffs/internal/cgen/auxiliary/base.hh +FILE: ../../../third_party/wuffs/internal/cgen/auxiliary/cbor.cc +FILE: ../../../third_party/wuffs/internal/cgen/auxiliary/cbor.hh +FILE: ../../../third_party/wuffs/internal/cgen/auxiliary/json.cc +FILE: ../../../third_party/wuffs/internal/cgen/auxiliary/json.hh FILE: ../../../third_party/wuffs/internal/cgen/base/all-impl.c -FILE: ../../../third_party/wuffs/internal/cgen/base/f64conv-submodule.c +FILE: ../../../third_party/wuffs/internal/cgen/base/floatconv-submodule-code.c +FILE: ../../../third_party/wuffs/internal/cgen/base/floatconv-submodule-data.c FILE: ../../../third_party/wuffs/internal/cgen/base/fundamental-private.h FILE: ../../../third_party/wuffs/internal/cgen/base/fundamental-public.h FILE: ../../../third_party/wuffs/internal/cgen/base/image-private.h FILE: ../../../third_party/wuffs/internal/cgen/base/image-public.h +FILE: ../../../third_party/wuffs/internal/cgen/base/intconv-submodule.c FILE: ../../../third_party/wuffs/internal/cgen/base/io-private.h FILE: ../../../third_party/wuffs/internal/cgen/base/io-public.h FILE: ../../../third_party/wuffs/internal/cgen/base/memory-private.h @@ -795,11 +1269,11 @@ FILE: ../../../third_party/wuffs/internal/cgen/base/memory-public.h FILE: ../../../third_party/wuffs/internal/cgen/base/pixconv-submodule.c FILE: ../../../third_party/wuffs/internal/cgen/base/range-private.h FILE: ../../../third_party/wuffs/internal/cgen/base/range-public.h -FILE: ../../../third_party/wuffs/internal/cgen/base/strconv-impl.c FILE: ../../../third_party/wuffs/internal/cgen/base/strconv-private.h FILE: ../../../third_party/wuffs/internal/cgen/base/strconv-public.h FILE: ../../../third_party/wuffs/internal/cgen/base/token-private.h FILE: ../../../third_party/wuffs/internal/cgen/base/token-public.h +FILE: ../../../third_party/wuffs/internal/cgen/base/utf8-submodule.c FILE: ../../../third_party/wuffs/internal/cgen/builtin.go FILE: ../../../third_party/wuffs/internal/cgen/cgen.go FILE: ../../../third_party/wuffs/internal/cgen/data/data.go @@ -890,11 +1364,13 @@ FILE: ../../../third_party/wuffs/script/compress-giflzw.go FILE: ../../../third_party/wuffs/script/convert-png-to-wbmp.go FILE: ../../../third_party/wuffs/script/crawl.go FILE: ../../../third_party/wuffs/script/draw-with-mask.go +FILE: ../../../third_party/wuffs/script/extract-cbor-rfc-7049-examples.go FILE: ../../../third_party/wuffs/script/extract-deflate-offsets.go FILE: ../../../third_party/wuffs/script/extract-giflzw.go FILE: ../../../third_party/wuffs/script/extract-palette-indexes.go FILE: ../../../third_party/wuffs/script/inline-c-relative-includes.go FILE: ../../../third_party/wuffs/script/make-artificial.go +FILE: ../../../third_party/wuffs/script/manual-test-parse-number-f64.cc FILE: ../../../third_party/wuffs/script/mmap-ring-buffer.c FILE: ../../../third_party/wuffs/script/preprocess-wuffs.go FILE: ../../../third_party/wuffs/script/print-bits.go @@ -905,21 +1381,24 @@ FILE: ../../../third_party/wuffs/script/print-deflate-huff-table-size.go FILE: ../../../third_party/wuffs/script/print-deflate-magic-numbers.go FILE: ../../../third_party/wuffs/script/print-file-sizes-json.go FILE: ../../../third_party/wuffs/script/print-hpd-left-shift.go +FILE: ../../../third_party/wuffs/script/print-json-ascii-escapes.go FILE: ../../../third_party/wuffs/script/print-json-token-debug-format.c FILE: ../../../third_party/wuffs/script/print-lzw-example.go FILE: ../../../third_party/wuffs/script/print-markdown-links.go FILE: ../../../third_party/wuffs/script/print-mpb-powers-of-10.go +FILE: ../../../third_party/wuffs/script/print-render-number-f64-tests.go +FILE: ../../../third_party/wuffs/script/process-json-numbers.c FILE: ../../../third_party/wuffs/script/rac-random-seek-test.go FILE: ../../../third_party/wuffs/script/wuffs-deflate-decoder-decode-huffman.c FILE: ../../../third_party/wuffs/std/adler32/common_adler32.wuffs FILE: ../../../third_party/wuffs/std/bmp/decode_bmp.wuffs +FILE: ../../../third_party/wuffs/std/cbor/decode_cbor.wuffs FILE: ../../../third_party/wuffs/std/crc32/common_crc32.wuffs FILE: ../../../third_party/wuffs/std/deflate/common_consts.wuffs FILE: ../../../third_party/wuffs/std/deflate/decode_deflate.wuffs FILE: ../../../third_party/wuffs/std/deflate/decode_huffman_fast.wuffs FILE: ../../../third_party/wuffs/std/deflate/decode_huffman_slow.wuffs FILE: ../../../third_party/wuffs/std/gif/common_consts.wuffs -FILE: ../../../third_party/wuffs/std/gif/decode_config.wuffs FILE: ../../../third_party/wuffs/std/gif/decode_gif.wuffs FILE: ../../../third_party/wuffs/std/gif/decode_quirks.wuffs FILE: ../../../third_party/wuffs/std/gzip/decode_gzip.wuffs @@ -927,6 +1406,9 @@ FILE: ../../../third_party/wuffs/std/json/common_consts.wuffs FILE: ../../../third_party/wuffs/std/json/decode_json.wuffs FILE: ../../../third_party/wuffs/std/json/decode_quirks.wuffs FILE: ../../../third_party/wuffs/std/lzw/decode_lzw.wuffs +FILE: ../../../third_party/wuffs/std/nie/decode_nie.wuffs +FILE: ../../../third_party/wuffs/std/png/decode_filter_fallback.wuffs +FILE: ../../../third_party/wuffs/std/png/decode_png.wuffs FILE: ../../../third_party/wuffs/std/wbmp/decode_wbmp.wuffs FILE: ../../../third_party/wuffs/std/zlib/decode_zlib.wuffs ---------------------------------------------------------------------------------------------------- @@ -1134,394 +1616,368 @@ limitations under the License. ==================================================================================================== ==================================================================================================== -LIBRARY: angle -LIBRARY: khronos -ORIGIN: ../../../third_party/angle/include/EGL/egl.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/EGL/egl.h -FILE: ../../../third_party/angle/include/EGL/eglext.h -FILE: ../../../third_party/angle/include/GLES/glext.h -FILE: ../../../third_party/angle/include/GLES2/gl2.h -FILE: ../../../third_party/angle/scripts/egl.xml -FILE: ../../../third_party/khronos/EGL/egl.h -FILE: ../../../third_party/khronos/EGL/eglext.h +LIBRARY: abseil-cpp +ORIGIN: ../../../third_party/abseil-cpp/LICENSE +TYPE: LicenseType.apache +FILE: ../../../third_party/abseil-cpp/CMake/Googletest/CMakeLists.txt.in +FILE: ../../../third_party/abseil-cpp/CMake/abslConfig.cmake.in +FILE: ../../../third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl +FILE: ../../../third_party/abseil-cpp/absl/copts/configure_copts.bzl +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc +FILE: ../../../third_party/abseil-cpp/absl/random/gaussian_distribution.cc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h +FILE: ../../../third_party/abseil-cpp/absl/time/internal/get_current_time_posix.inc +FILE: ../../../third_party/abseil-cpp/absl/time/internal/zoneinfo.inc +FILE: ../../../third_party/abseil-cpp/patches/0001-Turn-on-hardened-mode.patch +FILE: ../../../third_party/abseil-cpp/patches/0002-delete-unprefixed-annotations.patch +FILE: ../../../third_party/abseil-cpp/patches/0003-delete-static-initializer-in-stacktrace.patch +FILE: ../../../third_party/abseil-cpp/symbols_arm64_dbg.def +FILE: ../../../third_party/abseil-cpp/symbols_arm64_rel.def +FILE: ../../../third_party/abseil-cpp/symbols_x64_dbg.def +FILE: ../../../third_party/abseil-cpp/symbols_x64_rel.def +FILE: ../../../third_party/abseil-cpp/symbols_x64_rel_asan.def +FILE: ../../../third_party/abseil-cpp/symbols_x86_dbg.def +FILE: ../../../third_party/abseil-cpp/symbols_x86_rel.def ---------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2017 The Khronos Group Inc. +Apache License +Version 2.0, January 2004 +https://www.apache.org/licenses -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. +1. Definitions. -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -==================================================================================================== -LIBRARY: angle -LIBRARY: khronos -ORIGIN: ../../../third_party/angle/include/EGL/eglplatform.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/EGL/eglplatform.h -FILE: ../../../third_party/khronos/EGL/eglplatform.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2007-2016 The Khronos Group Inc. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -==================================================================================================== -LIBRARY: angle -LIBRARY: khronos -ORIGIN: ../../../third_party/angle/include/GLES/gl.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/GLES/gl.h -FILE: ../../../third_party/angle/include/GLES2/gl2ext.h -FILE: ../../../third_party/angle/include/GLES3/gl3.h -FILE: ../../../third_party/angle/include/WGL/wgl.h -FILE: ../../../third_party/khronos/GLES2/gl2.h -FILE: ../../../third_party/khronos/GLES2/gl2ext.h -FILE: ../../../third_party/khronos/GLES3/gl3.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2018 The Khronos Group Inc. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." -==================================================================================================== -LIBRARY: angle -LIBRARY: khronos -ORIGIN: ../../../third_party/angle/include/GLES3/gl31.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/GLES3/gl31.h -FILE: ../../../third_party/angle/include/GLES3/gl32.h -FILE: ../../../third_party/khronos/GLES3/gl31.h -FILE: ../../../third_party/khronos/GLES3/gl32.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2016 The Khronos Group Inc. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. ==================================================================================================== ==================================================================================================== -LIBRARY: angle -LIBRARY: khronos -ORIGIN: ../../../third_party/angle/src/third_party/khronos/GL/wglext.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/src/third_party/khronos/GL/wglext.h -FILE: ../../../third_party/khronos/noninclude/GL/glext.h -FILE: ../../../third_party/khronos/noninclude/GL/wglext.h +LIBRARY: abseil-cpp +ORIGIN: ../../../third_party/abseil-cpp/absl_hardening_test.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/abseil-cpp/absl_hardening_test.cc ---------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2014 The Khronos Group Inc. +Copyright 2020 The Chromium Authors. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. + * 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. -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +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: angle -LIBRARY: xxhash -ORIGIN: ../../../third_party/angle/include/platform/Feature.h + ../../../third_party/angle/LICENSE +LIBRARY: base +ORIGIN: ../../../third_party/angle/include/EGL/eglext_angle.h + ../../../third_party/angle/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/WATCHLISTS -FILE: ../../../third_party/angle/include/platform/Feature.h -FILE: ../../../third_party/angle/include/platform/FeaturesMtl.h -FILE: ../../../third_party/angle/include/vulkan/vulkan_fuchsia_ext.h -FILE: ../../../third_party/angle/samples/capture_replay/CaptureReplay.cpp -FILE: ../../../third_party/angle/scripts/entry_point_packed_gl_enums.json -FILE: ../../../third_party/angle/src/common/PoolAlloc.cpp -FILE: ../../../third_party/angle/src/common/PoolAlloc.h -FILE: ../../../third_party/angle/src/common/PoolAlloc_unittest.cpp -FILE: ../../../third_party/angle/src/common/apple_platform_utils.h -FILE: ../../../third_party/angle/src/common/system_utils_unittest.cpp -FILE: ../../../third_party/angle/src/common/system_utils_win32.cpp -FILE: ../../../third_party/angle/src/common/system_utils_winuwp.cpp -FILE: ../../../third_party/angle/src/common/third_party/xxhash/xxhash_fuzzer.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.l -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.y -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_lex_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_tab_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/BuiltinsWorkaroundGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/BuiltinsWorkaroundGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/ParseContext_interm.h -FILE: ../../../third_party/angle/src/compiler/translator/ValidateAST.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateAST.h +FILE: ../../../third_party/angle/include/EGL/eglext_angle.h +FILE: ../../../third_party/angle/include/GLES2/gl2ext_angle.h +FILE: ../../../third_party/angle/samples/multiview/Multiview.cpp +FILE: ../../../third_party/angle/src/common/PackedEGLEnums_autogen.cpp +FILE: ../../../third_party/angle/src/common/PackedEGLEnums_autogen.h +FILE: ../../../third_party/angle/src/common/PackedEnums.h +FILE: ../../../third_party/angle/src/common/PackedGLEnums_autogen.cpp +FILE: ../../../third_party/angle/src/common/PackedGLEnums_autogen.h +FILE: ../../../third_party/angle/src/common/aligned_memory.cpp +FILE: ../../../third_party/angle/src/common/aligned_memory.h +FILE: ../../../third_party/angle/src/common/aligned_memory_unittest.cpp +FILE: ../../../third_party/angle/src/common/angleutils_unittest.cpp +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/base_export.h +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/macros.h +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/sys_byteorder.h +FILE: ../../../third_party/angle/src/common/uniform_type_info_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Declarator.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Declarator.h +FILE: ../../../third_party/angle/src/compiler/translator/ExtensionBehavior.cpp +FILE: ../../../third_party/angle/src/compiler/translator/HashNames.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ImageFunctionHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ImageFunctionHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/IsASTDepthBelowLimit.cpp +FILE: ../../../third_party/angle/src/compiler/translator/IsASTDepthBelowLimit.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputTree.h +FILE: ../../../third_party/angle/src/compiler/translator/StaticType.h +FILE: ../../../third_party/angle/src/compiler/translator/Symbol.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Symbol.h +FILE: ../../../third_party/angle/src/compiler/translator/SymbolUniqueId.cpp +FILE: ../../../third_party/angle/src/compiler/translator/SymbolUniqueId.h FILE: ../../../third_party/angle/src/compiler/translator/emulated_builtin_functions_hlsl_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/glslang.l -FILE: ../../../third_party/angle/src/compiler/translator/glslang.y -FILE: ../../../third_party/angle/src/compiler/translator/glslang_lex_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/glslang_tab_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/span_unittest.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveInactiveInterfaceVariables.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteAtomicCounters.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteAtomicCounters.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteDfdy.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteDfdy.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteRowMajorMatrices.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteRowMajorMatrices.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/BuiltIn.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindFunction.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindFunction.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceShadowingVariables.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl.cpp -FILE: ../../../third_party/angle/src/libANGLE/EGLSync.h -FILE: ../../../third_party/angle/src/libANGLE/FrameCapture.cpp -FILE: ../../../third_party/angle/src/libANGLE/FrameCapture.h -FILE: ../../../third_party/angle/src/libANGLE/FrameCapture_mock.cpp -FILE: ../../../third_party/angle/src/libANGLE/MemoryObject.cpp -FILE: ../../../third_party/angle/src/libANGLE/MemoryObject.h -FILE: ../../../third_party/angle/src/libANGLE/Overlay.cpp -FILE: ../../../third_party/angle/src/libANGLE/Overlay.h -FILE: ../../../third_party/angle/src/libANGLE/OverlayWidgets.cpp -FILE: ../../../third_party/angle/src/libANGLE/OverlayWidgets.h -FILE: ../../../third_party/angle/src/libANGLE/Overlay_font_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/Overlay_font_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Semaphore.cpp -FILE: ../../../third_party/angle/src/libANGLE/Semaphore.h -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_1_0_params.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_2_0_params.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_0_params.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_1_params.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_2_params.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_ext_params.cpp -FILE: ../../../third_party/angle/src/libANGLE/es3_copy_conversion_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/format_map_desktop.cpp -FILE: ../../../third_party/angle/src/libANGLE/gl_enum_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/gl_enum_utils.h -FILE: ../../../third_party/angle/src/libANGLE/overlay_widgets.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLSyncImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/MemoryObjectImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/OverlayImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/SemaphoreImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ShaderImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicImage2DHLSL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicImage2DHLSL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11Helper_autogen.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/MappedSubresourceVerifier11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/MappedSubresourceVerifier11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/driver_utils_mac.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/MemoryObjectGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/MemoryObjectGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SemaphoreGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SemaphoreGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/glslang_wrapper_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/glslang_wrapper_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/CompilerMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/CompilerMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ContextMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ContextMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/DisplayMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/DisplayMtl_api.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/FrameBufferMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/FrameBufferMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderBufferMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderBufferMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderTargetMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderTargetMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ShaderMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ShaderMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SurfaceMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SurfaceMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TextureMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TextureMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/VertexArrayMtl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/VertexArrayMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_buffer_pool.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_buffer_pool.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_command_buffer.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_command_buffer.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_common.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_common.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_utils.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_render_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_render_utils.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_resources.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_resources.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_state_cache.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_state_cache.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_utils.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/serial_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/serial_utils_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DisplayVk_api.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/OverlayVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/OverlayVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/PersistentCommandPool.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/PersistentCommandPool.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SemaphoreVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SemaphoreVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/WindowSurfaceVkFuchsia.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/WindowSurfaceVkFuchsia.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/DisplayVkGGP.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/DisplayVkGGP.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/WindowSurfaceVkGGP.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/WindowSurfaceVkGGP.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/DisplayVkMac.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/DisplayVkMac.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/WindowSurfaceVkMac.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/WindowSurfaceVkMac.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolve.frag -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolve.frag.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolveStencilNoExport.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolveStencilNoExport.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndex.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndex.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndexIndirectLineLoop.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndirectLineLoop.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndirectLineLoop.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayCull.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayCull.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_wrapper.h -FILE: ../../../third_party/angle/src/libANGLE/trace.h -FILE: ../../../third_party/angle/src/libANGLE/validationES32.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationES32.h -FILE: ../../../third_party/angle/src/libANGLE/validationESEXT.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL1.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL11.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL12.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL13.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL14.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL15.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL2.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL21.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL3.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL31.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL32.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL33.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL4.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL41.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL42.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL43.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL44.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL45.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationGL46.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_wgl.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_wgl.h -FILE: ../../../third_party/angle/util/Timer.cpp -FILE: ../../../third_party/angle/util/Timer.h -FILE: ../../../third_party/angle/util/fuchsia/ScenicWindow.cpp -FILE: ../../../third_party/angle/util/fuchsia/ScenicWindow.h -FILE: ../../../third_party/angle/util/posix/crash_handler_posix.cpp -FILE: ../../../third_party/angle/util/test_utils.cpp -FILE: ../../../third_party/angle/util/test_utils_unittest.cpp -FILE: ../../../third_party/angle/util/test_utils_unittest_helper.cpp -FILE: ../../../third_party/angle/util/test_utils_unittest_helper.h -FILE: ../../../third_party/angle/util/windows/test_utils_winuwp.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ClampPointSize.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ClampPointSize.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveArrayLengthMethod.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveArrayLengthMethod.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveUnreferencedVariables.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveUnreferencedVariables.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/WrapSwitchStatementsInBlocks.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/ClampFragDepth.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/ClampFragDepth.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/VectorizeVectorScalarArithmetic.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/VectorizeVectorScalarArithmetic.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindMain.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindMain.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNode_util.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNode_util.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermTraverse.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RunAtTheEndOfShader.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RunAtTheEndOfShader.h +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_macos.mm +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/ErrorStrings.h +FILE: ../../../third_party/angle/src/libANGLE/LoggingAnnotator.cpp +FILE: ../../../third_party/angle/src/libANGLE/LoggingAnnotator.h +FILE: ../../../third_party/angle/src/libANGLE/MemoryProgramCache.cpp +FILE: ../../../third_party/angle/src/libANGLE/MemoryProgramCache.h +FILE: ../../../third_party/angle/src/libANGLE/ProgramLinkedResources.cpp +FILE: ../../../third_party/angle/src/libANGLE/ProgramLinkedResources.h +FILE: ../../../third_party/angle/src/libANGLE/ProgramPipeline.cpp +FILE: ../../../third_party/angle/src/libANGLE/ProgramPipeline.h +FILE: ../../../third_party/angle/src/libANGLE/ResourceMap.h +FILE: ../../../third_party/angle/src/libANGLE/SizedMRUCache.h +FILE: ../../../third_party/angle/src/libANGLE/SizedMRUCache_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/VertexArray_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramPipelineImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ProgramPipeline11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ProgramPipeline11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Clear11.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ClearMultiviewGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ClearMultiviewGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DispatchTableGL_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DispatchTableGL_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramPipelineGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramPipelineGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/egl_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/egl_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/null_functions.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/null_functions.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/SurfaceWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramPipelineNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramPipelineNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramPipelineVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramPipelineVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ResourceVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ResourceVk.h +FILE: ../../../third_party/angle/src/libGL/proc_table_wgl.h +FILE: ../../../third_party/angle/src/libGLESv2/proc_table_egl.h ---------------------------------------------------------------------------------------------------- -Copyright 2019 The ANGLE Project Authors. All rights reserved. +Copyright 2017 The ANGLE Project Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -1556,17130 +2012,18102 @@ POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: angle -ORIGIN: ../../../fuchsia/sdk/linux/LICENSE +LIBRARY: base +ORIGIN: ../../../third_party/angle/include/platform/FrontendFeatures.h + ../../../third_party/angle/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl.c -FILE: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl.h -FILE: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl_backend.h +FILE: ../../../third_party/angle/include/platform/FrontendFeatures.h +FILE: ../../../third_party/angle/src/common/Color.h +FILE: ../../../third_party/angle/src/common/Color.inc +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/logging.h +FILE: ../../../third_party/angle/src/common/vector_utils.h +FILE: ../../../third_party/angle/src/common/vector_utils_unittest.cpp +FILE: ../../../third_party/angle/src/common/vulkan/vk_headers.h +FILE: ../../../third_party/angle/src/compiler/fuzz/translator_fuzzer.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ConstantUnion.cpp +FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/Severity.h +FILE: ../../../third_party/angle/src/compiler/translator/TextureFunctionHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/TextureFunctionHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorVulkan.cpp +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorVulkan.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateMaxParameters.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateMaxParameters.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/DeferGlobalInitializers.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveInvariantDeclaration.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveInvariantDeclaration.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteTexelFetchOffset.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteTexelFetchOffset.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SimplifyLoopConditions.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SimplifyLoopConditions.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SplitSequenceOperator.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SplitSequenceOperator.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/AddDefaultReturnStatements.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/BreakVariableAliasingInInnerLoops.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/ExpandIntegerPowExpressions.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteUnaryMinusOperatorInt.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/UseInterfaceBlockFields.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/UseInterfaceBlockFields.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/AddAndTrueToLoopCondition.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/AddAndTrueToLoopCondition.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/RewriteUnaryMinusOperatorFloat.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/RewriteUnaryMinusOperatorFloat.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNodePatternMatcher.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermNodePatternMatcher.h +FILE: ../../../third_party/angle/src/image_util/imageformats.cpp +FILE: ../../../third_party/angle/src/libANGLE/Observer.h +FILE: ../../../third_party/angle/src/libANGLE/Observer_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/Stream.cpp +FILE: ../../../third_party/angle/src/libANGLE/Stream.h +FILE: ../../../third_party/angle/src/libANGLE/Thread.cpp +FILE: ../../../third_party/angle/src/libANGLE/Thread.h +FILE: ../../../third_party/angle/src/libANGLE/VaryingPacking_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/WorkerThread.cpp +FILE: ../../../third_party/angle/src/libANGLE/WorkerThread.h +FILE: ../../../third_party/angle/src/libANGLE/WorkerThread_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/es3_copy_conversion_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/format_map_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/queryutils.cpp +FILE: ../../../third_party/angle/src/libANGLE/queryutils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ContextImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/ContextImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLImplFactory.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/Format.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/FramebufferAttachmentObjectImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/StreamProducerImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/TextureImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/HLSLCompiler.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/NativeWindowD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/NativeWindowD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SwapChainD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Context11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Context11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/NativeWindow11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerD3DTexture.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerD3DTexture.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Context9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Context9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/NativeWindow9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/NativeWindow9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/driver_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/driver_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ContextGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ContextGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DisplayEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DisplayEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGLDL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/FunctionsEGLDL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/PbufferSurfaceEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/PbufferSurfaceEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SurfaceEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SurfaceEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/WindowSurfaceEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/WindowSurfaceEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/DisplayAndroid.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/DisplayAndroid.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/functionsegl_typedefs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/DisplayGbm.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/DisplayGbm.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/SurfaceGbm.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/gbm/SurfaceGbm.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/SurfaceGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/BufferNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/BufferNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/CompilerNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/CompilerNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ContextNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ContextNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DeviceNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DeviceNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DisplayNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/DisplayNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FenceNVNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FenceNVNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FramebufferNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/FramebufferNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ImageNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ImageNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ProgramNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/QueryNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/QueryNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/RenderbufferNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/RenderbufferNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SamplerNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SamplerNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ShaderNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/ShaderNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SurfaceNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SurfaceNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SyncNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/SyncNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TextureNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TextureNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TransformFeedbackNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/TransformFeedbackNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/VertexArrayNULL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/null/VertexArrayNULL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/renderer_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/renderer_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/BufferVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/BufferVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CompilerVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CompilerVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DeviceVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DeviceVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DisplayVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DisplayVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FenceNVVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FenceNVVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FramebufferVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/FramebufferVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/GlslangWrapperVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/GlslangWrapperVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ImageVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ImageVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/QueryVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/QueryVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderTargetVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderTargetVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderbufferVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RenderbufferVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RendererVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/RendererVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SamplerVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SamplerVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShaderVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ShaderVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SurfaceVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SurfaceVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SyncVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SyncVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TextureVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TextureVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TransformFeedbackVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/TransformFeedbackVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/VertexArrayVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/DisplayVkWin32.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/DisplayVkWin32.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/WindowSurfaceVkWin32.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/win32/WindowSurfaceVkWin32.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/DisplayVkXcb.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/DisplayVkXcb.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/WindowSurfaceVkXcb.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/xcb/WindowSurfaceVkXcb.h +FILE: ../../../third_party/angle/src/libANGLE/validationEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationES31.cpp +FILE: ../../../third_party/angle/util/android/AndroidPixmap.cpp +FILE: ../../../third_party/angle/util/android/AndroidWindow.cpp +FILE: ../../../third_party/angle/util/android/AndroidWindow.h +FILE: ../../../third_party/angle/util/ozone/OzonePixmap.cpp +FILE: ../../../third_party/angle/util/ozone/OzoneWindow.cpp +FILE: ../../../third_party/angle/util/ozone/OzoneWindow.h ---------------------------------------------------------------------------------------------------- -Copyright 2019 The Fuchsia Authors. All rights reserved. +Copyright 2016 The ANGLE 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: +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. + 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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. +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: angle -ORIGIN: ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/.style.yapf -FILE: ../../../third_party/angle/.yapfignore -FILE: ../../../third_party/angle/CONTRIBUTORS -FILE: ../../../third_party/angle/DEPS -FILE: ../../../third_party/angle/additional_readme_paths.json -FILE: ../../../third_party/angle/doc/img/StateChangeNotificationFlow.svg -FILE: ../../../third_party/angle/doc/img/StateNotificationExample.svg -FILE: ../../../third_party/angle/include/GLES2/gl2platform.h -FILE: ../../../third_party/angle/include/GLES3/gl3platform.h -FILE: ../../../third_party/angle/infra/config/global/commit-queue.cfg -FILE: ../../../third_party/angle/infra/config/global/cr-buildbucket.cfg -FILE: ../../../third_party/angle/infra/config/global/luci-logdog.cfg -FILE: ../../../third_party/angle/infra/config/global/luci-milo.cfg -FILE: ../../../third_party/angle/infra/config/global/luci-scheduler.cfg -FILE: ../../../third_party/angle/infra/config/global/project.cfg -FILE: ../../../third_party/angle/infra/config/global/refs.cfg -FILE: ../../../third_party/angle/samples/multi_texture/basemap.tga -FILE: ../../../third_party/angle/samples/multi_texture/lightmap.tga -FILE: ../../../third_party/angle/samples/multiple_draw_buffers/multiple_draw_buffers_copy_fs.glsl -FILE: ../../../third_party/angle/samples/multiple_draw_buffers/multiple_draw_buffers_fs.glsl -FILE: ../../../third_party/angle/samples/multiple_draw_buffers/multiple_draw_buffers_vs.glsl -FILE: ../../../third_party/angle/samples/particle_system/smoke.tga -FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_format.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_load_functions_table.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_shader_preprocessor.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_shader_translator.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/D3D11_blit_shader_selection.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/D3D11_format.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/DXGI_format.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/DXGI_format_support.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Emulated_HLSL_functions.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_CTS_(dEQP)_build_files.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_EGL_WGL_loader.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_EGL_entry_points.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_copy_conversion_table.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_format_map.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/GLenum_value_to_string_map.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Metal_default_shaders.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Metal_format_table.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/OpenGL_dispatch_table.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Static_builtins.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Vulkan_format.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Vulkan_internal_shader_programs.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/Vulkan_mandatory_format_support_table.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/overlay_fonts.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/overlay_widgets.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/packed_enum.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/proc_table.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/restricted_traces.json -FILE: ../../../third_party/angle/scripts/code_generation_hashes/uniform_type.json -FILE: ../../../third_party/angle/src/android_system_settings/assets/a4a_rules.json -FILE: ../../../third_party/angle/src/android_system_settings/res/drawable/icon.png -FILE: ../../../third_party/angle/src/common/packed_egl_enums.json -FILE: ../../../third_party/angle/src/common/packed_gl_enums.json -FILE: ../../../third_party/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json -FILE: ../../../third_party/angle/src/feature_support_util/a4a_rules.json -FILE: ../../../third_party/angle/src/libANGLE/es3_copy_conversion_formats.json -FILE: ../../../third_party/angle/src/libANGLE/es3_format_type_combinations.json -FILE: ../../../third_party/angle/src/libANGLE/format_map_data.json -FILE: ../../../third_party/angle/src/libANGLE/overlay/DejaVuSansMono-Bold.ttf -FILE: ../../../third_party/angle/src/libANGLE/renderer/angle_format_data.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/angle_format_map.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/dxgi_format_data.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/dxgi_support_data.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/BufferToTexture11.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Passthrough2D11.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/ResolveColor.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/ResolveDepthStencil.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Swizzle11.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_gs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_ps_4f.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_ps_4i.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_ps_4ui.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_vs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11_fl9vs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11multiviewgs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11multiviewvs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11vs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/cleardepth11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11_fl9ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps1.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps2.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps3.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps4.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps5.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps6.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps7.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps8.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps1.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps2.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps3.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps4.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps5.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps6.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps7.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps8.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps1.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps2.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps3.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps4.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps5.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps6.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps7.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps8.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_luma_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_luma_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_luma_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_lumaalpha_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_lumaalpha_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_lumaalpha_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_565_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_565_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_565_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_4444_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_4444_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_4444_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_5551_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_5551_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_5551_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_luma_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_luma_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_luma_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_lumaalpha_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_lumaalpha_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_lumaalpha_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_565_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_565_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_565_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgb_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgba_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgb_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgba_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgb_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgb_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgb_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgba_2d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgba_2darray_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgba_3d_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough3d11gs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough3d11vs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrougha2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughdepth2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlum2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlum2darray11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlum3d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlumalpha2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlumalpha2darray11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlumalpha3d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2darray11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2darrayi11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2darrayui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr3d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr3di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr3dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2darray11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2darrayi11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2darrayui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg3d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg3di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg3dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2d_565_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darray11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darray_565_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darrayi11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darrayui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3d_565_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d_4444_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d_5551_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darray11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darray_4444_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darray_5551_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darrayi11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darrayui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2dms11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3d11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3d_4444_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3d_5551_11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3di11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3dui11ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvecolor2dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvedepth11_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvedepthstencil11_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvedepthstencil11_vs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvestencil11_ps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlef2darrayps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlef2dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlef3dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlei2darrayps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlei2dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlei3dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui2darrayps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui2dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui3dps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_map.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/componentmaskpremultps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/componentmaskps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/componentmaskunmultps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/luminancepremultps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/luminanceps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/luminanceunmultps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/passthroughps.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/standardvs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/gl_bindings_data.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/load_functions_data.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_map.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/LineRasterComparison.gif -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/LineRasterHoles.jpg -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/LineRasterPixelExample.png -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/VulkanShaderTranslation.svg -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000004.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000005.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000006.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000007.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000008.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000009.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000A.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000B.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000C.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000D.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000E.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000F.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000010.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000011.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000012.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000013.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000014.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000015.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000016.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000017.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000004.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000005.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000006.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000007.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000008.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.00000009.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.0000000A.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BufferUtils.comp.0000000B.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndexIndirectLineLoop.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndexIndirectLineLoop.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndexIndirectLineLoop.comp.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndirectLineLoop.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000004.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000005.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000006.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000004.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000005.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000006.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000007.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000008.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000009.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000A.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000B.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000C.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000D.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000E.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000F.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000010.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000011.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000012.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000013.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000014.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000015.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000016.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000017.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000004.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000005.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000008.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000009.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000000A.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000000B.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000000C.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000000D.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000010.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000011.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000012.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000013.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000014.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000015.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000002.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000003.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000004.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000005.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayDraw.comp.00000000.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayDraw.comp.00000001.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mandatory_format_support_data.json -FILE: ../../../third_party/angle/src/libEGL/libEGL.def -FILE: ../../../third_party/angle/src/libEGL/libEGL.rc -FILE: ../../../third_party/angle/src/libEGL/resource.h -FILE: ../../../third_party/angle/src/libGL/libGL.rc -FILE: ../../../third_party/angle/src/libGL/resource.h -FILE: ../../../third_party/angle/src/libGLESv1_CM/libGLESv1_CM.def -FILE: ../../../third_party/angle/src/libGLESv1_CM/libGLESv1_CM.rc -FILE: ../../../third_party/angle/src/libGLESv1_CM/resource.h -FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2.rc -FILE: ../../../third_party/angle/src/libGLESv2/resource.h ----------------------------------------------------------------------------------------------------- -Copyright 2018 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/android/AndroidManifest.xml.jinja2 + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/android/AndroidManifest.xml.jinja2 -FILE: ../../../third_party/angle/include/GLES/glext_angle.h -FILE: ../../../third_party/angle/include/platform/FeaturesVk.h -FILE: ../../../third_party/angle/samples/gles1/DrawTexture.cpp -FILE: ../../../third_party/angle/samples/gles1/FlatShading.cpp -FILE: ../../../third_party/angle/samples/gles1/HelloTriangle.cpp -FILE: ../../../third_party/angle/samples/gles1/SimpleLighting.cpp -FILE: ../../../third_party/angle/samples/gles1/SimpleTexture2D.cpp -FILE: ../../../third_party/angle/scripts/egl_angle_ext.xml -FILE: ../../../third_party/angle/scripts/gl_angle_ext.xml -FILE: ../../../third_party/angle/scripts/vk_mandatory_format_support_capture_to_json.js -FILE: ../../../third_party/angle/src/android_system_settings/res/values-v17/styles.xml -FILE: ../../../third_party/angle/src/common/FastVector.h -FILE: ../../../third_party/angle/src/common/FastVector_unittest.cpp -FILE: ../../../third_party/angle/src/common/FixedVector.h -FILE: ../../../third_party/angle/src/common/FixedVector_unittest.cpp -FILE: ../../../third_party/angle/src/common/PackedEnums.cpp -FILE: ../../../third_party/angle/src/common/android_util.cpp -FILE: ../../../third_party/angle/src/common/android_util.h -FILE: ../../../third_party/angle/src/common/hash_utils.h -FILE: ../../../third_party/angle/src/common/hash_utils_unittest.cpp -FILE: ../../../third_party/angle/src/common/matrix_utils.cpp -FILE: ../../../third_party/angle/src/common/system_utils.cpp -FILE: ../../../third_party/angle/src/common/system_utils_posix.cpp -FILE: ../../../third_party/angle/src/compiler/translator/AtomicCounterFunctionHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/AtomicCounterFunctionHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/FunctionLookup.cpp -FILE: ../../../third_party/angle/src/compiler/translator/FunctionLookup.h -FILE: ../../../third_party/angle/src/compiler/translator/ImmutableString.h -FILE: ../../../third_party/angle/src/compiler/translator/ImmutableStringBuilder.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ImmutableStringBuilder.h -FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockOutputHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/builtin_variables.json -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/FoldExpressions.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/FoldExpressions.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/NameEmbeddedUniformStructs.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/NameEmbeddedUniformStructs.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneEmptyCases.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneEmptyCases.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteAtomicFunctionExpressions.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteAtomicFunctionExpressions.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteExpressionsWithShaderStorageBlock.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteExpressionsWithShaderStorageBlock.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteRepeatedAssignToSwizzled.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteRepeatedAssignToSwizzled.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplersOld.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateArrayConstructorStatements.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateArrayConstructorStatements.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceVariable.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceVariable.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/Visit.h -FILE: ../../../third_party/angle/src/feature_support_util/feature_support_util.cpp -FILE: ../../../third_party/angle/src/feature_support_util/feature_support_util.h -FILE: ../../../third_party/angle/src/feature_support_util/feature_support_util_unittest.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_android.cpp -FILE: ../../../third_party/angle/src/libANGLE/BlobCache.cpp -FILE: ../../../third_party/angle/src/libANGLE/BlobCache.h -FILE: ../../../third_party/angle/src/libANGLE/BlobCache_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/Context.inl.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_1_0.cpp -FILE: ../../../third_party/angle/src/libANGLE/GLES1Renderer.cpp -FILE: ../../../third_party/angle/src/libANGLE/GLES1Renderer.h -FILE: ../../../third_party/angle/src/libANGLE/GLES1Shaders.inc -FILE: ../../../third_party/angle/src/libANGLE/GLES1State.cpp -FILE: ../../../third_party/angle/src/libANGLE/GLES1State.h -FILE: ../../../third_party/angle/src/libANGLE/Observer.cpp -FILE: ../../../third_party/angle/src/libANGLE/ResourceMap_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/entry_points_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/QueryImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/RenderTargetCache.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ContextD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Program11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Program11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/MultiplyAlpha.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Passthrough2DArray11.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Passthrough3D11.hlsl -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ImageGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ImageGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ContextEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ContextEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ExternalImageSiblingEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ImageEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ImageEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/RendererEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/RendererEGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/ContextWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/ContextWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/RendererWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/RendererWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/UtilsVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/UtilsVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/WindowSurfaceVkAndroid.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/WindowSurfaceVkAndroid.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BufferUtils.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BufferUtils.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/FullScreenQuad.vert -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageCopy.frag -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageCopy.frag.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_caps_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_map.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.h -FILE: ../../../third_party/angle/src/libANGLE/validationES1.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationES1.h -FILE: ../../../third_party/angle/src/libANGLE/validationES2.h -FILE: ../../../third_party/angle/src/libANGLE/validationES3.h -FILE: ../../../third_party/angle/src/libANGLE/validationES31.h -FILE: ../../../third_party/angle/src/libANGLE/validationESEXT.h -FILE: ../../../third_party/angle/src/libGLESv1_CM/libGLESv1_CM.cpp -FILE: ../../../third_party/angle/util/EGLPlatformParameters.h -FILE: ../../../third_party/angle/util/util_export.h -FILE: ../../../third_party/angle/util/util_gl.h -FILE: ../../../third_party/angle/util/windows/WGLWindow.cpp -FILE: ../../../third_party/angle/util/windows/WGLWindow.h ----------------------------------------------------------------------------------------------------- -Copyright 2018 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/include/GLES/glext_explicit_context_autogen.inc + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/GLES/glext_explicit_context_autogen.inc -FILE: ../../../third_party/angle/include/GLES2/gl2ext_explicit_context_autogen.inc -FILE: ../../../third_party/angle/include/GLES3/gl31ext_explicit_context_autogen.inc -FILE: ../../../third_party/angle/include/GLES3/gl32ext_explicit_context_autogen.inc -FILE: ../../../third_party/angle/include/GLES3/gl3ext_explicit_context_autogen.inc -FILE: ../../../third_party/angle/include/platform/Platform.h -FILE: ../../../third_party/angle/src/common/PackedEGLEnums_autogen.cpp -FILE: ../../../third_party/angle/src/common/PackedEGLEnums_autogen.h -FILE: ../../../third_party/angle/src/common/PackedGLEnums_autogen.cpp -FILE: ../../../third_party/angle/src/common/PackedGLEnums_autogen.h -FILE: ../../../third_party/angle/src/common/uniform_type_info_autogen.cpp -FILE: ../../../third_party/angle/src/common/vulkan/vulkan_icd.cpp -FILE: ../../../third_party/angle/src/common/vulkan/vulkan_icd.h -FILE: ../../../third_party/angle/src/compiler/translator/ImmutableString_ESSL_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ImmutableString_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ParseContext_ESSL_autogen.h -FILE: ../../../third_party/angle/src/compiler/translator/ParseContext_complete_autogen.h -FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable_ESSL_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable_autogen.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EarlyFragmentTestsOptimization.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EarlyFragmentTestsOptimization.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveAtomicCounterBuiltins.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/BuiltIn_ESSL_autogen.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/BuiltIn_complete_autogen.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceClipDistanceVariable.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceClipDistanceVariable.h -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_fuchsia.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_ios.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_vulkan.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_vulkan.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_3_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_4_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_5_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_2_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_2_1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_3_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_3_1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_3_2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_3_3_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_3_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_4_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_5_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_6_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_1_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_2_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_3_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_3_1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_3_2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/Context_gles_ext_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/InfoLog.h -FILE: ../../../third_party/angle/src/libANGLE/Overlay_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/ProgramExecutable.cpp -FILE: ../../../third_party/angle/src/libANGLE/ProgramExecutable.h -FILE: ../../../third_party/angle/src/libANGLE/angletypes_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_1_0_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_1_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_2_0_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_2_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_0_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_0_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_1_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_2_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_3_2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_ext_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/capture_gles_ext_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/entry_points_enum_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/entry_points_enum_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/format_map_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/frame_capture_replay_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/frame_capture_utils_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/frame_capture_utils_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/gl_enum_utils_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/gl_enum_utils_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/FormatID_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/Format_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramPipelineImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/dxgi_format_map_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DispatchTableGL_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DispatchTableGL_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/ContextEAGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/ContextEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DeviceEAGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DeviceEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DisplayEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DisplayEAGL.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PixmapSurfaceGLX.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PixmapSurfaceGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/glx_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/glx_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/null_functions.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/null_functions.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/load_functions_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CommandProcessor.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CommandProcessor.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndexIndirectLineLoop.comp.json -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mandatory_format_support_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h -FILE: ../../../third_party/angle/src/libANGLE/validationES1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationES2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationES31_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationES32_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationES3_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationESEXT_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL11_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL12_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL13_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL14_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL15_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL1_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL21_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL2_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL31_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL32_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL33_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL3_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL41_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL42_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL43_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL44_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL45_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL46_autogen.h -FILE: ../../../third_party/angle/src/libANGLE/validationGL4_autogen.h -FILE: ../../../third_party/angle/src/libEGL/egl_loader_autogen.cpp -FILE: ../../../third_party/angle/src/libEGL/egl_loader_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_0_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_1_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_1_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_2_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_2_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_3_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_3_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_4_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_4_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_5_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_5_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_2_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_2_0_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_2_1_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_2_1_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_0_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_1_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_1_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_2_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_2_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_3_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_3_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_0_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_1_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_1_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_2_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_2_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_3_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_3_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_4_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_4_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_5_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_5_autogen.h -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_6_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_6_autogen.h -FILE: ../../../third_party/angle/src/libGL/libGL_autogen.cpp -FILE: ../../../third_party/angle/src/libGL/libGL_autogen.def -FILE: ../../../third_party/angle/src/libGL/proc_table_wgl_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_1_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_1_0_autogen.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_2_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_2_0_autogen.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_0_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_0_autogen.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_1_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_1_autogen.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_2_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_2_autogen.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_ext_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_ext_autogen.h -FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_autogen.cpp -FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_autogen.def -FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_no_capture_autogen.def -FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_with_capture_autogen.def -FILE: ../../../third_party/angle/src/libGLESv2/proc_table_egl_autogen.cpp -FILE: ../../../third_party/angle/util/egl_loader_autogen.cpp -FILE: ../../../third_party/angle/util/egl_loader_autogen.h -FILE: ../../../third_party/angle/util/frame_capture_utils.h -FILE: ../../../third_party/angle/util/gles_loader_autogen.cpp -FILE: ../../../third_party/angle/util/gles_loader_autogen.h -FILE: ../../../third_party/angle/util/png_utils.cpp -FILE: ../../../third_party/angle/util/png_utils.h -FILE: ../../../third_party/angle/util/windows/wgl_loader_autogen.cpp -FILE: ../../../third_party/angle/util/windows/wgl_loader_autogen.h ----------------------------------------------------------------------------------------------------- -Copyright 2020 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/include/GLSLANG/ShaderLang.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/GLSLANG/ShaderLang.h -FILE: ../../../third_party/angle/samples/shader_translator/shader_translator.cpp -FILE: ../../../third_party/angle/src/common/angleutils.h -FILE: ../../../third_party/angle/src/common/debug.cpp -FILE: ../../../third_party/angle/src/common/debug.h -FILE: ../../../third_party/angle/src/common/mathutil.h -FILE: ../../../third_party/angle/src/common/utilities.cpp -FILE: ../../../third_party/angle/src/common/utilities.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.l -FILE: ../../../third_party/angle/src/compiler/translator/ASTMetadataHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ASTMetadataHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/BaseTypes.h -FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp -FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/CallDAG.cpp -FILE: ../../../third_party/angle/src/compiler/translator/CallDAG.h -FILE: ../../../third_party/angle/src/compiler/translator/CollectVariables.cpp -FILE: ../../../third_party/angle/src/compiler/translator/CollectVariables.h -FILE: ../../../third_party/angle/src/compiler/translator/Common.h -FILE: ../../../third_party/angle/src/compiler/translator/Compiler.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Compiler.h -FILE: ../../../third_party/angle/src/compiler/translator/ConstantUnion.h -FILE: ../../../third_party/angle/src/compiler/translator/ExtensionBehavior.h -FILE: ../../../third_party/angle/src/compiler/translator/HashNames.h -FILE: ../../../third_party/angle/src/compiler/translator/InfoSink.cpp -FILE: ../../../third_party/angle/src/compiler/translator/InfoSink.h -FILE: ../../../third_party/angle/src/compiler/translator/Initialize.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Initialize.h -FILE: ../../../third_party/angle/src/compiler/translator/InitializeDll.cpp -FILE: ../../../third_party/angle/src/compiler/translator/InitializeDll.h -FILE: ../../../third_party/angle/src/compiler/translator/InitializeGlobals.h -FILE: ../../../third_party/angle/src/compiler/translator/IntermNode.cpp -FILE: ../../../third_party/angle/src/compiler/translator/IntermNode.h -FILE: ../../../third_party/angle/src/compiler/translator/Operator.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Operator.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputESSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/OutputESSL.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSLBase.cpp -FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSLBase.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/OutputHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputTree.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ParseContext.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ParseContext.h -FILE: ../../../third_party/angle/src/compiler/translator/PoolAlloc.cpp -FILE: ../../../third_party/angle/src/compiler/translator/PoolAlloc.h -FILE: ../../../third_party/angle/src/compiler/translator/QualifierTypes.cpp -FILE: ../../../third_party/angle/src/compiler/translator/QualifierTypes.h -FILE: ../../../third_party/angle/src/compiler/translator/ShaderLang.cpp -FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable.cpp -FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable.h -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorESSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorESSL.h -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/Types.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Types.h -FILE: ../../../third_party/angle/src/compiler/translator/ValidateGlobalInitializer.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateGlobalInitializer.h -FILE: ../../../third_party/angle/src/compiler/translator/ValidateLimitations.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateSwitch.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateSwitch.h -FILE: ../../../third_party/angle/src/compiler/translator/ValidateVaryingLocations.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateVaryingLocations.h -FILE: ../../../third_party/angle/src/compiler/translator/VariablePacker.cpp -FILE: ../../../third_party/angle/src/compiler/translator/VariablePacker.h -FILE: ../../../third_party/angle/src/compiler/translator/VersionGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/VersionGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/glslang.l -FILE: ../../../third_party/angle/src/compiler/translator/glslang.y -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ArrayReturnValueToOutParameter.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ArrayReturnValueToOutParameter.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulatePrecision.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulatePrecision.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/InitializeVariables.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/InitializeVariables.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneNoOps.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneNoOps.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RecordConstantPrecision.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RecordConstantPrecision.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RegenerateStructNames.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RegenerateStructNames.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveDynamicIndexing.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveDynamicIndexing.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemovePow.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemovePow.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveSwitchFallThrough.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveSwitchFallThrough.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateArrayInitialization.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateArrayInitialization.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateDeclarations.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateDeclarations.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateExpressionsReturningArrays.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateExpressionsReturningArrays.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/UnfoldShortCircuitAST.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/UnfoldShortCircuitAST.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/UnfoldShortCircuitToIf.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/UnfoldShortCircuitToIf.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/NodeSearch.h -FILE: ../../../third_party/angle/src/compiler/translator/util.h -FILE: ../../../third_party/angle/src/image_util/generatemip.h -FILE: ../../../third_party/angle/src/libANGLE/Buffer.cpp -FILE: ../../../third_party/angle/src/libANGLE/Buffer.h -FILE: ../../../third_party/angle/src/libANGLE/Config.cpp -FILE: ../../../third_party/angle/src/libANGLE/Config.h -FILE: ../../../third_party/angle/src/libANGLE/Context.cpp -FILE: ../../../third_party/angle/src/libANGLE/Context.h -FILE: ../../../third_party/angle/src/libANGLE/Display.cpp -FILE: ../../../third_party/angle/src/libANGLE/Display.h -FILE: ../../../third_party/angle/src/libANGLE/EGLSync.cpp -FILE: ../../../third_party/angle/src/libANGLE/Fence.cpp -FILE: ../../../third_party/angle/src/libANGLE/Fence.h -FILE: ../../../third_party/angle/src/libANGLE/Framebuffer.cpp -FILE: ../../../third_party/angle/src/libANGLE/Framebuffer.h -FILE: ../../../third_party/angle/src/libANGLE/HandleAllocator.cpp -FILE: ../../../third_party/angle/src/libANGLE/HandleAllocator.h -FILE: ../../../third_party/angle/src/libANGLE/Program.cpp -FILE: ../../../third_party/angle/src/libANGLE/Program.h -FILE: ../../../third_party/angle/src/libANGLE/RefCountObject.h -FILE: ../../../third_party/angle/src/libANGLE/Renderbuffer.cpp -FILE: ../../../third_party/angle/src/libANGLE/Renderbuffer.h -FILE: ../../../third_party/angle/src/libANGLE/ResourceManager.cpp -FILE: ../../../third_party/angle/src/libANGLE/ResourceManager.h -FILE: ../../../third_party/angle/src/libANGLE/Shader.cpp -FILE: ../../../third_party/angle/src/libANGLE/Shader.h -FILE: ../../../third_party/angle/src/libANGLE/Surface.cpp -FILE: ../../../third_party/angle/src/libANGLE/Surface.h -FILE: ../../../third_party/angle/src/libANGLE/Texture.cpp -FILE: ../../../third_party/angle/src/libANGLE/Texture.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/SurfaceImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ImageD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ImageD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexBuffer.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexBuffer.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexDataManager.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/TextureStorage.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexBuffer.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexBuffer.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexDataManager.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Image9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Image9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/IndexBuffer9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/IndexBuffer9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexBuffer9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexBuffer9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/vertexconversion.h -FILE: ../../../third_party/angle/src/libEGL/libEGL.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2002 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/include/GLSLANG/ShaderVars.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/GLSLANG/ShaderVars.h -FILE: ../../../third_party/angle/samples/sample_util/SampleApplication.cpp -FILE: ../../../third_party/angle/src/common/mathutil.cpp -FILE: ../../../third_party/angle/src/compiler/translator/CodeGen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/FlagStd140Structs.cpp -FILE: ../../../third_party/angle/src/compiler/translator/FlagStd140Structs.h -FILE: ../../../third_party/angle/src/compiler/translator/ValidateOutputs.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ValidateOutputs.h -FILE: ../../../third_party/angle/src/compiler/translator/blocklayout.cpp -FILE: ../../../third_party/angle/src/compiler/translator/blocklayout.h -FILE: ../../../third_party/angle/src/compiler/translator/blocklayoutHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/blocklayoutHLSL.h -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo.h -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_internal.h -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_libpci.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_linux.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_win.cpp -FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_x11.cpp -FILE: ../../../third_party/angle/src/image_util/copyimage.cpp -FILE: ../../../third_party/angle/src/image_util/copyimage.h -FILE: ../../../third_party/angle/src/image_util/imageformats.h -FILE: ../../../third_party/angle/src/image_util/loadimage.cpp -FILE: ../../../third_party/angle/src/image_util/loadimage.h -FILE: ../../../third_party/angle/src/image_util/loadimage_etc.cpp -FILE: ../../../third_party/angle/src/libANGLE/Constants.h -FILE: ../../../third_party/angle/src/libANGLE/IndexRangeCache.cpp -FILE: ../../../third_party/angle/src/libANGLE/IndexRangeCache.h -FILE: ../../../third_party/angle/src/libANGLE/Sampler.cpp -FILE: ../../../third_party/angle/src/libANGLE/Sampler.h -FILE: ../../../third_party/angle/src/libANGLE/VertexArray.cpp -FILE: ../../../third_party/angle/src/libANGLE/VertexArray.h -FILE: ../../../third_party/angle/src/libANGLE/VertexAttribute.h -FILE: ../../../third_party/angle/src/libANGLE/angletypes.cpp -FILE: ../../../third_party/angle/src/libANGLE/formatutils.cpp -FILE: ../../../third_party/angle/src/libANGLE/formatutils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/QueryImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/copyvertex.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Fence11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Fence11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Query11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexBuffer11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/formatutils11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/formatutils11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Fence9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Fence9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Query9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Query9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.h -FILE: ../../../third_party/angle/src/libANGLE/validationES.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationES.h -FILE: ../../../third_party/angle/src/libANGLE/validationES2.cpp -FILE: ../../../third_party/angle/src/libANGLE/validationES3.cpp -FILE: ../../../third_party/angle/util/EGLWindow.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2013 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/include/KHR/khrplatform.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/KHR/khrplatform.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2008-2018 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/include/angle_gl.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/angle_gl.h -FILE: ../../../third_party/angle/include/angle_windowsstore.h -FILE: ../../../third_party/angle/include/export.h -FILE: ../../../third_party/angle/samples/hello_triangle/HelloTriangle.cpp -FILE: ../../../third_party/angle/samples/mip_map_2d/MipMap2D.cpp -FILE: ../../../third_party/angle/samples/multi_texture/MultiTexture.cpp -FILE: ../../../third_party/angle/samples/multiple_draw_buffers/MultipleDrawBuffers.cpp -FILE: ../../../third_party/angle/samples/particle_system/ParticleSystem.cpp -FILE: ../../../third_party/angle/samples/post_sub_buffer/PostSubBuffer.cpp -FILE: ../../../third_party/angle/samples/sample_util/SampleApplication.h -FILE: ../../../third_party/angle/samples/sample_util/texture_utils.cpp -FILE: ../../../third_party/angle/samples/sample_util/texture_utils.h -FILE: ../../../third_party/angle/samples/sample_util/tga_utils.cpp -FILE: ../../../third_party/angle/samples/sample_util/tga_utils.h -FILE: ../../../third_party/angle/samples/simple_instancing/SimpleInstancing.cpp -FILE: ../../../third_party/angle/samples/simple_texture_2d/SimpleTexture2D.cpp -FILE: ../../../third_party/angle/samples/simple_texture_cubemap/SimpleTextureCubemap.cpp -FILE: ../../../third_party/angle/samples/simple_vertex_shader/SimpleVertexShader.cpp -FILE: ../../../third_party/angle/samples/stencil_operations/StencilOperations.cpp -FILE: ../../../third_party/angle/samples/tex_redef_microbench/TexRedefMicroBench.cpp -FILE: ../../../third_party/angle/samples/texture_wrap/TextureWrap.cpp -FILE: ../../../third_party/angle/samples/tri_fan_microbench/TriFanMicroBench.cpp -FILE: ../../../third_party/angle/src/common/MemoryBuffer.cpp -FILE: ../../../third_party/angle/src/common/MemoryBuffer.h -FILE: ../../../third_party/angle/src/common/angleutils.cpp -FILE: ../../../third_party/angle/src/common/platform.h -FILE: ../../../third_party/angle/src/common/system_utils.h -FILE: ../../../third_party/angle/src/common/system_utils_win.cpp -FILE: ../../../third_party/angle/src/common/tls.cpp -FILE: ../../../third_party/angle/src/common/tls.h -FILE: ../../../third_party/angle/src/common/version.h -FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/ResourcesHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ResourcesHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/ShaderVars.cpp -FILE: ../../../third_party/angle/src/compiler/translator/StructureHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/StructureHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/UtilsHLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/UtilsHLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteElseBlocks.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteElseBlocks.h -FILE: ../../../third_party/angle/src/image_util/copyimage.inc -FILE: ../../../third_party/angle/src/image_util/loadimage.inc -FILE: ../../../third_party/angle/src/libANGLE/AttributeMap.cpp -FILE: ../../../third_party/angle/src/libANGLE/AttributeMap.h -FILE: ../../../third_party/angle/src/libANGLE/Caps.cpp -FILE: ../../../third_party/angle/src/libANGLE/Caps.h -FILE: ../../../third_party/angle/src/libANGLE/Compiler.cpp -FILE: ../../../third_party/angle/src/libANGLE/Compiler.h -FILE: ../../../third_party/angle/src/libANGLE/Config_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/Error.cpp -FILE: ../../../third_party/angle/src/libANGLE/Error.h -FILE: ../../../third_party/angle/src/libANGLE/Error.inc -FILE: ../../../third_party/angle/src/libANGLE/Fence_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/FramebufferAttachment.cpp -FILE: ../../../third_party/angle/src/libANGLE/FramebufferAttachment.h -FILE: ../../../third_party/angle/src/libANGLE/ImageIndex.cpp -FILE: ../../../third_party/angle/src/libANGLE/ImageIndex.h -FILE: ../../../third_party/angle/src/libANGLE/ImageIndexIterator_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/State.cpp -FILE: ../../../third_party/angle/src/libANGLE/State.h -FILE: ../../../third_party/angle/src/libANGLE/Surface_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/TransformFeedback.cpp -FILE: ../../../third_party/angle/src/libANGLE/TransformFeedback.h -FILE: ../../../third_party/angle/src/libANGLE/TransformFeedback_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/VertexAttribute.cpp -FILE: ../../../third_party/angle/src/libANGLE/features.h -FILE: ../../../third_party/angle/src/libANGLE/queryconversions.cpp -FILE: ../../../third_party/angle/src/libANGLE/queryconversions.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/BufferImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/CompilerImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/DisplayImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/DisplayImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/FramebufferImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/RenderbufferImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/SamplerImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ShaderImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/SurfaceImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/TextureImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/TransformFeedbackImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/VertexArrayImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/copyvertex.inc.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/BufferD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/CompilerD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DisplayD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderbufferD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderbufferD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SamplerD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Trim11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Trim11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexArray11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexArray9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SamplerGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SamplerGL.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl.h -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl_ext.cpp -FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl_ext.h -FILE: ../../../third_party/angle/src/libGLESv2/global_state.cpp -FILE: ../../../third_party/angle/src/libGLESv2/global_state.h -FILE: ../../../third_party/angle/util/EGLWindow.h -FILE: ../../../third_party/angle/util/Event.h -FILE: ../../../third_party/angle/util/Matrix.cpp -FILE: ../../../third_party/angle/util/Matrix.h -FILE: ../../../third_party/angle/util/OSWindow.cpp -FILE: ../../../third_party/angle/util/OSWindow.h -FILE: ../../../third_party/angle/util/com_utils.h -FILE: ../../../third_party/angle/util/geometry_utils.cpp -FILE: ../../../third_party/angle/util/geometry_utils.h -FILE: ../../../third_party/angle/util/keyboard.h -FILE: ../../../third_party/angle/util/mouse.h -FILE: ../../../third_party/angle/util/random_utils.cpp -FILE: ../../../third_party/angle/util/random_utils.h -FILE: ../../../third_party/angle/util/shader_utils.cpp -FILE: ../../../third_party/angle/util/shader_utils.h -FILE: ../../../third_party/angle/util/test_utils.h -FILE: ../../../third_party/angle/util/windows/test_utils_win.cpp -FILE: ../../../third_party/angle/util/windows/win32/Win32Window.cpp -FILE: ../../../third_party/angle/util/windows/win32/Win32Window.h -FILE: ../../../third_party/angle/util/windows/win32/test_utils_win32.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2014 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/include/platform/FeaturesD3D.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/include/platform/FeaturesD3D.h -FILE: ../../../third_party/angle/include/platform/FeaturesGL.h -FILE: ../../../third_party/angle/include/platform/PlatformMethods.h -FILE: ../../../third_party/angle/samples/WindowTest/WindowTest.cpp -FILE: ../../../third_party/angle/samples/multi_window/MultiWindow.cpp -FILE: ../../../third_party/angle/src/common/Optional.h -FILE: ../../../third_party/angle/src/common/Optional_unittest.cpp -FILE: ../../../third_party/angle/src/common/bitset_utils.h -FILE: ../../../third_party/angle/src/common/bitset_utils_unittest.cpp -FILE: ../../../third_party/angle/src/common/mathutil_unittest.cpp -FILE: ../../../third_party/angle/src/common/matrix_utils.h -FILE: ../../../third_party/angle/src/common/matrix_utils_unittest.cpp -FILE: ../../../third_party/angle/src/common/string_utils.cpp -FILE: ../../../third_party/angle/src/common/string_utils.h -FILE: ../../../third_party/angle/src/common/string_utils_unittest.cpp -FILE: ../../../third_party/angle/src/common/system_utils_linux.cpp -FILE: ../../../third_party/angle/src/common/system_utils_mac.cpp -FILE: ../../../third_party/angle/src/common/utilities_unittest.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ExtensionGLSL.cpp -FILE: ../../../third_party/angle/src/compiler/translator/ExtensionGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteDoWhile.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RewriteDoWhile.h -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindSymbolNode.cpp -FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindSymbolNode.h -FILE: ../../../third_party/angle/src/image_util/generatemip.inc -FILE: ../../../third_party/angle/src/libANGLE/BinaryStream_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/Debug.cpp -FILE: ../../../third_party/angle/src/libANGLE/Debug.h -FILE: ../../../third_party/angle/src/libANGLE/Device.cpp -FILE: ../../../third_party/angle/src/libANGLE/Device.h -FILE: ../../../third_party/angle/src/libANGLE/HandleAllocator_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/Image.cpp -FILE: ../../../third_party/angle/src/libANGLE/Image.h -FILE: ../../../third_party/angle/src/libANGLE/Image_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/Platform.cpp -FILE: ../../../third_party/angle/src/libANGLE/Program_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/ResourceManager_unittest.cpp -FILE: ../../../third_party/angle/src/libANGLE/VaryingPacking.cpp -FILE: ../../../third_party/angle/src/libANGLE/VaryingPacking.h -FILE: ../../../third_party/angle/src/libANGLE/Version.h -FILE: ../../../third_party/angle/src/libANGLE/Version.inc -FILE: ../../../third_party/angle/src/libANGLE/VertexAttribute.inc -FILE: ../../../third_party/angle/src/libANGLE/histogram_macros.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/BufferImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/DeviceImpl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/DeviceImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/FenceNVImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/FramebufferImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/GLImplFactory.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ImageImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ImageImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/RenderbufferImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/SyncImpl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/TextureImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/TransformFeedbackImpl_mock.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/CompilerD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DeviceD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/EGLImageD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/EGLImageD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/dxgi_support_table.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/dxgi_support_table_autogen.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/DebugAnnotator9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/DebugAnnotator9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/formatutilsD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BlitGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BlitGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BufferGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BufferGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/CompilerGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/CompilerGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DisplayGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DisplayGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FenceNVGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FenceNVGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FramebufferGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FunctionsGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/QueryGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/QueryGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RenderbufferGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RenderbufferGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RendererGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RendererGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ShaderGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ShaderGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/StateManagerGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SurfaceGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SurfaceGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SyncGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SyncGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TextureGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TextureGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/VertexArrayGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/formatutilsgl.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/functionsgl_enums.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/functionsgl_typedefs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/functionsglx_typedefs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/platform_glx.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/D3DTextureSurfaceWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/D3DTextureSurfaceWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/functionswgl_typedefs.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/load_functions_table.h -FILE: ../../../third_party/angle/src/libANGLE/validationEGL.h -FILE: ../../../third_party/angle/util/OSPixmap.h -FILE: ../../../third_party/angle/util/osx/OSXPixmap.h -FILE: ../../../third_party/angle/util/osx/OSXPixmap.mm -FILE: ../../../third_party/angle/util/osx/OSXWindow.h -FILE: ../../../third_party/angle/util/osx/OSXWindow.mm -FILE: ../../../third_party/angle/util/posix/test_utils_posix.cpp -FILE: ../../../third_party/angle/util/windows/win32/Win32Pixmap.cpp -FILE: ../../../third_party/angle/util/windows/win32/Win32Pixmap.h -FILE: ../../../third_party/angle/util/x11/X11Pixmap.cpp -FILE: ../../../third_party/angle/util/x11/X11Pixmap.h -FILE: ../../../third_party/angle/util/x11/X11Window.cpp -FILE: ../../../third_party/angle/util/x11/X11Window.h ----------------------------------------------------------------------------------------------------- -Copyright 2015 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/common/Float16ToFloat32.cpp + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/common/Float16ToFloat32.cpp -FILE: ../../../third_party/angle/src/common/event_tracer.cpp -FILE: ../../../third_party/angle/src/common/event_tracer.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/DiagnosticsBase.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/DiagnosticsBase.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveHandlerBase.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveParser.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/ExpressionParser.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/Lexer.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/Lexer.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/Macro.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/MacroExpander.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/SourceLocation.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/Tokenizer.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/numeric_lex.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.y -FILE: ../../../third_party/angle/src/compiler/translator/Diagnostics.cpp -FILE: ../../../third_party/angle/src/compiler/translator/Diagnostics.h -FILE: ../../../third_party/angle/src/compiler/translator/DirectiveHandler.cpp -FILE: ../../../third_party/angle/src/compiler/translator/DirectiveHandler.h -FILE: ../../../third_party/angle/src/compiler/translator/Pragma.h -FILE: ../../../third_party/angle/src/libANGLE/BinaryStream.h -FILE: ../../../third_party/angle/src/libANGLE/Query.cpp -FILE: ../../../third_party/angle/src/libANGLE/Query.h -FILE: ../../../third_party/angle/src/libANGLE/angletypes.h -FILE: ../../../third_party/angle/src/libANGLE/angletypes.inc -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderTargetD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderTargetD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderExecutableD3D.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderExecutableD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SwapChainD3D.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/IndexBuffer11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/IndexBuffer11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderTarget11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderTarget11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ShaderExecutable11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexBuffer11.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/RenderTarget9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/RenderTarget9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/ShaderCache.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/ShaderExecutable9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/ShaderExecutable9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/Blit.ps -FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/Blit.vs -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.h ----------------------------------------------------------------------------------------------------- -Copyright 2012 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/compiler/preprocessor/DirectiveParser.cpp + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveParser.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/Input.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/Input.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/Macro.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/MacroExpander.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/Preprocessor.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/Preprocessor.h -FILE: ../../../third_party/angle/src/compiler/preprocessor/Token.cpp -FILE: ../../../third_party/angle/src/compiler/preprocessor/Token.h -FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulator.h -FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h -FILE: ../../../third_party/angle/src/compiler/translator/length_limits.h ----------------------------------------------------------------------------------------------------- -Copyright 2011 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_tab_autogen.cpp -TYPE: LicenseType.bison -FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_tab_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/glslang_tab_autogen.cpp -FILE: ../../../third_party/angle/src/compiler/translator/glslang_tab_autogen.h ----------------------------------------------------------------------------------------------------- - -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSLForMetal.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSLForMetal.h -FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSLForMetal.mm -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorMetal.cpp -FILE: ../../../third_party/angle/src/compiler/translator/TranslatorMetal.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/DisplayMtl.mm -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_glslang_utils.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_glslang_utils.mm ----------------------------------------------------------------------------------------------------- -Copyright (c) 2019 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/compiler/translator/ValidateLimitations.h + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/compiler/translator/ValidateLimitations.h -FILE: ../../../third_party/angle/src/compiler/translator/glslang.h -FILE: ../../../third_party/angle/src/compiler/translator/util.cpp -FILE: ../../../third_party/angle/src/libANGLE/Uniform.cpp -FILE: ../../../third_party/angle/src/libANGLE/Uniform.h ----------------------------------------------------------------------------------------------------- -Copyright 2010 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp -FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.h -FILE: ../../../third_party/angle/util/fuchsia/FuchsiaPixmap.cpp -FILE: ../../../third_party/angle/util/fuchsia/FuchsiaPixmap.h ----------------------------------------------------------------------------------------------------- -Copyright The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/blit.metal + ../../../third_party/angle/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/blit.metal -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/clear.metal -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/common.h -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/gen_indices.metal -FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/master_source.metal ----------------------------------------------------------------------------------------------------- -Copyright 2019 The ANGLE Project. 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 TransGaming Inc., Google Inc., 3DLabs Inc. - Ltd., nor the names of their 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: angle -ORIGIN: ../../../third_party/angle/src/third_party/compiler/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/third_party/compiler/ArrayBoundsClamper.cpp -FILE: ../../../third_party/angle/src/third_party/compiler/ArrayBoundsClamper.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 2012 Apple 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: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. 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. - -THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``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 APPLE, INC. 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: angle -ORIGIN: ../../../third_party/angle/src/third_party/libXNVCtrl/LICENSE -TYPE: LicenseType.mit -FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.c -FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrlLib.h -FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/nv_control.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2008 NVIDIA, Corporation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice (including the next -paragraph) shall be included in all copies or substantial portions of the -Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.h -TYPE: LicenseType.mit -FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2010 NVIDIA, Corporation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice (including the next -paragraph) shall be included in all copies or substantial portions of the -Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/src/third_party/systeminfo/SystemInfo.cpp -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/third_party/systeminfo/SystemInfo.cpp -FILE: ../../../third_party/angle/src/third_party/systeminfo/SystemInfo.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 2009 Apple 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: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. 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. - -THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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: angle -ORIGIN: ../../../third_party/angle/src/third_party/trace_event/trace_event.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/third_party/trace_event/trace_event.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013 The Chromium 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: base -LIBRARY: icu -LIBRARY: zlib -ORIGIN: ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_conversions.h -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_conversions_impl.h -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_math.h -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_math_impl.h -FILE: ../../../third_party/icu/icu.isolate -FILE: ../../../third_party/zlib/google/compression_utils.cc -FILE: ../../../third_party/zlib/google/compression_utils.h -FILE: ../../../third_party/zlib/google/compression_utils_unittest.cc ----------------------------------------------------------------------------------------------------- -Copyright 2014 The Chromium 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: base -LIBRARY: zlib -ORIGIN: ../../../third_party/angle/src/common/third_party/base/anglebase/containers/mru_cache.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/containers/mru_cache.h -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/sha1.cc -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/sha1.h -FILE: ../../../third_party/zlib/google/zip.h -FILE: ../../../third_party/zlib/google/zip_internal.cc -FILE: ../../../third_party/zlib/google/zip_internal.h -FILE: ../../../third_party/zlib/google/zip_reader.h -FILE: ../../../third_party/zlib/google/zip_reader_unittest.cc -FILE: ../../../third_party/zlib/google/zip_unittest.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 2011 The Chromium 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: base -LIBRARY: zlib -ORIGIN: ../../../third_party/angle/src/common/third_party/base/anglebase/no_destructor.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/no_destructor.h -FILE: ../../../third_party/zlib/contrib/bench/zlib_bench.cc -FILE: ../../../third_party/zlib/contrib/optimizations/slide_hash_neon.h -FILE: ../../../third_party/zlib/cpu_features.c -FILE: ../../../third_party/zlib/cpu_features.h ----------------------------------------------------------------------------------------------------- -Copyright 2018 The Chromium 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: base -ORIGIN: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc ----------------------------------------------------------------------------------------------------- -Copyright 2013 The Chromium 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: boringssl -LIBRARY: dart -ORIGIN: ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.openssl -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/aes-armv4.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/bsaes-armv7.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha256-armv4.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha512-armv4.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/aes-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S -FILE: ../../../third_party/boringssl/src/crypto/err/asn1.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/bio.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/bn.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/cipher.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/conf.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/dh.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/digest.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/dsa.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/ec.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/ecdh.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/ecdsa.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/engine.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/evp.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/hkdf.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/obj.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/pem.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/pkcs7.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/pkcs8.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/rsa.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/ssl.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/x509.errordata -FILE: ../../../third_party/boringssl/src/crypto/err/x509v3.errordata -FILE: ../../../third_party/boringssl/src/crypto/evp/scrypt.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/fips_shared.lds -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/intcheck1.png -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/intcheck2.png -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/intcheck3.png -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/[Content_Types].xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/_rels/.rels -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/customXml/_rels/item1.xml.rels -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/customXml/item1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/customXml/itemProps1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/docProps/app.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/docProps/core.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/docProps/thumbnail.emf -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/_rels/document.xml.rels -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/document.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/endnotes.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/fontTable.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/footer1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/footnotes.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/media/image1.jpeg -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/media/image2.png -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/numbering.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/settings.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/styles.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/theme/theme1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/webSettings.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/[Content_Types].xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/_rels/.rels -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/customXml/_rels/item1.xml.rels -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/customXml/item1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/customXml/itemProps1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/docProps/app.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/docProps/core.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/docProps/thumbnail.emf -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/_rels/document.xml.rels -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/document.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/endnotes.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/fontTable.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footer1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footer2.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footer3.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footnotes.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/header1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/header2.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/header3.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/media/image1.png -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/media/image2.png -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/numbering.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/settings.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/styles.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/theme/theme1.xml -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/webSettings.xml -FILE: ../../../third_party/boringssl/src/crypto/obj/obj_mac.num -FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_arm_asm.S -FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c -FILE: ../../../third_party/boringssl/src/crypto/x509/charmap.h -FILE: ../../../third_party/boringssl/src/crypto/x509/many_constraints.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/many_names1.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/many_names2.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/many_names3.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/some_names1.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/some_names2.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/some_names3.pem -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_time_test.cc -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ocsp.c -FILE: ../../../third_party/boringssl/src/go.mod -FILE: ../../../third_party/boringssl/src/ssl/bio_ssl.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_c_test.c -FILE: ../../../third_party/boringssl/src/util/all_tests.json -FILE: ../../../third_party/boringssl/src/util/bot/UPDATING -FILE: ../../../third_party/boringssl/src/util/bot/cmake-linux64.tar.gz.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/cmake-mac.tar.gz.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/cmake-win32.zip.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/nasm-win32.exe.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/perl-win32.zip.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/sde-linux64.tar.bz2.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/sde-win32.tar.bz2.sha1 -FILE: ../../../third_party/boringssl/src/util/bot/yasm-win32.exe.sha1 -FILE: ../../../third_party/boringssl/src/util/doc.config -FILE: ../../../third_party/boringssl/src/util/doc.go -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/acvp.go -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/acvp/acvp.go -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/interactive.go -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/parser.peg -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/parser.peg.go -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/hash.go -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/run_cavp.go -FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate.peg.go -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart ----------------------------------------------------------------------------------------------------- -OpenSSL License - - ==================================================================== - Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. 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. - - 3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - - 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - - 5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - - 6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - - THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - EXPRESSED 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 OpenSSL PROJECT OR - ITS 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. - ==================================================================== - - This product includes cryptographic software written by Eric Young - (eay@cryptsoft.com). This product includes software written by Tim - Hudson (tjh@cryptsoft.com). - -Original SSLeay License - -* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) -* All rights reserved. - -* This package is an SSL implementation written -* by Eric Young (eay@cryptsoft.com). -* The implementation was written so as to conform with Netscapes SSL. - -* This library is free for commercial and non-commercial use as long as -* the following conditions are aheared to. The following conditions -* apply to all code found in this distribution, be it the RC4, RSA, -* lhash, DES, etc., code; not just the SSL code. The SSL documentation -* included with this distribution is covered by the same copyright terms -* except that the holder is Tim Hudson (tjh@cryptsoft.com). - -* Copyright remains Eric Young's, and as such any Copyright notices in -* the code are not to be removed. -* If this package is used in a product, Eric Young should be given attribution -* as the author of the parts of the library used. -* This can be in the form of a textual message at program startup or -* in documentation (online or textual) provided with the package. - -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* 1. Redistributions of source code must retain the copyright -* notice, this list of conditions and the following disclaimer. -* 2. 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. -* 3. All advertising materials mentioning features or use of this software -* must display the following acknowledgement: -* "This product includes cryptographic software written by -* Eric Young (eay@cryptsoft.com)" -* The word 'cryptographic' can be left out if the rouines from the library -* being used are not cryptographic related :-). -* 4. If you include any Windows specific code (or a derivative thereof) from -* the apps directory (application code) you must include an acknowledgement: -* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - -* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. - -* The licence and distribution terms for any publically available version or -* derivative of this code cannot be changed. i.e. this code cannot simply be -* copied and put under another distribution licence -* [including the GNU Public Licence.] - -ISC license used for completely new code in BoringSSL: - -/* Copyright (c) 2015, Google Inc. - - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -The code in third_party/fiat carries the MIT license: - -Copyright (c) 2015-2016 the fiat-crypto authors (see -https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS). - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -The code in third_party/sike also carries the MIT license: - -Copyright (c) Microsoft Corporation. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE - -Licenses for support code - -Parts of the TLS test suite are under the Go license. This code is not included -in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so -distributing code linked against BoringSSL does not trigger this license: - -Copyright (c) 2009 The Go 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: boringssl -ORIGIN: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S ----------------------------------------------------------------------------------------------------- -Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/aes-armv4.S + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/aes-armv4.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha256-armv4.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha512-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/aes-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S ----------------------------------------------------------------------------------------------------- -Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/bsaes-armv7.S + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/bsaes-armv7.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S ----------------------------------------------------------------------------------------------------- -Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/a_bitstr.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_bitstr.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_bool.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_d2i_fp.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_dup.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_enum.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_gentm.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_i2d_fp.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_int.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_mbstr.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_object.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_octet.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_print.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_strnid.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_time.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_type.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_utctm.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/a_utf8.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_lib.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_par.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/asn_pack.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/f_enum.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/f_int.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/f_string.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_dec.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_enc.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_fre.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_new.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_typ.c -FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_utl.c -FILE: ../../../third_party/boringssl/src/crypto/base64/base64.c -FILE: ../../../third_party/boringssl/src/crypto/bio/bio.c -FILE: ../../../third_party/boringssl/src/crypto/bio/bio_mem.c -FILE: ../../../third_party/boringssl/src/crypto/bio/connect.c -FILE: ../../../third_party/boringssl/src/crypto/bio/fd.c -FILE: ../../../third_party/boringssl/src/crypto/bio/file.c -FILE: ../../../third_party/boringssl/src/crypto/bio/hexdump.c -FILE: ../../../third_party/boringssl/src/crypto/bio/internal.h -FILE: ../../../third_party/boringssl/src/crypto/bio/printf.c -FILE: ../../../third_party/boringssl/src/crypto/bio/socket.c -FILE: ../../../third_party/boringssl/src/crypto/bn_extra/convert.c -FILE: ../../../third_party/boringssl/src/crypto/buf/buf.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/cipher_extra.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/derive_key.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_null.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_rc2.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_rc4.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/internal.h -FILE: ../../../third_party/boringssl/src/crypto/conf/conf.c -FILE: ../../../third_party/boringssl/src/crypto/conf/conf_def.h -FILE: ../../../third_party/boringssl/src/crypto/cpu-intel.c -FILE: ../../../third_party/boringssl/src/crypto/dh/check.c -FILE: ../../../third_party/boringssl/src/crypto/dh/dh.c -FILE: ../../../third_party/boringssl/src/crypto/dh/dh_test.cc -FILE: ../../../third_party/boringssl/src/crypto/digest_extra/digest_extra.c -FILE: ../../../third_party/boringssl/src/crypto/dsa/dsa.c -FILE: ../../../third_party/boringssl/src/crypto/dsa/dsa_test.cc -FILE: ../../../third_party/boringssl/src/crypto/err/err.c -FILE: ../../../third_party/boringssl/src/crypto/evp/evp.c -FILE: ../../../third_party/boringssl/src/crypto/evp/evp_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/evp/evp_ctx.c -FILE: ../../../third_party/boringssl/src/crypto/evp/internal.h -FILE: ../../../third_party/boringssl/src/crypto/evp/sign.c -FILE: ../../../third_party/boringssl/src/crypto/ex_data.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/add.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bn.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bn_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bytes.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/cmp.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/div.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/generic.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/mul.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/prime.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/random.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/shift.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/cipher.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/e_des.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/des/des.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/des/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/digest.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/digests.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/hmac/hmac.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md4/md4.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md5/md5.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/blinding.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/rsa.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha1-altivec.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha1.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha256.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha512.c -FILE: ../../../third_party/boringssl/src/crypto/hmac_extra/hmac_test.cc -FILE: ../../../third_party/boringssl/src/crypto/internal.h -FILE: ../../../third_party/boringssl/src/crypto/lhash/lhash.c -FILE: ../../../third_party/boringssl/src/crypto/mem.c -FILE: ../../../third_party/boringssl/src/crypto/obj/obj.c -FILE: ../../../third_party/boringssl/src/crypto/obj/obj_xref.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_all.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_info.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_lib.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_oth.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_pk8.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_pkey.c -FILE: ../../../third_party/boringssl/src/crypto/rc4/rc4.c -FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_test.cc -FILE: ../../../third_party/boringssl/src/crypto/stack/stack.c -FILE: ../../../third_party/boringssl/src/crypto/thread.c -FILE: ../../../third_party/boringssl/src/crypto/x509/a_digest.c -FILE: ../../../third_party/boringssl/src/crypto/x509/a_sign.c -FILE: ../../../third_party/boringssl/src/crypto/x509/a_strex.c -FILE: ../../../third_party/boringssl/src/crypto/x509/a_verify.c -FILE: ../../../third_party/boringssl/src/crypto/x509/algorithm.c -FILE: ../../../third_party/boringssl/src/crypto/x509/asn1_gen.c -FILE: ../../../third_party/boringssl/src/crypto/x509/by_dir.c -FILE: ../../../third_party/boringssl/src/crypto/x509/by_file.c -FILE: ../../../third_party/boringssl/src/crypto/x509/i2d_pr.c -FILE: ../../../third_party/boringssl/src/crypto/x509/t_crl.c -FILE: ../../../third_party/boringssl/src/crypto/x509/t_req.c -FILE: ../../../third_party/boringssl/src/crypto/x509/t_x509.c -FILE: ../../../third_party/boringssl/src/crypto/x509/t_x509a.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_att.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_cmp.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_d2.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_def.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_ext.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_lu.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_obj.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_r2x.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_req.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_set.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_txt.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_v3.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_vfy.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509name.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509rset.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_all.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_attrib.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_crl.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_exten.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_info.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_name.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_pkey.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_pubkey.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_req.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_sig.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_spki.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_val.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_x509.c -FILE: ../../../third_party/boringssl/src/decrepit/bio/base64_bio.c -FILE: ../../../third_party/boringssl/src/decrepit/blowfish/blowfish.c -FILE: ../../../third_party/boringssl/src/decrepit/cast/cast.c -FILE: ../../../third_party/boringssl/src/decrepit/cast/cast_tables.c -FILE: ../../../third_party/boringssl/src/decrepit/cast/internal.h -FILE: ../../../third_party/boringssl/src/decrepit/des/cfb64ede.c -FILE: ../../../third_party/boringssl/src/decrepit/macros.h -FILE: ../../../third_party/boringssl/src/decrepit/rc4/rc4_decrepit.c -FILE: ../../../third_party/boringssl/src/decrepit/ripemd/internal.h -FILE: ../../../third_party/boringssl/src/decrepit/ripemd/ripemd.c -FILE: ../../../third_party/boringssl/src/decrepit/rsa/rsa_decrepit.c -FILE: ../../../third_party/boringssl/src/decrepit/ssl/ssl_decrepit.c -FILE: ../../../third_party/boringssl/src/include/openssl/asn1.h -FILE: ../../../third_party/boringssl/src/include/openssl/base64.h -FILE: ../../../third_party/boringssl/src/include/openssl/bio.h -FILE: ../../../third_party/boringssl/src/include/openssl/blowfish.h -FILE: ../../../third_party/boringssl/src/include/openssl/buf.h -FILE: ../../../third_party/boringssl/src/include/openssl/cast.h -FILE: ../../../third_party/boringssl/src/include/openssl/cipher.h -FILE: ../../../third_party/boringssl/src/include/openssl/conf.h -FILE: ../../../third_party/boringssl/src/include/openssl/cpu.h -FILE: ../../../third_party/boringssl/src/include/openssl/des.h -FILE: ../../../third_party/boringssl/src/include/openssl/dh.h -FILE: ../../../third_party/boringssl/src/include/openssl/digest.h -FILE: ../../../third_party/boringssl/src/include/openssl/dsa.h -FILE: ../../../third_party/boringssl/src/include/openssl/err.h -FILE: ../../../third_party/boringssl/src/include/openssl/evp.h -FILE: ../../../third_party/boringssl/src/include/openssl/ex_data.h -FILE: ../../../third_party/boringssl/src/include/openssl/hmac.h -FILE: ../../../third_party/boringssl/src/include/openssl/lhash.h -FILE: ../../../third_party/boringssl/src/include/openssl/md4.h -FILE: ../../../third_party/boringssl/src/include/openssl/md5.h -FILE: ../../../third_party/boringssl/src/include/openssl/mem.h -FILE: ../../../third_party/boringssl/src/include/openssl/obj.h -FILE: ../../../third_party/boringssl/src/include/openssl/rc4.h -FILE: ../../../third_party/boringssl/src/include/openssl/ripemd.h -FILE: ../../../third_party/boringssl/src/include/openssl/rsa.h -FILE: ../../../third_party/boringssl/src/include/openssl/sha.h -FILE: ../../../third_party/boringssl/src/include/openssl/ssl.h -FILE: ../../../third_party/boringssl/src/include/openssl/ssl3.h -FILE: ../../../third_party/boringssl/src/include/openssl/stack.h -FILE: ../../../third_party/boringssl/src/include/openssl/thread.h -FILE: ../../../third_party/boringssl/src/include/openssl/tls1.h -FILE: ../../../third_party/boringssl/src/include/openssl/type_check.h -FILE: ../../../third_party/boringssl/src/include/openssl/x509.h -FILE: ../../../third_party/boringssl/src/include/openssl/x509_vfy.h -FILE: ../../../third_party/boringssl/src/ssl/d1_both.cc -FILE: ../../../third_party/boringssl/src/ssl/d1_pkt.cc -FILE: ../../../third_party/boringssl/src/ssl/d1_srtp.cc -FILE: ../../../third_party/boringssl/src/ssl/dtls_record.cc -FILE: ../../../third_party/boringssl/src/ssl/handshake.cc -FILE: ../../../third_party/boringssl/src/ssl/handshake_client.cc -FILE: ../../../third_party/boringssl/src/ssl/handshake_server.cc -FILE: ../../../third_party/boringssl/src/ssl/internal.h -FILE: ../../../third_party/boringssl/src/ssl/s3_both.cc -FILE: ../../../third_party/boringssl/src/ssl/s3_lib.cc -FILE: ../../../third_party/boringssl/src/ssl/s3_pkt.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_asn1.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_cert.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_cipher.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_file.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_lib.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_privkey.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_session.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_stat.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_transcript.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_x509.cc -FILE: ../../../third_party/boringssl/src/ssl/t1_enc.cc -FILE: ../../../third_party/boringssl/src/ssl/t1_lib.cc -FILE: ../../../third_party/boringssl/src/ssl/tls_method.cc -FILE: ../../../third_party/boringssl/src/ssl/tls_record.cc ----------------------------------------------------------------------------------------------------- -Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) -All rights reserved. - -This package is an SSL implementation written -by Eric Young (eay@cryptsoft.com). -The implementation was written so as to conform with Netscapes SSL. - -This library is free for commercial and non-commercial use as long as -the following conditions are aheared to. The following conditions -apply to all code found in this distribution, be it the RC4, RSA, -lhash, DES, etc., code; not just the SSL code. The SSL documentation -included with this distribution is covered by the same copyright terms -except that the holder is Tim Hudson (tjh@cryptsoft.com). - -Copyright remains Eric Young's, and as such any Copyright notices in -the code are not to be removed. -If this package is used in a product, Eric Young should be given attribution -as the author of the parts of the library used. -This can be in the form of a textual message at program startup or -in documentation (online or textual) provided with the package. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the copyright - notice, this list of conditions and the following disclaimer. -2. 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. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - "This product includes cryptographic software written by - Eric Young (eay@cryptsoft.com)" - The word 'cryptographic' can be left out if the rouines from the library - being used are not cryptographic related :-). -4. If you include any Windows specific code (or a derivative thereof) from - the apps directory (application code) you must include an acknowledgement: - "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - -THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. - -The licence and distribution terms for any publically available version or -derivative of this code cannot be changed. i.e. this code cannot simply be -copied and put under another distribution licence -[including the GNU Public Licence.] -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/asn1_locl.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_locl.h -FILE: ../../../third_party/boringssl/src/crypto/evp/p_dsa_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/evp/p_ec.c -FILE: ../../../third_party/boringssl/src/crypto/evp/p_ec_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/evp/p_rsa.c -FILE: ../../../third_party/boringssl/src/crypto/evp/p_rsa_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/evp/print.c -FILE: ../../../third_party/boringssl/src/crypto/x509/rsa_pss.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2006 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/asn1_test.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_test.cc -FILE: ../../../third_party/boringssl/src/crypto/bytestring/asn1_compat.c -FILE: ../../../third_party/boringssl/src/crypto/chacha/chacha_test.cc -FILE: ../../../third_party/boringssl/src/crypto/cpu-aarch64-linux.c -FILE: ../../../third_party/boringssl/src/crypto/cpu-arm-linux.c -FILE: ../../../third_party/boringssl/src/crypto/cpu-ppc64le.c -FILE: ../../../third_party/boringssl/src/crypto/curve25519/spake25519.c -FILE: ../../../third_party/boringssl/src/crypto/curve25519/spake25519_test.cc -FILE: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/check_bn_tests.go -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/polyval.c -FILE: ../../../third_party/boringssl/src/crypto/obj/obj_test.cc -FILE: ../../../third_party/boringssl/src/crypto/obj/objects.go -FILE: ../../../third_party/boringssl/src/crypto/poly1305/internal.h -FILE: ../../../third_party/boringssl/src/crypto/pool/internal.h -FILE: ../../../third_party/boringssl/src/crypto/pool/pool.c -FILE: ../../../third_party/boringssl/src/crypto/pool/pool_test.cc -FILE: ../../../third_party/boringssl/src/crypto/rand_extra/deterministic.c -FILE: ../../../third_party/boringssl/src/crypto/x509/internal.h -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_test.cc -FILE: ../../../third_party/boringssl/src/decrepit/evp/dss1.c -FILE: ../../../third_party/boringssl/src/decrepit/evp/evp_do_all.c -FILE: ../../../third_party/boringssl/src/decrepit/obj/obj_decrepit.c -FILE: ../../../third_party/boringssl/src/decrepit/ripemd/ripemd_test.cc -FILE: ../../../third_party/boringssl/src/decrepit/x509/x509_decrepit.c -FILE: ../../../third_party/boringssl/src/include/openssl/asn1_mac.h -FILE: ../../../third_party/boringssl/src/include/openssl/obj_mac.h -FILE: ../../../third_party/boringssl/src/include/openssl/pool.h -FILE: ../../../third_party/boringssl/src/ssl/tls13_both.cc -FILE: ../../../third_party/boringssl/src/ssl/tls13_client.cc -FILE: ../../../third_party/boringssl/src/ssl/tls13_enc.cc -FILE: ../../../third_party/boringssl/src/ssl/tls13_server.cc -FILE: ../../../third_party/boringssl/src/util/BUILD.toplevel -FILE: ../../../third_party/boringssl/src/util/diff_asm.go -FILE: ../../../third_party/boringssl/src/util/run_android_tests.go ----------------------------------------------------------------------------------------------------- -Copyright (c) 2016, Google Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/time_support.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/asn1/time_support.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_x509.c -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_xaux.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509cset.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2001 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/base64/base64_test.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/base64/base64_test.cc -FILE: ../../../third_party/boringssl/src/crypto/bio/bio_test.cc -FILE: ../../../third_party/boringssl/src/crypto/bio/socket_helper.c -FILE: ../../../third_party/boringssl/src/crypto/bytestring/ber.c -FILE: ../../../third_party/boringssl/src/crypto/bytestring/bytestring_test.cc -FILE: ../../../third_party/boringssl/src/crypto/bytestring/cbb.c -FILE: ../../../third_party/boringssl/src/crypto/bytestring/cbs.c -FILE: ../../../third_party/boringssl/src/crypto/bytestring/internal.h -FILE: ../../../third_party/boringssl/src/crypto/chacha/chacha.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/aead_test.cc -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_chacha20poly1305.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_tls.c -FILE: ../../../third_party/boringssl/src/crypto/cpu-arm.c -FILE: ../../../third_party/boringssl/src/crypto/crypto.c -FILE: ../../../third_party/boringssl/src/crypto/digest_extra/digest_test.cc -FILE: ../../../third_party/boringssl/src/crypto/engine/engine.c -FILE: ../../../third_party/boringssl/src/crypto/err/err_test.cc -FILE: ../../../third_party/boringssl/src/crypto/evp/evp_extra_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/aead.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/rand.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/urandom.c -FILE: ../../../third_party/boringssl/src/crypto/hkdf/hkdf.c -FILE: ../../../third_party/boringssl/src/crypto/hkdf/hkdf_test.cc -FILE: ../../../third_party/boringssl/src/crypto/lhash/lhash_test.cc -FILE: ../../../third_party/boringssl/src/crypto/pkcs7/pkcs7.c -FILE: ../../../third_party/boringssl/src/crypto/pkcs7/pkcs7_test.cc -FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs12_test.cc -FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305.c -FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_arm.c -FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_vec.c -FILE: ../../../third_party/boringssl/src/crypto/rand_extra/windows.c -FILE: ../../../third_party/boringssl/src/include/openssl/aead.h -FILE: ../../../third_party/boringssl/src/include/openssl/bytestring.h -FILE: ../../../third_party/boringssl/src/include/openssl/chacha.h -FILE: ../../../third_party/boringssl/src/include/openssl/crypto.h -FILE: ../../../third_party/boringssl/src/include/openssl/engine.h -FILE: ../../../third_party/boringssl/src/include/openssl/hkdf.h -FILE: ../../../third_party/boringssl/src/include/openssl/objects.h -FILE: ../../../third_party/boringssl/src/include/openssl/opensslconf.h -FILE: ../../../third_party/boringssl/src/include/openssl/opensslv.h -FILE: ../../../third_party/boringssl/src/include/openssl/ossl_typ.h -FILE: ../../../third_party/boringssl/src/include/openssl/pkcs12.h -FILE: ../../../third_party/boringssl/src/include/openssl/pkcs7.h -FILE: ../../../third_party/boringssl/src/include/openssl/poly1305.h -FILE: ../../../third_party/boringssl/src/include/openssl/rand.h -FILE: ../../../third_party/boringssl/src/include/openssl/safestack.h -FILE: ../../../third_party/boringssl/src/ssl/ssl_test.cc -FILE: ../../../third_party/boringssl/src/tool/args.cc -FILE: ../../../third_party/boringssl/src/tool/client.cc -FILE: ../../../third_party/boringssl/src/tool/const.cc -FILE: ../../../third_party/boringssl/src/tool/digest.cc -FILE: ../../../third_party/boringssl/src/tool/internal.h -FILE: ../../../third_party/boringssl/src/tool/pkcs12.cc -FILE: ../../../third_party/boringssl/src/tool/server.cc -FILE: ../../../third_party/boringssl/src/tool/speed.cc -FILE: ../../../third_party/boringssl/src/tool/tool.cc -FILE: ../../../third_party/boringssl/src/tool/transport_common.cc -FILE: ../../../third_party/boringssl/src/tool/transport_common.h -FILE: ../../../third_party/boringssl/src/util/make_errors.go ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014, Google Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/bio/pair.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/bio/pair.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2003 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/bn_extra/bn_asn1.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/err_data.c -FILE: ../../../third_party/boringssl/src/crypto/bn_extra/bn_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/cmac/cmac_test.cc -FILE: ../../../third_party/boringssl/src/crypto/conf/internal.h -FILE: ../../../third_party/boringssl/src/crypto/curve25519/asm/x25519-asm-arm.S -FILE: ../../../third_party/boringssl/src/crypto/curve25519/ed25519_test.cc -FILE: ../../../third_party/boringssl/src/crypto/curve25519/x25519_test.cc -FILE: ../../../third_party/boringssl/src/crypto/err/err_data_generate.go -FILE: ../../../third_party/boringssl/src/crypto/evp/pbkdf_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p224-64.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/util.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/internal.h -FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs8_test.cc -FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_test.cc -FILE: ../../../third_party/boringssl/src/crypto/refcount_c11.c -FILE: ../../../third_party/boringssl/src/crypto/refcount_lock.c -FILE: ../../../third_party/boringssl/src/crypto/refcount_test.cc -FILE: ../../../third_party/boringssl/src/crypto/thread_none.c -FILE: ../../../third_party/boringssl/src/crypto/thread_pthread.c -FILE: ../../../third_party/boringssl/src/crypto/thread_test.cc -FILE: ../../../third_party/boringssl/src/crypto/thread_win.c -FILE: ../../../third_party/boringssl/src/include/openssl/buffer.h -FILE: ../../../third_party/boringssl/src/include/openssl/cmac.h -FILE: ../../../third_party/boringssl/src/include/openssl/curve25519.h -FILE: ../../../third_party/boringssl/src/include/openssl/dtls1.h -FILE: ../../../third_party/boringssl/src/include/openssl/srtp.h -FILE: ../../../third_party/boringssl/src/ssl/ssl_aead_ctx.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_buffer.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_key_share.cc -FILE: ../../../third_party/boringssl/src/tool/ciphers.cc -FILE: ../../../third_party/boringssl/src/tool/generate_ed25519.cc -FILE: ../../../third_party/boringssl/src/tool/genrsa.cc -FILE: ../../../third_party/boringssl/src/tool/rand.cc -FILE: ../../../third_party/boringssl/src/util/all_tests.go -FILE: ../../../third_party/boringssl/src/util/bot/DEPS ----------------------------------------------------------------------------------------------------- -Copyright (c) 2015, Google Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/buf/buf_test.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/crypto_test_data.cc -FILE: ../../../third_party/boringssl/src/crypto/buf/buf_test.cc -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesctrhmac.c -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesgcmsiv.c -FILE: ../../../third_party/boringssl/src/crypto/compiler_test.cc -FILE: ../../../third_party/boringssl/src/crypto/err/internal.h -FILE: ../../../third_party/boringssl/src/crypto/evp/p_ed25519.c -FILE: ../../../third_party/boringssl/src/crypto/evp/p_ed25519_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/evp/scrypt_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bcm.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bn_test_to_fuzzer.go -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/delocate.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/is_fips.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/ctrdrbg.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/ctrdrbg_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/self_check/self_check.c -FILE: ../../../third_party/boringssl/src/crypto/pkcs7/internal.h -FILE: ../../../third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.c -FILE: ../../../third_party/boringssl/src/crypto/rand_extra/forkunsafe.c -FILE: ../../../third_party/boringssl/src/crypto/rand_extra/fuchsia.c -FILE: ../../../third_party/boringssl/src/crypto/rand_extra/rand_extra.c -FILE: ../../../third_party/boringssl/src/crypto/x509/make_many_constraints.go -FILE: ../../../third_party/boringssl/src/decrepit/cfb/cfb.c -FILE: ../../../third_party/boringssl/src/decrepit/cfb/cfb_test.cc -FILE: ../../../third_party/boringssl/src/include/openssl/is_boringssl.h -FILE: ../../../third_party/boringssl/src/include/openssl/span.h -FILE: ../../../third_party/boringssl/src/ssl/span_test.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_versions.cc -FILE: ../../../third_party/boringssl/src/tool/file.cc -FILE: ../../../third_party/boringssl/src/tool/sign.cc -FILE: ../../../third_party/boringssl/src/util/ar/ar.go -FILE: ../../../third_party/boringssl/src/util/check_imported_libraries.go -FILE: ../../../third_party/boringssl/src/util/convert_comments.go -FILE: ../../../third_party/boringssl/src/util/embed_test_data.go -FILE: ../../../third_party/boringssl/src/util/fipstools/break-hash.go -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_aes_gcm_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_aes_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ctr_drbg_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_keypair_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_pkv_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_siggen_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_sigver_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_hmac_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_keywrap_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_main.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_rsa2_keygen_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_rsa2_siggen_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_rsa2_sigver_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_sha_monte_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_sha_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_tdes_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_test_util.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_test_util.h -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/test_fips.c -FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate.go -FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate.peg -FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate_test.go -FILE: ../../../third_party/boringssl/src/util/fipstools/fipscommon/const.go -FILE: ../../../third_party/boringssl/src/util/fipstools/inject_hash/inject_hash.go ----------------------------------------------------------------------------------------------------- -Copyright (c) 2017, Google Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/bytestring/unicode.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/abi_self_test.cc -FILE: ../../../third_party/boringssl/src/crypto/bytestring/unicode.c -FILE: ../../../third_party/boringssl/src/crypto/chacha/internal.h -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesccm.c -FILE: ../../../third_party/boringssl/src/crypto/cpu-aarch64-fuchsia.c -FILE: ../../../third_party/boringssl/src/crypto/cpu-arm-linux.h -FILE: ../../../third_party/boringssl/src/crypto/cpu-arm-linux_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/div_extra.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/felem.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_ec_scalar_base_mult_tests.go -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_p256-x86_64-table.go -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_p256-x86_64-tests.go -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/scalar.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/simple_mul.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md5/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md5/md5_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/tls/internal.h -FILE: ../../../third_party/boringssl/src/crypto/hrss/hrss.c -FILE: ../../../third_party/boringssl/src/crypto/hrss/hrss_test.cc -FILE: ../../../third_party/boringssl/src/crypto/hrss/internal.h -FILE: ../../../third_party/boringssl/src/crypto/impl_dispatch_test.cc -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_test.cc -FILE: ../../../third_party/boringssl/src/crypto/rand_extra/rand_test.cc -FILE: ../../../third_party/boringssl/src/crypto/self_test.cc -FILE: ../../../third_party/boringssl/src/crypto/stack/stack_test.cc -FILE: ../../../third_party/boringssl/src/crypto/x509v3/internal.h -FILE: ../../../third_party/boringssl/src/include/openssl/e_os2.h -FILE: ../../../third_party/boringssl/src/include/openssl/hrss.h -FILE: ../../../third_party/boringssl/src/ssl/handoff.cc -FILE: ../../../third_party/boringssl/src/third_party/sike/sike_test.cc -FILE: ../../../third_party/boringssl/src/util/ar/ar_test.go -FILE: ../../../third_party/boringssl/src/util/check_filenames.go -FILE: ../../../third_party/boringssl/src/util/convert_wycheproof.go -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_kas_test.cc -FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_tlskdf_test.cc -FILE: ../../../third_party/boringssl/src/util/godeps.go -FILE: ../../../third_party/boringssl/src/util/make_prefix_headers.go -FILE: ../../../third_party/boringssl/src/util/read_symbols.go -FILE: ../../../third_party/boringssl/src/util/testresult/testresult.go ----------------------------------------------------------------------------------------------------- -Copyright (c) 2018, Google Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/cipher_extra/cipher_test.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/cipher_test.cc -FILE: ../../../third_party/boringssl/src/crypto/evp/evp_test.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 2015 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/cipher_extra/tls_cbc.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/tls_cbc.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2012 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/cmac/cmac.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/cmac/cmac.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2010 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/constant_time_test.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/constant_time_test.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the copyright - notice, this list of conditions and the following disclaimer. -2. 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. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - "This product includes cryptographic software written by - Eric Young (eay@cryptsoft.com)" - The word 'cryptographic' can be left out if the rouines from the library - being used are not cryptographic related :-). -4. If you include any Windows specific code (or a derivative thereof) from - the apps directory (application code) you must include an acknowledgement: - "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - -THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. - -The licence and distribution terms for any publically available version or -derivative of this code cannot be changed. i.e. this code cannot simply be -copied and put under another distribution licence -[including the GNU Public Licence.] -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/dh/dh_asn1.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/dh/dh_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/dsa/dsa_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_asn1.c -FILE: ../../../third_party/boringssl/src/include/openssl/asn1t.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2000-2005 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/dh/params.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/dh/params.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2011 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/ec_extra/ec_asn1.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/ec_extra/ec_asn1.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2000-2003 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdh/ecdh.c -FILE: ../../../third_party/boringssl/src/include/openssl/ecdh.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2000-2002 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c + ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdh/ecdh.c -FILE: ../../../third_party/boringssl/src/include/openssl/ecdh.h ----------------------------------------------------------------------------------------------------- -Copyright 2002 Sun Microsystems, 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: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/ecdsa_extra/ecdsa_asn1.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/ecdsa_extra/ecdsa_asn1.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc -FILE: ../../../third_party/boringssl/src/include/openssl/ecdsa.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/err/err.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/err/err.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/blinding.c -FILE: ../../../third_party/boringssl/src/include/openssl/bn.h -FILE: ../../../third_party/boringssl/src/include/openssl/err.h -FILE: ../../../third_party/boringssl/src/include/openssl/tls1.h -FILE: ../../../third_party/boringssl/src/ssl/d1_srtp.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_session.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/evp/digestsign.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/evp/digestsign.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2006,2007 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/evp/pbkdf.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/evp/pbkdf.c -FILE: ../../../third_party/boringssl/src/crypto/pkcs8/internal.h -FILE: ../../../third_party/boringssl/src/crypto/pkcs8/p5_pbev2.c -FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs8.c -FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs8_x509.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_trs.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x509spki.c -FILE: ../../../third_party/boringssl/src/crypto/x509/x_x509a.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/tab_test.cc -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_akey.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_akeya.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_bcons.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_bitst.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_enum.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_extku.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ia5.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_info.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_lib.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pku.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_prn.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_skey.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_sxnet.c -FILE: ../../../third_party/boringssl/src/include/openssl/pkcs8.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/evp/scrypt.c + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/evp/scrypt.c ----------------------------------------------------------------------------------------------------- -Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/mode_wrappers.c -FILE: ../../../third_party/boringssl/src/include/openssl/aes.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/key_wrap.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/key_wrap.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/e_aes.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2001-2011 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/ctx.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/ctx.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_montgomery.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/oct.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/simple.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/wnaf.c -FILE: ../../../third_party/boringssl/src/include/openssl/ec.h -FILE: ../../../third_party/boringssl/src/include/openssl/ec_key.h -FILE: ../../../third_party/boringssl/src/ssl/d1_both.cc -FILE: ../../../third_party/boringssl/src/ssl/d1_pkt.cc -FILE: ../../../third_party/boringssl/src/ssl/dtls_record.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/ex_data.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/prime.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/random.c -FILE: ../../../third_party/boringssl/src/crypto/internal.h -FILE: ../../../third_party/boringssl/src/include/openssl/base.h -FILE: ../../../third_party/boringssl/src/include/openssl/ex_data.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/internal.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/internal.h -FILE: ../../../third_party/boringssl/src/crypto/obj/obj_dat.h -FILE: ../../../third_party/boringssl/src/include/openssl/bn.h -FILE: ../../../third_party/boringssl/src/include/openssl/nid.h -FILE: ../../../third_party/boringssl/src/include/openssl/pem.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) -All rights reserved. - -This package is an SSL implementation written -by Eric Young (eay@cryptsoft.com). -The implementation was written so as to conform with Netscapes SSL. - -This library is free for commercial and non-commercial use as long as -the following conditions are aheared to. The following conditions -apply to all code found in this distribution, be it the RC4, RSA, -lhash, DES, etc., code; not just the SSL code. The SSL documentation -included with this distribution is covered by the same copyright terms -except that the holder is Tim Hudson (tjh@cryptsoft.com). - -Copyright remains Eric Young's, and as such any Copyright notices in -the code are not to be removed. -If this package is used in a product, Eric Young should be given attribution -as the author of the parts of the library used. -This can be in the form of a textual message at program startup or -in documentation (online or textual) provided with the package. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the copyright - notice, this list of conditions and the following disclaimer. -2. 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. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - "This product includes cryptographic software written by - Eric Young (eay@cryptsoft.com)" - The word 'cryptographic' can be left out if the rouines from the library - being used are not cryptographic related :-). -4. If you include any Windows specific code (or a derivative thereof) from - the apps directory (application code) you must include an acknowledgement: - "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - -THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. - -The licence and distribution terms for any publically available version or -derivative of this code cannot be changed. i.e. this code cannot simply be -copied and put under another distribution licence -[including the GNU Public Licence.] -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/jacobi.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/jacobi.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/sqrt.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery_inv.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery_inv.c ----------------------------------------------------------------------------------------------------- -Copyright 2016 Brian Smith. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.c + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.h ----------------------------------------------------------------------------------------------------- -Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. -Copyright (c) 2012, Intel Corporation. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/md32_common.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/md32_common.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec.c + ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_montgomery.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/oct.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/simple.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/wnaf.c -FILE: ../../../third_party/boringssl/src/include/openssl/ec.h -FILE: ../../../third_party/boringssl/src/include/openssl/ec_key.h ----------------------------------------------------------------------------------------------------- -Copyright 2002 Sun Microsystems, 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: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64-table.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64-table.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2015, Intel Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.c + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.h ----------------------------------------------------------------------------------------------------- -Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. -Copyright (c) 2014, Intel Corporation. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/fips_shared_support.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/fips_shared_support.c -FILE: ../../../third_party/boringssl/src/crypto/siphash/siphash.c -FILE: ../../../third_party/boringssl/src/crypto/siphash/siphash_test.cc -FILE: ../../../third_party/boringssl/src/decrepit/blowfish/blowfish_test.cc -FILE: ../../../third_party/boringssl/src/decrepit/cast/cast_test.cc -FILE: ../../../third_party/boringssl/src/include/openssl/siphash.h -FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 2019, Google Inc. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/cbc.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/cbc.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/cfb.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/ctr.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/gcm.c -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/gcm_test.cc -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/internal.h -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/ofb.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2008 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/ccm.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/ccm.c -FILE: ../../../third_party/boringssl/src/decrepit/xts/xts.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2011 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/padding.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/padding.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2005 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/tls/kdf.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/tls/kdf.c -FILE: ../../../third_party/boringssl/src/decrepit/ssl/ssl_decrepit.c -FILE: ../../../third_party/boringssl/src/include/openssl/ssl.h -FILE: ../../../third_party/boringssl/src/ssl/handshake_client.cc -FILE: ../../../third_party/boringssl/src/ssl/handshake_server.cc -FILE: ../../../third_party/boringssl/src/ssl/internal.h -FILE: ../../../third_party/boringssl/src/ssl/s3_lib.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_cert.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_cipher.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_file.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_lib.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_transcript.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_x509.cc -FILE: ../../../third_party/boringssl/src/ssl/t1_enc.cc -FILE: ../../../third_party/boringssl/src/ssl/t1_lib.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/hrss/asm/poly_rq_mul.S -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/hrss/asm/poly_rq_mul.S ----------------------------------------------------------------------------------------------------- -Copyright (c) 2017, the HRSS authors. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/pem/pem_all.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/pem/pem_all.c -FILE: ../../../third_party/boringssl/src/decrepit/dh/dh_decrepit.c -FILE: ../../../third_party/boringssl/src/decrepit/dsa/dsa_decrepit.c -FILE: ../../../third_party/boringssl/src/include/openssl/ssl3.h -FILE: ../../../third_party/boringssl/src/ssl/handshake.cc -FILE: ../../../third_party/boringssl/src/ssl/s3_both.cc -FILE: ../../../third_party/boringssl/src/ssl/s3_pkt.cc -FILE: ../../../third_party/boringssl/src/ssl/tls_record.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c ----------------------------------------------------------------------------------------------------- -Copyright 2006-2017 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509/vpm_int.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509/vpm_int.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509/x509_time_test.cc + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_time_test.cc ----------------------------------------------------------------------------------------------------- -Copyright 2017 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509/x509_vpm.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509/x509_vpm.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_cache.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_data.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_int.h -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_lib.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_map.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_node.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_tree.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2004 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509/x_algor.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509/x_algor.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2000 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/ext_dat.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509v3/ext_dat.h -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_cpols.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_int.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_purp.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3name_test.cc -FILE: ../../../third_party/boringssl/src/include/openssl/x509v3.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_alt.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_alt.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_utl.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_conf.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_conf.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999-2002 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_crld.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_crld.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_genn.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999-2008 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_ncons.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ncons.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pcons.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pmaps.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2003 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - licensing@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_ocsp.c + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ocsp.c ----------------------------------------------------------------------------------------------------- -Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_pci.c -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pci.c -FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pcia.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2004 Kungliga Tekniska Högskolan -(Royal Institute of Technology, Stockholm, Sweden). -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/include/openssl/arm_arch.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/include/openssl/arm_arch.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/ssl/bio_ssl.cc + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/ssl/bio_ssl.cc ----------------------------------------------------------------------------------------------------- -Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. - -Licensed under the OpenSSL license (the "License"). You may not use -this file except in compliance with the License. You can obtain a copy -in the file LICENSE in the source distribution or at -https://www.openssl.org/source/license.html -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/ssl/d1_lib.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/ssl/d1_lib.cc -FILE: ../../../third_party/boringssl/src/ssl/dtls_method.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@OpenSSL.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED 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 OpenSSL PROJECT OR -ITS 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/ssl/ssl_asn1.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/src/ssl/ssl_asn1.cc -FILE: ../../../third_party/boringssl/src/ssl/ssl_stat.cc ----------------------------------------------------------------------------------------------------- -Copyright 2005 Nokia. All rights reserved. - -The portions of the attached software ("Contribution") is developed by -Nokia Corporation and is licensed pursuant to the OpenSSL open source -license. - -The Contribution, originally written by Mika Kousa and Pasi Eronen of -Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites -support (see RFC 4279) to OpenSSL. - -No patent licenses or other rights except those expressly stated in -the OpenSSL open source license shall be deemed granted or received -expressly, by implication, estoppel, or otherwise. - -No assurances are provided by Nokia that the Contribution does not -infringe the patent or other intellectual property rights of any third -party or that the license provides you with all the necessary rights -to make use of the Contribution. - -THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN -ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA -SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY -OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR -OTHERWISE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/fiat/LICENSE -TYPE: LicenseType.mit -FILE: ../../../third_party/boringssl/src/third_party/fiat/METADATA -FILE: ../../../third_party/boringssl/src/third_party/fiat/curve25519_32.h -FILE: ../../../third_party/boringssl/src/third_party/fiat/curve25519_64.h -FILE: ../../../third_party/boringssl/src/third_party/fiat/p256_32.h -FILE: ../../../third_party/boringssl/src/third_party/fiat/p256_64.h ----------------------------------------------------------------------------------------------------- -The MIT License (MIT) - -Copyright (c) 2015-2016 the fiat-crypto authors (see -https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS). - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/fiat/curve25519.c -TYPE: LicenseType.mit -FILE: ../../../third_party/boringssl/src/third_party/fiat/curve25519.c -FILE: ../../../third_party/boringssl/src/third_party/fiat/internal.h -FILE: ../../../third_party/boringssl/src/third_party/fiat/p256.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2015-2016 the fiat-crypto authors (see the AUTHORS file). - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/fiat/curve25519_tables.h -TYPE: LicenseType.mit -FILE: ../../../third_party/boringssl/src/third_party/fiat/curve25519_tables.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2015-2016 the fiat-crypto authors (see the AUTHORS file). - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/CONTRIBUTORS -FILE: ../../../third_party/boringssl/src/third_party/googletest/METADATA -FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/Config.cmake.in -FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/gtest.pc.in -FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/gtest_main.pc.in -FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/libgtest.la.in -FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest.cbproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest.groupproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_main.cbproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_unittest.cbproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-param-test.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-test-part.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-filepath.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest-md.sln -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest-md.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest-md.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest.sln -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main-md.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main-md.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test-md.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test-md.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest-md.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest-md.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest.vcxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest.vcxproj.filters -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-all.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-filepath.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-port.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-test-part.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/DebugProject.xcconfig -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/FrameworkTarget.xcconfig -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/General.xcconfig -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/ReleaseProject.xcconfig -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/StaticLibraryTarget.xcconfig -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/TestTarget.xcconfig -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Resources/Info.plist -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/Info.plist -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/widget.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/widget.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/widget_test.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/gtest.xcodeproj/project.pbxproj ----------------------------------------------------------------------------------------------------- -Copyright 2008, 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_all.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_all.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_link.cc ----------------------------------------------------------------------------------------------------- -Copyright 2009, 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-matchers.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-matchers.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-printers.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-spi.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-matchers.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-printers.cc ----------------------------------------------------------------------------------------------------- -Copyright 2007, 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest_pred_impl.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest_pred_impl.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest_prod.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest_main.cc ----------------------------------------------------------------------------------------------------- -Copyright 2006, 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest-port.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest-port.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest-printers.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-port-arch.h ----------------------------------------------------------------------------------------------------- -Copyright 2015, 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-death-test-internal.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-death-test.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-message.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-death-test-internal.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-internal.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-port.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-string.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample1.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample1.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample1_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample2.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample2.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample2_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample3-inl.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample3_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample4.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample4.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample4_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample5_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-death-test.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-internal-inl.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest.cc ----------------------------------------------------------------------------------------------------- -Copyright 2005, 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-param-util.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-typed-test.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-param-util.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-type-util.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-type-util.h.pump -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/prime_tables.h -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample6_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample7_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample8_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-typed-test.cc ----------------------------------------------------------------------------------------------------- -Copyright 2008 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/samples/sample10_unittest.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample10_unittest.cc -FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample9_unittest.cc ----------------------------------------------------------------------------------------------------- -Copyright 2009 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: boringssl -ORIGIN: ../../../third_party/boringssl/src/third_party/sike/LICENSE -TYPE: LicenseType.mit -FILE: ../../../third_party/boringssl/src/third_party/sike/asm/fp_generic.c -FILE: ../../../third_party/boringssl/src/third_party/sike/curve_params.c -FILE: ../../../third_party/boringssl/src/third_party/sike/fpx.c -FILE: ../../../third_party/boringssl/src/third_party/sike/fpx.h -FILE: ../../../third_party/boringssl/src/third_party/sike/isogeny.c -FILE: ../../../third_party/boringssl/src/third_party/sike/isogeny.h -FILE: ../../../third_party/boringssl/src/third_party/sike/sike.c -FILE: ../../../third_party/boringssl/src/third_party/sike/sike.h -FILE: ../../../third_party/boringssl/src/third_party/sike/utils.h ----------------------------------------------------------------------------------------------------- -MIT License - -Copyright (c) Microsoft Corporation. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE -==================================================================================================== - -==================================================================================================== -LIBRARY: boringssl -ORIGIN: null -TYPE: LicenseType.unknown -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/chacha/chacha-armv8.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/aesv8-armx64.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/armv8-mont.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/ghash-neon-armv8.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha1-armv8.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/vpaes-armv8.S -FILE: ../../../third_party/boringssl/ios-aarch64/crypto/third_party/sike/asm/fp-armv8.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/chacha/chacha-armv4.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/aesv8-armx32.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/armv4-mont.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/ghash-armv4.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/ghashv8-armx32.S -FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha1-armv4-large.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S -FILE: ../../../third_party/boringssl/linux-aarch64/crypto/third_party/sike/asm/fp-armv8.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S -FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S -FILE: ../../../third_party/boringssl/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S -FILE: ../../../third_party/boringssl/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/aes-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S -FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/aes-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S -FILE: ../../../third_party/boringssl/linux-x86_64/crypto/third_party/sike/asm/fp-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/chacha/chacha-x86.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/aes-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/aesni-x86.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/bn-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/co-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/ghash-ssse3-x86.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/ghash-x86.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/md5-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/sha1-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/sha256-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/sha512-586.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/vpaes-x86.S -FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/x86-mont.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/chacha/chacha-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/aes-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/aesni-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/ghash-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/md5-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/rsaz-avx2.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/sha1-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/sha256-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/sha512-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/x86_64-mont.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/x86_64-mont5.S -FILE: ../../../third_party/boringssl/mac-x86_64/crypto/third_party/sike/asm/fp-x86_64.S -FILE: ../../../third_party/boringssl/win-x86/crypto/chacha/chacha-x86.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/aes-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/aesni-x86.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/bn-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/co-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/ghash-ssse3-x86.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/ghash-x86.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/md5-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/sha1-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/sha256-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/sha512-586.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/vpaes-x86.asm -FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/x86-mont.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/chacha/chacha-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/aes-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/md5-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm -FILE: ../../../third_party/boringssl/win-x86_64/crypto/third_party/sike/asm/fp-x86_64.asm ----------------------------------------------------------------------------------------------------- - -==================================================================================================== - -==================================================================================================== -LIBRARY: colorama -ORIGIN: ../../../third_party/colorama/src/LICENSE.txt -TYPE: LicenseType.bsd -FILE: ../../../third_party/colorama/src/MANIFEST.in -FILE: ../../../third_party/colorama/src/screenshots/ubuntu-demo.png -FILE: ../../../third_party/colorama/src/screenshots/windows-demo.png ----------------------------------------------------------------------------------------------------- -Copyright (c) 2010 Jonathan Hartley -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 the copyright holders, nor those 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 HOLDER 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: dart -LIBRARY: wasmer -ORIGIN: ../../../third_party/dart/benchmarks/Dynamic/dart/Dynamic.dart + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/benchmarks/Dynamic/dart/Dynamic.dart -FILE: ../../../third_party/dart/benchmarks/Dynamic/dart2/Dynamic.dart -FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart/EventLoopLatencyJson.dart -FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart/json_benchmark.dart -FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart/latency.dart -FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart2/EventLoopLatencyJson.dart -FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart2/json_benchmark.dart -FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart2/latency.dart -FILE: ../../../third_party/dart/benchmarks/ListCopy/dart/ListCopy.dart -FILE: ../../../third_party/dart/benchmarks/ListCopy/dart2/ListCopy.dart -FILE: ../../../third_party/dart/benchmarks/MD5/dart/md5.dart -FILE: ../../../third_party/dart/benchmarks/MD5/dart2/md5.dart -FILE: ../../../third_party/dart/benchmarks/RuntimeType/dart/RuntimeType.dart -FILE: ../../../third_party/dart/benchmarks/RuntimeType/dart2/RuntimeType.dart -FILE: ../../../third_party/dart/benchmarks/SHA1/dart/sha1.dart -FILE: ../../../third_party/dart/benchmarks/SHA1/dart2/sha1.dart -FILE: ../../../third_party/dart/benchmarks/SHA256/dart/sha256.dart -FILE: ../../../third_party/dart/benchmarks/SHA256/dart2/sha256.dart -FILE: ../../../third_party/dart/benchmarks/SkeletalAnimation/dart/SkeletalAnimation.dart -FILE: ../../../third_party/dart/benchmarks/SkeletalAnimation/dart2/SkeletalAnimation.dart -FILE: ../../../third_party/dart/benchmarks/SkeletalAnimationSIMD/dart/SkeletalAnimationSIMD.dart -FILE: ../../../third_party/dart/benchmarks/SkeletalAnimationSIMD/dart2/SkeletalAnimationSIMD.dart -FILE: ../../../third_party/dart/benchmarks/TypedDataDuplicate/dart/TypedDataDuplicate.dart -FILE: ../../../third_party/dart/benchmarks/TypedDataDuplicate/dart2/TypedDataDuplicate.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/Utf8Decode.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/Utf8Decode.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/Utf8Encode.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/Utf8Encode.dart -FILE: ../../../third_party/dart/runtime/bin/dartdev_isolate.cc -FILE: ../../../third_party/dart/runtime/bin/dartdev_isolate.h -FILE: ../../../third_party/dart/runtime/bin/exe_utils.cc -FILE: ../../../third_party/dart/runtime/bin/exe_utils.h -FILE: ../../../third_party/dart/runtime/bin/platform_macos.h -FILE: ../../../third_party/dart/runtime/bin/platform_macos_test.cc -FILE: ../../../third_party/dart/runtime/include/dart_api_dl.c -FILE: ../../../third_party/dart/runtime/include/dart_api_dl.h -FILE: ../../../third_party/dart/runtime/include/dart_version.h -FILE: ../../../third_party/dart/runtime/include/internal/dart_api_dl_impl.h -FILE: ../../../third_party/dart/runtime/observatory/bin/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/process_snapshot.dart -FILE: ../../../third_party/dart/runtime/platform/leak_sanitizer.h -FILE: ../../../third_party/dart/runtime/platform/unaligned.h -FILE: ../../../third_party/dart/runtime/platform/undefined_behavior_sanitizer.h -FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/bin/main.dart -FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/lib/cquery_driver.dart -FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/lib/xref_extractor.dart -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/dispatch_table_generator.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/dispatch_table_generator.h -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler_tracer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler_tracer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/api/deopt_id.h -FILE: ../../../third_party/dart/runtime/vm/compiler/api/print_filter.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/api/print_filter.h -FILE: ../../../third_party/dart/runtime/vm/compiler/api/type_check_mode.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_base.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/constant_propagator_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/reachability_fence_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/abi.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/abi.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/call.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/call.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/callback.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/callback.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/frame_rebase.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/frame_rebase.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/marshaller.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/marshaller.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_calling_convention.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_calling_convention.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_location.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_location.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type.h -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/recognized_method.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/recognized_method.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_binary_flowgraph_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination.h -FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination_test.cc -FILE: ../../../third_party/dart/runtime/vm/constants_base.h -FILE: ../../../third_party/dart/runtime/vm/dispatch_table.cc -FILE: ../../../third_party/dart/runtime/vm/dispatch_table.h -FILE: ../../../third_party/dart/runtime/vm/field_table.cc -FILE: ../../../third_party/dart/runtime/vm/field_table.h -FILE: ../../../third_party/dart/runtime/vm/port_set.h -FILE: ../../../third_party/dart/runtime/vm/tagged_pointer.h -FILE: ../../../third_party/dart/runtime/vm/timeline_macos.cc -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test.cc -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test.h -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test_arm.cc -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test_x64.cc -FILE: ../../../third_party/dart/runtime/vm/visitor.cc -FILE: ../../../third_party/dart/samples/ffi/async/async_test.dart -FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart -FILE: ../../../third_party/dart/samples/ffi/async/sample_native_port_call.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions_callbacks_closures.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart -FILE: ../../../third_party/dart/sdk/lib/internal/lowering.dart -FILE: ../../../third_party/dart/sdk/lib/io/network_policy.dart -FILE: ../../../third_party/dart/third_party/wasmer/wasmer_wrapper.cc ----------------------------------------------------------------------------------------------------- -Copyright (c) 2020, 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: dart -ORIGIN: ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/.gitconfig -FILE: ../../../third_party/dart/.mailmap -FILE: ../../../third_party/dart/.style.yapf -FILE: ../../../third_party/dart/.vpython -FILE: ../../../third_party/dart/PATENT_GRANT -FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/sample.json -FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart2/sample.json -FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/helloworld.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart2/helloworld.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/helloworld.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart2/helloworld.dart -FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/iterable.dart -FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart2/iterable.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/datext_latin1_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/entext_ascii_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/netext_3_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/rutext_2_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/sktext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/zhtext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/datext_latin1_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/entext_ascii_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/netext_3_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/rutext_2_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/sktext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/zhtext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/datext_latin1_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/entext_ascii_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/netext_3_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/rutext_2_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/sktext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/zhtext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/datext_latin1_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/entext_ascii_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/netext_3_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/rutext_2_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/sktext_10k.dart -FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/zhtext_10k.dart -FILE: ../../../third_party/dart/client/idea/.idea/.name -FILE: ../../../third_party/dart/client/idea/.idea/inspectionProfiles/Project_Default.xml -FILE: ../../../third_party/dart/client/idea/.idea/vcs.xml -FILE: ../../../third_party/dart/runtime/.clang-tidy -FILE: ../../../third_party/dart/runtime/CPPLINT.cfg -FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_arm.S -FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_arm64.S -FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_x64.S -FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_x64_win.S -FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_x86.S -FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions_helpers.S -FILE: ../../../third_party/dart/runtime/docs/compiler/images/catch-block-entry-0.png -FILE: ../../../third_party/dart/runtime/docs/compiler/images/catch-block-entry-1.png -FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-dictionary.png -FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-linear.png -FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-monomorphic.png -FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-singletarget.png -FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-unlinked.png -FILE: ../../../third_party/dart/runtime/docs/images/aot.png -FILE: ../../../third_party/dart/runtime/docs/images/dart-to-kernel.png -FILE: ../../../third_party/dart/runtime/docs/images/flutter-cfe.png -FILE: ../../../third_party/dart/runtime/docs/images/images.graffle!images -FILE: ../../../third_party/dart/runtime/docs/images/inline-cache-1.png -FILE: ../../../third_party/dart/runtime/docs/images/isolates.png -FILE: ../../../third_party/dart/runtime/docs/images/kernel-loaded-1.png -FILE: ../../../third_party/dart/runtime/docs/images/kernel-loaded-2.png -FILE: ../../../third_party/dart/runtime/docs/images/kernel-service.png -FILE: ../../../third_party/dart/runtime/docs/images/optimizing-compilation.png -FILE: ../../../third_party/dart/runtime/docs/images/raw-function-lazy-compile.png -FILE: ../../../third_party/dart/runtime/docs/images/snapshot-appjit.png -FILE: ../../../third_party/dart/runtime/docs/images/snapshot-with-code.png -FILE: ../../../third_party/dart/runtime/docs/images/snapshot.png -FILE: ../../../third_party/dart/runtime/docs/images/unoptimized-compilation.png -FILE: ../../../third_party/dart/runtime/docs/infra/images/isolated-out-browser.png -FILE: ../../../third_party/dart/runtime/docs/infra/images/isolated-out-link.png -FILE: ../../../third_party/dart/runtime/observatory/lib/elements.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/img/chromium_icon.png -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/img/dart_icon.png -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/img/isolate_icon.png -FILE: ../../../third_party/dart/runtime/observatory/web/favicon.ico -FILE: ../../../third_party/dart/runtime/observatory/web/index.html -FILE: ../../../third_party/dart/runtime/observatory/web/third_party/trace_viewer_full.html -FILE: ../../../third_party/dart/runtime/observatory/web/timeline.html -FILE: ../../../third_party/dart/runtime/tools/wiki/styles/style.scss -FILE: ../../../third_party/dart/runtime/tools/wiki/templates/includes/auto-refresh.html -FILE: ../../../third_party/dart/runtime/tools/wiki/templates/includes/favicon.html -FILE: ../../../third_party/dart/runtime/tools/wiki/templates/page.html -FILE: ../../../third_party/dart/samples/ffi/sqlite/docs/lib/scenario-default.svg -FILE: ../../../third_party/dart/samples/ffi/sqlite/docs/lib/scenario-full.svg -FILE: ../../../third_party/dart/sdk/lib/_internal/allowed_experiments.json -FILE: ../../../third_party/dart/sdk/lib/html/html_common/conversions_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/html_common.dart -FILE: ../../../third_party/dart/sdk/lib/libraries.json -FILE: ../../../third_party/dart/sdk/lib/vmservice_libraries.json -FILE: ../../../third_party/dart/third_party/7zip.tar.gz.sha1 -FILE: ../../../third_party/dart/third_party/clang.tar.gz.sha1 ----------------------------------------------------------------------------------------------------- -Copyright 2012, the Dart project authors. - -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: dart -ORIGIN: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/native_version.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/native_version_dummy.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/native_version_javascript.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/native_version.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/native_version_dummy.dart -FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/native_version_javascript.dart -FILE: ../../../third_party/dart/benchmarks/Calls/dart/Calls.dart -FILE: ../../../third_party/dart/benchmarks/Calls/dart2/Calls.dart -FILE: ../../../third_party/dart/benchmarks/Example/dart/Example.dart -FILE: ../../../third_party/dart/benchmarks/Example/dart2/Example.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/digest.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/dlopen_helper.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/types.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/digest.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/dlopen_helper.dart -FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/types.dart -FILE: ../../../third_party/dart/benchmarks/FfiCall/dart/FfiCall.dart -FILE: ../../../third_party/dart/benchmarks/FfiCall/dart/dlopen_helper.dart -FILE: ../../../third_party/dart/benchmarks/FfiCall/dart2/FfiCall.dart -FILE: ../../../third_party/dart/benchmarks/FfiCall/dart2/dlopen_helper.dart -FILE: ../../../third_party/dart/benchmarks/FfiCall/native/native_functions.c -FILE: ../../../third_party/dart/benchmarks/FfiMemory/dart/FfiMemory.dart -FILE: ../../../third_party/dart/benchmarks/FfiMemory/dart2/FfiMemory.dart -FILE: ../../../third_party/dart/benchmarks/FfiStruct/dart/FfiStruct.dart -FILE: ../../../third_party/dart/benchmarks/FfiStruct/dart2/FfiStruct.dart -FILE: ../../../third_party/dart/benchmarks/Isolate/dart/Isolate.dart -FILE: ../../../third_party/dart/benchmarks/Isolate/dart2/Isolate.dart -FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/IsolateJson.dart -FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart2/IsolateJson.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart -FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart -FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/SoundSplayTreeSieve.dart -FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/sound_splay_tree.dart -FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart2/SoundSplayTreeSieve.dart -FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart2/sound_splay_tree.dart -FILE: ../../../third_party/dart/runtime/bin/elf_loader.cc -FILE: ../../../third_party/dart/runtime/bin/elf_loader.h -FILE: ../../../third_party/dart/runtime/bin/entrypoints_verification_test_extension.cc -FILE: ../../../third_party/dart/runtime/bin/entrypoints_verification_test_extension_dllmain_win.cc -FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_dynamic_library.cc -FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions.cc -FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc -FILE: ../../../third_party/dart/runtime/bin/ifaddrs-android.cc -FILE: ../../../third_party/dart/runtime/bin/ifaddrs-android.h -FILE: ../../../third_party/dart/runtime/bin/namespace_fuchsia.h -FILE: ../../../third_party/dart/runtime/bin/socket_base_android.cc -FILE: ../../../third_party/dart/runtime/lib/ffi.cc -FILE: ../../../third_party/dart/runtime/lib/ffi_dynamic_library.cc -FILE: ../../../third_party/dart/runtime/lib/wasm.cc -FILE: ../../../third_party/dart/runtime/llvm_codegen/bit/bit.h -FILE: ../../../third_party/dart/runtime/llvm_codegen/bit/main.cc -FILE: ../../../third_party/dart/runtime/llvm_codegen/bit/test.cc -FILE: ../../../third_party/dart/runtime/llvm_codegen/codegen/custom_zone.cc -FILE: ../../../third_party/dart/runtime/llvm_codegen/codegen/custom_zone.h -FILE: ../../../third_party/dart/runtime/llvm_codegen/codegen/dart.cc -FILE: ../../../third_party/dart/runtime/llvm_codegen/codegen/dart.h -FILE: ../../../third_party/dart/runtime/llvm_codegen/codegen/main.cc -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/tree_map.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/isolate_group.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/isolate_group.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/isolate_group.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline_base.dart -FILE: ../../../third_party/dart/runtime/platform/elf.h -FILE: ../../../third_party/dart/runtime/platform/thread_sanitizer.h -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_api_table.dart -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_ffi_api.dart -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_type_table.dart -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/gen_api_table.dart -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/gen_type_table.dart -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/gen_util.dart -FILE: ../../../third_party/dart/runtime/tools/ffi/sdk_lib_ffi_generator.dart -FILE: ../../../third_party/dart/runtime/tools/graphexplorer/graphexplorer.html -FILE: ../../../third_party/dart/runtime/tools/graphexplorer/graphexplorer.js -FILE: ../../../third_party/dart/runtime/tools/run_clang_tidy.dart -FILE: ../../../third_party/dart/runtime/vm/bss_relocs.cc -FILE: ../../../third_party/dart/runtime/vm/bss_relocs.h -FILE: ../../../third_party/dart/runtime/vm/catch_entry_moves_test.cc -FILE: ../../../third_party/dart/runtime/vm/class_id.h -FILE: ../../../third_party/dart/runtime/vm/code_comments.cc -FILE: ../../../third_party/dart/runtime/vm/code_comments.h -FILE: ../../../third_party/dart/runtime/vm/code_entry_kind.h -FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier.h -FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/bce_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/block_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/evaluator.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/evaluator.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_checker.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_checker.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_deserializer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_deserializer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_serializer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_serializer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_test_helper.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_test_helper.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/inliner_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/sexpression.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/sexpression.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/sexpression_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/typed_data_aot_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/yield_position_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_fingerprints.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_fingerprints.h -FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier.h -FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/offsets_extractor.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/recognized_methods_list.h -FILE: ../../../third_party/dart/runtime/vm/compiler/relocation.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_api.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_api.h -FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_offsets_extracted.h -FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_offsets_list.h -FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler.h -FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/type_testing_stubs_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/type_testing_stubs_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/type_testing_stubs_x64.cc -FILE: ../../../third_party/dart/runtime/vm/constants_arm.cc -FILE: ../../../third_party/dart/runtime/vm/constants_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/constants_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/constants_kbc.cc -FILE: ../../../third_party/dart/runtime/vm/constants_x64.cc -FILE: ../../../third_party/dart/runtime/vm/debugger_kbc.cc -FILE: ../../../third_party/dart/runtime/vm/elf.cc -FILE: ../../../third_party/dart/runtime/vm/elf.h -FILE: ../../../third_party/dart/runtime/vm/ffi_callback_trampolines.cc -FILE: ../../../third_party/dart/runtime/vm/ffi_callback_trampolines.h -FILE: ../../../third_party/dart/runtime/vm/frame_layout.h -FILE: ../../../third_party/dart/runtime/vm/intrusive_dlist.h -FILE: ../../../third_party/dart/runtime/vm/intrusive_dlist_test.cc -FILE: ../../../third_party/dart/runtime/vm/libfuzzer/dart_libfuzzer.cc -FILE: ../../../third_party/dart/runtime/vm/longjump.h -FILE: ../../../third_party/dart/runtime/vm/pointer_tagging.h -FILE: ../../../third_party/dart/runtime/vm/splay-tree.h -FILE: ../../../third_party/dart/runtime/vm/static_type_exactness_state.h -FILE: ../../../third_party/dart/runtime/vm/stub_code_list.h -FILE: ../../../third_party/dart/runtime/vm/thread_stack_resource.cc -FILE: ../../../third_party/dart/runtime/vm/thread_stack_resource.h -FILE: ../../../third_party/dart/runtime/vm/thread_state.cc -FILE: ../../../third_party/dart/runtime/vm/thread_state.h -FILE: ../../../third_party/dart/samples/ffi/coordinate.dart -FILE: ../../../third_party/dart/samples/ffi/dylib_utils.dart -FILE: ../../../third_party/dart/samples/ffi/resource_management/pool.dart -FILE: ../../../third_party/dart/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart -FILE: ../../../third_party/dart/samples/ffi/resource_management/pool_sample.dart -FILE: ../../../third_party/dart/samples/ffi/resource_management/pool_zoned_sample.dart -FILE: ../../../third_party/dart/samples/ffi/resource_management/resource_management_test.dart -FILE: ../../../third_party/dart/samples/ffi/resource_management/unmanaged_sample.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_bitfield.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_data.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_dynamic_library.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions_callbacks.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions_structs.dart -FILE: ../../../third_party/dart/samples/ffi/sample_ffi_structs.dart -FILE: ../../../third_party/dart/samples/ffi/samples_test.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/example/main.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/sqlite.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/bindings.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/constants.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/signatures.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/types.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/collections/closable_iterator.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/database.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/ffi/arena.dart -FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/ffi/dylib_utils.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/rti.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/wasm_patch.dart -FILE: ../../../third_party/dart/sdk/lib/ffi/annotations.dart -FILE: ../../../third_party/dart/sdk/lib/ffi/dynamic_library.dart -FILE: ../../../third_party/dart/sdk/lib/ffi/ffi.dart -FILE: ../../../third_party/dart/sdk/lib/ffi/native_type.dart -FILE: ../../../third_party/dart/sdk/lib/ffi/struct.dart -FILE: ../../../third_party/dart/sdk/lib/internal/errors.dart -FILE: ../../../third_party/dart/sdk/lib/wasm/wasm.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2019, 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: dart -ORIGIN: ../../../third_party/dart/client/dart.js + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/DEPS -FILE: ../../../third_party/dart/client/dart.js -FILE: ../../../third_party/dart/runtime/bin/builtin.cc -FILE: ../../../third_party/dart/runtime/bin/builtin.h -FILE: ../../../third_party/dart/runtime/bin/builtin_gen_snapshot.cc -FILE: ../../../third_party/dart/runtime/bin/builtin_in.cc -FILE: ../../../third_party/dart/runtime/bin/builtin_natives.cc -FILE: ../../../third_party/dart/runtime/bin/crashpad.cc -FILE: ../../../third_party/dart/runtime/bin/crypto.cc -FILE: ../../../third_party/dart/runtime/bin/crypto.h -FILE: ../../../third_party/dart/runtime/bin/crypto_android.cc -FILE: ../../../third_party/dart/runtime/bin/crypto_linux.cc -FILE: ../../../third_party/dart/runtime/bin/crypto_macos.cc -FILE: ../../../third_party/dart/runtime/bin/crypto_win.cc -FILE: ../../../third_party/dart/runtime/bin/dartutils.cc -FILE: ../../../third_party/dart/runtime/bin/dartutils.h -FILE: ../../../third_party/dart/runtime/bin/directory.cc -FILE: ../../../third_party/dart/runtime/bin/directory.h -FILE: ../../../third_party/dart/runtime/bin/directory_android.cc -FILE: ../../../third_party/dart/runtime/bin/directory_linux.cc -FILE: ../../../third_party/dart/runtime/bin/directory_macos.cc -FILE: ../../../third_party/dart/runtime/bin/directory_win.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler.h -FILE: ../../../third_party/dart/runtime/bin/eventhandler_android.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler_android.h -FILE: ../../../third_party/dart/runtime/bin/eventhandler_linux.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler_linux.h -FILE: ../../../third_party/dart/runtime/bin/eventhandler_macos.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler_macos.h -FILE: ../../../third_party/dart/runtime/bin/eventhandler_test.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler_win.h -FILE: ../../../third_party/dart/runtime/bin/extensions.cc -FILE: ../../../third_party/dart/runtime/bin/extensions.h -FILE: ../../../third_party/dart/runtime/bin/extensions_android.cc -FILE: ../../../third_party/dart/runtime/bin/extensions_linux.cc -FILE: ../../../third_party/dart/runtime/bin/extensions_macos.cc -FILE: ../../../third_party/dart/runtime/bin/extensions_win.cc -FILE: ../../../third_party/dart/runtime/bin/fdutils.h -FILE: ../../../third_party/dart/runtime/bin/fdutils_android.cc -FILE: ../../../third_party/dart/runtime/bin/fdutils_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/fdutils_linux.cc -FILE: ../../../third_party/dart/runtime/bin/fdutils_macos.cc -FILE: ../../../third_party/dart/runtime/bin/file_android.cc -FILE: ../../../third_party/dart/runtime/bin/file_linux.cc -FILE: ../../../third_party/dart/runtime/bin/file_macos.cc -FILE: ../../../third_party/dart/runtime/bin/file_test.cc -FILE: ../../../third_party/dart/runtime/bin/file_win.cc -FILE: ../../../third_party/dart/runtime/bin/hashmap_test.cc -FILE: ../../../third_party/dart/runtime/bin/io_buffer.cc -FILE: ../../../third_party/dart/runtime/bin/io_buffer.h -FILE: ../../../third_party/dart/runtime/bin/io_natives.h -FILE: ../../../third_party/dart/runtime/bin/isolate_data.h -FILE: ../../../third_party/dart/runtime/bin/lockers.h -FILE: ../../../third_party/dart/runtime/bin/main.cc -FILE: ../../../third_party/dart/runtime/bin/platform.cc -FILE: ../../../third_party/dart/runtime/bin/platform.h -FILE: ../../../third_party/dart/runtime/bin/platform_android.cc -FILE: ../../../third_party/dart/runtime/bin/platform_linux.cc -FILE: ../../../third_party/dart/runtime/bin/platform_macos.cc -FILE: ../../../third_party/dart/runtime/bin/platform_win.cc -FILE: ../../../third_party/dart/runtime/bin/process.h -FILE: ../../../third_party/dart/runtime/bin/process_android.cc -FILE: ../../../third_party/dart/runtime/bin/process_linux.cc -FILE: ../../../third_party/dart/runtime/bin/process_macos.cc -FILE: ../../../third_party/dart/runtime/bin/process_win.cc -FILE: ../../../third_party/dart/runtime/bin/run_vm_tests.cc -FILE: ../../../third_party/dart/runtime/bin/secure_socket_unsupported.cc -FILE: ../../../third_party/dart/runtime/bin/socket_android.cc -FILE: ../../../third_party/dart/runtime/bin/socket_base_android.h -FILE: ../../../third_party/dart/runtime/bin/socket_base_linux.h -FILE: ../../../third_party/dart/runtime/bin/socket_base_macos.h -FILE: ../../../third_party/dart/runtime/bin/socket_base_win.h -FILE: ../../../third_party/dart/runtime/bin/test_extension.c -FILE: ../../../third_party/dart/runtime/bin/test_extension_dllmain_win.cc -FILE: ../../../third_party/dart/runtime/bin/thread.h -FILE: ../../../third_party/dart/runtime/bin/thread_android.cc -FILE: ../../../third_party/dart/runtime/bin/thread_android.h -FILE: ../../../third_party/dart/runtime/bin/thread_linux.cc -FILE: ../../../third_party/dart/runtime/bin/thread_linux.h -FILE: ../../../third_party/dart/runtime/bin/thread_macos.cc -FILE: ../../../third_party/dart/runtime/bin/thread_macos.h -FILE: ../../../third_party/dart/runtime/bin/thread_win.cc -FILE: ../../../third_party/dart/runtime/bin/thread_win.h -FILE: ../../../third_party/dart/runtime/bin/utils.h -FILE: ../../../third_party/dart/runtime/bin/utils_android.cc -FILE: ../../../third_party/dart/runtime/bin/utils_linux.cc -FILE: ../../../third_party/dart/runtime/bin/utils_macos.cc -FILE: ../../../third_party/dart/runtime/bin/utils_win.cc -FILE: ../../../third_party/dart/runtime/bin/utils_win.h -FILE: ../../../third_party/dart/runtime/include/dart_api.h -FILE: ../../../third_party/dart/runtime/lib/array.cc -FILE: ../../../third_party/dart/runtime/lib/bool.cc -FILE: ../../../third_party/dart/runtime/lib/date.cc -FILE: ../../../third_party/dart/runtime/lib/errors.cc -FILE: ../../../third_party/dart/runtime/lib/function.cc -FILE: ../../../third_party/dart/runtime/lib/growable_array.cc -FILE: ../../../third_party/dart/runtime/lib/identical.cc -FILE: ../../../third_party/dart/runtime/lib/integers.cc -FILE: ../../../third_party/dart/runtime/lib/isolate.cc -FILE: ../../../third_party/dart/runtime/lib/mirrors.cc -FILE: ../../../third_party/dart/runtime/lib/regexp.cc -FILE: ../../../third_party/dart/runtime/lib/stopwatch.cc -FILE: ../../../third_party/dart/runtime/lib/weak_property.cc -FILE: ../../../third_party/dart/runtime/platform/assert.cc -FILE: ../../../third_party/dart/runtime/platform/assert.h -FILE: ../../../third_party/dart/runtime/platform/floating_point.h -FILE: ../../../third_party/dart/runtime/platform/floating_point_win.cc -FILE: ../../../third_party/dart/runtime/platform/floating_point_win.h -FILE: ../../../third_party/dart/runtime/platform/globals.h -FILE: ../../../third_party/dart/runtime/platform/hashmap.cc -FILE: ../../../third_party/dart/runtime/platform/hashmap.h -FILE: ../../../third_party/dart/runtime/platform/syslog.h -FILE: ../../../third_party/dart/runtime/platform/syslog_android.cc -FILE: ../../../third_party/dart/runtime/platform/syslog_linux.cc -FILE: ../../../third_party/dart/runtime/platform/syslog_macos.cc -FILE: ../../../third_party/dart/runtime/platform/syslog_win.cc -FILE: ../../../third_party/dart/runtime/platform/text_buffer.cc -FILE: ../../../third_party/dart/runtime/platform/text_buffer.h -FILE: ../../../third_party/dart/runtime/platform/unicode.cc -FILE: ../../../third_party/dart/runtime/platform/unicode.h -FILE: ../../../third_party/dart/runtime/platform/utils.cc -FILE: ../../../third_party/dart/runtime/platform/utils.h -FILE: ../../../third_party/dart/runtime/platform/utils_android.cc -FILE: ../../../third_party/dart/runtime/platform/utils_android.h -FILE: ../../../third_party/dart/runtime/platform/utils_linux.cc -FILE: ../../../third_party/dart/runtime/platform/utils_linux.h -FILE: ../../../third_party/dart/runtime/platform/utils_macos.cc -FILE: ../../../third_party/dart/runtime/platform/utils_macos.h -FILE: ../../../third_party/dart/runtime/platform/utils_win.cc -FILE: ../../../third_party/dart/runtime/platform/utils_win.h -FILE: ../../../third_party/dart/runtime/vm/allocation.cc -FILE: ../../../third_party/dart/runtime/vm/allocation_test.cc -FILE: ../../../third_party/dart/runtime/vm/assert_test.cc -FILE: ../../../third_party/dart/runtime/vm/base_isolate.h -FILE: ../../../third_party/dart/runtime/vm/benchmark_test.cc -FILE: ../../../third_party/dart/runtime/vm/benchmark_test.h -FILE: ../../../third_party/dart/runtime/vm/bit_set.h -FILE: ../../../third_party/dart/runtime/vm/bit_vector.cc -FILE: ../../../third_party/dart/runtime/vm/bit_vector.h -FILE: ../../../third_party/dart/runtime/vm/bit_vector_test.cc -FILE: ../../../third_party/dart/runtime/vm/bitfield_test.cc -FILE: ../../../third_party/dart/runtime/vm/bitmap.cc -FILE: ../../../third_party/dart/runtime/vm/bitmap.h -FILE: ../../../third_party/dart/runtime/vm/bitmap_test.cc -FILE: ../../../third_party/dart/runtime/vm/boolfield.h -FILE: ../../../third_party/dart/runtime/vm/boolfield_test.cc -FILE: ../../../third_party/dart/runtime/vm/bootstrap.cc -FILE: ../../../third_party/dart/runtime/vm/bootstrap.h -FILE: ../../../third_party/dart/runtime/vm/bootstrap_natives.cc -FILE: ../../../third_party/dart/runtime/vm/bootstrap_natives.h -FILE: ../../../third_party/dart/runtime/vm/class_finalizer.h -FILE: ../../../third_party/dart/runtime/vm/class_finalizer_test.cc -FILE: ../../../third_party/dart/runtime/vm/class_table.cc -FILE: ../../../third_party/dart/runtime/vm/class_table.h -FILE: ../../../third_party/dart/runtime/vm/code_descriptors.cc -FILE: ../../../third_party/dart/runtime/vm/code_descriptors.h -FILE: ../../../third_party/dart/runtime/vm/code_descriptors_test.cc -FILE: ../../../third_party/dart/runtime/vm/code_observers.cc -FILE: ../../../third_party/dart/runtime/vm/code_observers.h -FILE: ../../../third_party/dart/runtime/vm/code_patcher.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher.h -FILE: ../../../third_party/dart/runtime/vm/code_patcher_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/aot_call_specializer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_base.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_x86.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/object_pool_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/constant_propagator.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_printer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_printer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/inliner.h -FILE: ../../../third_party/dart/runtime/vm/compiler/cha.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/cha.h -FILE: ../../../third_party/dart/runtime/vm/compiler/cha_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/flow_graph_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/flow_graph_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/intrinsifier.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/intrinsifier.h -FILE: ../../../third_party/dart/runtime/vm/compiler/jit/compiler.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/jit/compiler.h -FILE: ../../../third_party/dart/runtime/vm/compiler_test.cc -FILE: ../../../third_party/dart/runtime/vm/cpu.h -FILE: ../../../third_party/dart/runtime/vm/cpu_arm.cc -FILE: ../../../third_party/dart/runtime/vm/cpu_test.cc -FILE: ../../../third_party/dart/runtime/vm/cpu_x64.cc -FILE: ../../../third_party/dart/runtime/vm/cpuinfo_linux.cc -FILE: ../../../third_party/dart/runtime/vm/cpuinfo_macos.cc -FILE: ../../../third_party/dart/runtime/vm/dart_api_impl_test.cc -FILE: ../../../third_party/dart/runtime/vm/dart_api_message.cc -FILE: ../../../third_party/dart/runtime/vm/dart_api_message.h -FILE: ../../../third_party/dart/runtime/vm/dart_api_state.h -FILE: ../../../third_party/dart/runtime/vm/datastream.h -FILE: ../../../third_party/dart/runtime/vm/debugger.cc -FILE: ../../../third_party/dart/runtime/vm/debugger.h -FILE: ../../../third_party/dart/runtime/vm/debugger_api_impl_test.cc -FILE: ../../../third_party/dart/runtime/vm/double_internals.h -FILE: ../../../third_party/dart/runtime/vm/exceptions_test.cc -FILE: ../../../third_party/dart/runtime/vm/flag_list.h -FILE: ../../../third_party/dart/runtime/vm/flags.cc -FILE: ../../../third_party/dart/runtime/vm/flags.h -FILE: ../../../third_party/dart/runtime/vm/flags_test.cc -FILE: ../../../third_party/dart/runtime/vm/globals.h -FILE: ../../../third_party/dart/runtime/vm/growable_array.h -FILE: ../../../third_party/dart/runtime/vm/growable_array_test.cc -FILE: ../../../third_party/dart/runtime/vm/handles.cc -FILE: ../../../third_party/dart/runtime/vm/handles_impl.h -FILE: ../../../third_party/dart/runtime/vm/handles_test.cc -FILE: ../../../third_party/dart/runtime/vm/hash_map.h -FILE: ../../../third_party/dart/runtime/vm/hash_map_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/freelist.h -FILE: ../../../third_party/dart/runtime/vm/heap/freelist_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/heap.cc -FILE: ../../../third_party/dart/runtime/vm/heap/heap.h -FILE: ../../../third_party/dart/runtime/vm/heap/heap_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/pages.cc -FILE: ../../../third_party/dart/runtime/vm/heap/pages_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/pointer_block.cc -FILE: ../../../third_party/dart/runtime/vm/heap/pointer_block.h -FILE: ../../../third_party/dart/runtime/vm/heap/scavenger.h -FILE: ../../../third_party/dart/runtime/vm/heap/verifier.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_ia32.h -FILE: ../../../third_party/dart/runtime/vm/instructions_ia32_test.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_x64.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_x64.h -FILE: ../../../third_party/dart/runtime/vm/instructions_x64_test.cc -FILE: ../../../third_party/dart/runtime/vm/isolate_test.cc -FILE: ../../../third_party/dart/runtime/vm/json_test.cc -FILE: ../../../third_party/dart/runtime/vm/lockers.h -FILE: ../../../third_party/dart/runtime/vm/megamorphic_cache_table.cc -FILE: ../../../third_party/dart/runtime/vm/megamorphic_cache_table.h -FILE: ../../../third_party/dart/runtime/vm/memory_region.h -FILE: ../../../third_party/dart/runtime/vm/memory_region_test.cc -FILE: ../../../third_party/dart/runtime/vm/message_handler_test.cc -FILE: ../../../third_party/dart/runtime/vm/message_test.cc -FILE: ../../../third_party/dart/runtime/vm/native_arguments.h -FILE: ../../../third_party/dart/runtime/vm/native_message_handler.cc -FILE: ../../../third_party/dart/runtime/vm/native_message_handler.h -FILE: ../../../third_party/dart/runtime/vm/object.cc -FILE: ../../../third_party/dart/runtime/vm/object.h -FILE: ../../../third_party/dart/runtime/vm/object_arm_test.cc -FILE: ../../../third_party/dart/runtime/vm/object_ia32_test.cc -FILE: ../../../third_party/dart/runtime/vm/object_set.h -FILE: ../../../third_party/dart/runtime/vm/object_store.h -FILE: ../../../third_party/dart/runtime/vm/object_test.cc -FILE: ../../../third_party/dart/runtime/vm/object_x64_test.cc -FILE: ../../../third_party/dart/runtime/vm/os_android.cc -FILE: ../../../third_party/dart/runtime/vm/os_linux.cc -FILE: ../../../third_party/dart/runtime/vm/os_macos.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread.h -FILE: ../../../third_party/dart/runtime/vm/os_thread_android.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread_android.h -FILE: ../../../third_party/dart/runtime/vm/os_thread_linux.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread_linux.h -FILE: ../../../third_party/dart/runtime/vm/os_thread_macos.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread_macos.h -FILE: ../../../third_party/dart/runtime/vm/os_thread_win.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread_win.h -FILE: ../../../third_party/dart/runtime/vm/os_win.cc -FILE: ../../../third_party/dart/runtime/vm/parser.cc -FILE: ../../../third_party/dart/runtime/vm/parser.h -FILE: ../../../third_party/dart/runtime/vm/port.cc -FILE: ../../../third_party/dart/runtime/vm/port_test.cc -FILE: ../../../third_party/dart/runtime/vm/proccpuinfo.cc -FILE: ../../../third_party/dart/runtime/vm/proccpuinfo.h -FILE: ../../../third_party/dart/runtime/vm/raw_object.cc -FILE: ../../../third_party/dart/runtime/vm/raw_object.h -FILE: ../../../third_party/dart/runtime/vm/raw_object_snapshot.cc -FILE: ../../../third_party/dart/runtime/vm/scopes.cc -FILE: ../../../third_party/dart/runtime/vm/scopes.h -FILE: ../../../third_party/dart/runtime/vm/scopes_test.cc -FILE: ../../../third_party/dart/runtime/vm/snapshot.cc -FILE: ../../../third_party/dart/runtime/vm/snapshot.h -FILE: ../../../third_party/dart/runtime/vm/snapshot_ids.h -FILE: ../../../third_party/dart/runtime/vm/snapshot_test.cc -FILE: ../../../third_party/dart/runtime/vm/stack_frame.cc -FILE: ../../../third_party/dart/runtime/vm/stack_frame_test.cc -FILE: ../../../third_party/dart/runtime/vm/stub_code.cc -FILE: ../../../third_party/dart/runtime/vm/symbols.cc -FILE: ../../../third_party/dart/runtime/vm/symbols.h -FILE: ../../../third_party/dart/runtime/vm/thread_pool.cc -FILE: ../../../third_party/dart/runtime/vm/thread_pool.h -FILE: ../../../third_party/dart/runtime/vm/thread_pool_test.cc -FILE: ../../../third_party/dart/runtime/vm/thread_test.cc -FILE: ../../../third_party/dart/runtime/vm/token.h -FILE: ../../../third_party/dart/runtime/vm/unicode.cc -FILE: ../../../third_party/dart/runtime/vm/unit_test.cc -FILE: ../../../third_party/dart/runtime/vm/utils_test.cc -FILE: ../../../third_party/dart/runtime/vm/version.h -FILE: ../../../third_party/dart/runtime/vm/version_in.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory.h -FILE: ../../../third_party/dart/runtime/vm/virtual_memory_posix.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory_test.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory_win.cc -FILE: ../../../third_party/dart/runtime/vm/zone.cc -FILE: ../../../third_party/dart/runtime/vm/zone.h -FILE: ../../../third_party/dart/runtime/vm/zone_test.cc -FILE: ../../../third_party/dart/samples/sample_extension/sample_asynchronous_extension.dart -FILE: ../../../third_party/dart/samples/sample_extension/sample_extension.cc -FILE: ../../../third_party/dart/samples/sample_extension/sample_extension_dllmain_win.cc -FILE: ../../../third_party/dart/samples/sample_extension/sample_synchronous_extension.dart -FILE: ../../../third_party/dart/samples/sample_extension/test_sample_asynchronous_extension.dart -FILE: ../../../third_party/dart/samples/sample_extension/test_sample_synchronous_extension.dart -FILE: ../../../third_party/dart/samples/samples.status -FILE: ../../../third_party/dart/sdk/lib/_http/crypto.dart -FILE: ../../../third_party/dart/sdk/lib/_http/http_date.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/math_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/interceptors.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_array.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_number.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_string.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/native_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/regexp_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/async_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/constant_map.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/core_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/foreign_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/interceptors.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_array.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_number.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_string.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/math_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/native_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/string_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/builtin.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/common_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/directory_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/eventhandler_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/file_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/platform_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/secure_socket_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/array.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/array_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bool_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/date_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/double.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/double_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/empty_source.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/errors_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/expando_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/function_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/growable_array.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/identical_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/immutable_map.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/integers.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/integers_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/invocation_mirror_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/isolate_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/map_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/math_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/mirrors_impl.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/mirrors_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/object_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/print_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/regexp_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/stopwatch_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/string_buffer_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/string_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/timer_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/type_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/weak_property.dart -FILE: ../../../third_party/dart/sdk/lib/async/async.dart -FILE: ../../../third_party/dart/sdk/lib/async/async_error.dart -FILE: ../../../third_party/dart/sdk/lib/async/broadcast_stream_controller.dart -FILE: ../../../third_party/dart/sdk/lib/async/future.dart -FILE: ../../../third_party/dart/sdk/lib/async/future_impl.dart -FILE: ../../../third_party/dart/sdk/lib/async/stream_controller.dart -FILE: ../../../third_party/dart/sdk/lib/async/stream_impl.dart -FILE: ../../../third_party/dart/sdk/lib/async/stream_pipe.dart -FILE: ../../../third_party/dart/sdk/lib/async/timer.dart -FILE: ../../../third_party/dart/sdk/lib/collection/collection.dart -FILE: ../../../third_party/dart/sdk/lib/collection/iterable.dart -FILE: ../../../third_party/dart/sdk/lib/collection/iterator.dart -FILE: ../../../third_party/dart/sdk/lib/collection/maps.dart -FILE: ../../../third_party/dart/sdk/lib/collection/splay_tree.dart -FILE: ../../../third_party/dart/sdk/lib/core/bool.dart -FILE: ../../../third_party/dart/sdk/lib/core/core.dart -FILE: ../../../third_party/dart/sdk/lib/core/double.dart -FILE: ../../../third_party/dart/sdk/lib/core/errors.dart -FILE: ../../../third_party/dart/sdk/lib/core/exceptions.dart -FILE: ../../../third_party/dart/sdk/lib/core/expando.dart -FILE: ../../../third_party/dart/sdk/lib/core/identical.dart -FILE: ../../../third_party/dart/sdk/lib/core/int.dart -FILE: ../../../third_party/dart/sdk/lib/core/invocation.dart -FILE: ../../../third_party/dart/sdk/lib/core/iterator.dart -FILE: ../../../third_party/dart/sdk/lib/core/list.dart -FILE: ../../../third_party/dart/sdk/lib/core/num.dart -FILE: ../../../third_party/dart/sdk/lib/core/object.dart -FILE: ../../../third_party/dart/sdk/lib/core/print.dart -FILE: ../../../third_party/dart/sdk/lib/core/regexp.dart -FILE: ../../../third_party/dart/sdk/lib/core/string.dart -FILE: ../../../third_party/dart/sdk/lib/core/type.dart -FILE: ../../../third_party/dart/sdk/lib/core/uri.dart -FILE: ../../../third_party/dart/sdk/lib/html/dart2js/html_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/html/dartium/nativewrappers.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/conversions.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/css_class_set.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/html_common_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/metadata.dart -FILE: ../../../third_party/dart/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/internal/async_cast.dart -FILE: ../../../third_party/dart/sdk/lib/internal/cast.dart -FILE: ../../../third_party/dart/sdk/lib/internal/internal.dart -FILE: ../../../third_party/dart/sdk/lib/io/common.dart -FILE: ../../../third_party/dart/sdk/lib/io/directory.dart -FILE: ../../../third_party/dart/sdk/lib/io/directory_impl.dart -FILE: ../../../third_party/dart/sdk/lib/io/eventhandler.dart -FILE: ../../../third_party/dart/sdk/lib/io/io.dart -FILE: ../../../third_party/dart/sdk/lib/io/platform.dart -FILE: ../../../third_party/dart/sdk/lib/io/platform_impl.dart -FILE: ../../../third_party/dart/sdk/lib/io/secure_server_socket.dart -FILE: ../../../third_party/dart/sdk/lib/isolate/isolate.dart -FILE: ../../../third_party/dart/sdk/lib/math/math.dart -FILE: ../../../third_party/dart/sdk/lib/math/random.dart -FILE: ../../../third_party/dart/sdk/lib/svg/dart2js/svg_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart -FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/address_sanitizer.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/bin/address_sanitizer.cc -FILE: ../../../third_party/dart/runtime/bin/dart_io_api_impl.cc -FILE: ../../../third_party/dart/runtime/bin/observatory_assets_empty.cc -FILE: ../../../third_party/dart/runtime/include/bin/dart_io_api.h -FILE: ../../../third_party/dart/runtime/lib/developer.cc -FILE: ../../../third_party/dart/runtime/lib/timeline.cc -FILE: ../../../third_party/dart/runtime/lib/vmservice.cc -FILE: ../../../third_party/dart/runtime/observatory/lib/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/cli.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/debugger.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/cli/command.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/debugger/debugger.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/debugger/debugger_location.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/logging.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/logging_list.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/megamorphiccache_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/objectpool_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/ports.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/timeline_page.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/view_footer.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/sample_profile/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/web/timeline.js -FILE: ../../../third_party/dart/runtime/vm/atomic_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.h -FILE: ../../../third_party/dart/runtime/vm/log.cc -FILE: ../../../third_party/dart/runtime/vm/log.h -FILE: ../../../third_party/dart/runtime/vm/log_test.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread.cc -FILE: ../../../third_party/dart/runtime/vm/profiler_service.cc -FILE: ../../../third_party/dart/runtime/vm/profiler_service.h -FILE: ../../../third_party/dart/runtime/vm/program_visitor.cc -FILE: ../../../third_party/dart/runtime/vm/program_visitor.h -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_bytecode.cc -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_bytecode.h -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_bytecode_inl.h -FILE: ../../../third_party/dart/runtime/vm/regexp_bytecodes.h -FILE: ../../../third_party/dart/runtime/vm/regexp_interpreter.cc -FILE: ../../../third_party/dart/runtime/vm/regexp_interpreter.h -FILE: ../../../third_party/dart/runtime/vm/scope_timer.h -FILE: ../../../third_party/dart/runtime/vm/service_event.cc -FILE: ../../../third_party/dart/runtime/vm/service_event.h -FILE: ../../../third_party/dart/runtime/vm/service_isolate.cc -FILE: ../../../third_party/dart/runtime/vm/source_report.cc -FILE: ../../../third_party/dart/runtime/vm/source_report.h -FILE: ../../../third_party/dart/runtime/vm/source_report_test.cc -FILE: ../../../third_party/dart/runtime/vm/thread.cc -FILE: ../../../third_party/dart/runtime/vm/thread.h -FILE: ../../../third_party/dart/runtime/vm/thread_barrier.h -FILE: ../../../third_party/dart/runtime/vm/thread_barrier_test.cc -FILE: ../../../third_party/dart/runtime/vm/thread_registry.cc -FILE: ../../../third_party/dart/runtime/vm/thread_registry.h -FILE: ../../../third_party/dart/runtime/vm/timeline.cc -FILE: ../../../third_party/dart/runtime/vm/timeline.h -FILE: ../../../third_party/dart/runtime/vm/timeline_analysis.cc -FILE: ../../../third_party/dart/runtime/vm/timeline_analysis.h -FILE: ../../../third_party/dart/runtime/vm/timeline_test.cc -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/rtti.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/utils.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/debugger.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/developer_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/preambles/d8.js -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/async_await_error_codes.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/async_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/compact_hash.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/developer.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/timeline.dart -FILE: ../../../third_party/dart/sdk/lib/convert/base64.dart -FILE: ../../../third_party/dart/sdk/lib/developer/developer.dart -FILE: ../../../third_party/dart/sdk/lib/developer/extension.dart -FILE: ../../../third_party/dart/sdk/lib/developer/timeline.dart -FILE: ../../../third_party/dart/sdk/lib/io/io_resource_info.dart -FILE: ../../../third_party/dart/sdk/lib/io/security_context.dart -FILE: ../../../third_party/dart/sdk/lib/js/_js_annotations.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/asset.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/vmservice.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2015, 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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/cli.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/bin/cli.cc -FILE: ../../../third_party/dart/runtime/bin/dfe.cc -FILE: ../../../third_party/dart/runtime/bin/dfe.h -FILE: ../../../third_party/dart/runtime/bin/error_exit.cc -FILE: ../../../third_party/dart/runtime/bin/error_exit.h -FILE: ../../../third_party/dart/runtime/bin/gzip.cc -FILE: ../../../third_party/dart/runtime/bin/gzip.h -FILE: ../../../third_party/dart/runtime/bin/isolate_data.cc -FILE: ../../../third_party/dart/runtime/bin/main_options.cc -FILE: ../../../third_party/dart/runtime/bin/main_options.h -FILE: ../../../third_party/dart/runtime/bin/namespace.cc -FILE: ../../../third_party/dart/runtime/bin/namespace.h -FILE: ../../../third_party/dart/runtime/bin/namespace_android.cc -FILE: ../../../third_party/dart/runtime/bin/namespace_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/namespace_linux.cc -FILE: ../../../third_party/dart/runtime/bin/namespace_macos.cc -FILE: ../../../third_party/dart/runtime/bin/namespace_win.cc -FILE: ../../../third_party/dart/runtime/bin/options.cc -FILE: ../../../third_party/dart/runtime/bin/options.h -FILE: ../../../third_party/dart/runtime/bin/secure_socket_filter.cc -FILE: ../../../third_party/dart/runtime/bin/secure_socket_filter.h -FILE: ../../../third_party/dart/runtime/bin/secure_socket_utils.cc -FILE: ../../../third_party/dart/runtime/bin/secure_socket_utils.h -FILE: ../../../third_party/dart/runtime/bin/security_context.cc -FILE: ../../../third_party/dart/runtime/bin/security_context.h -FILE: ../../../third_party/dart/runtime/bin/security_context_android.cc -FILE: ../../../third_party/dart/runtime/bin/security_context_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/security_context_linux.cc -FILE: ../../../third_party/dart/runtime/bin/security_context_macos.cc -FILE: ../../../third_party/dart/runtime/bin/security_context_win.cc -FILE: ../../../third_party/dart/runtime/bin/snapshot_utils.cc -FILE: ../../../third_party/dart/runtime/bin/snapshot_utils.h -FILE: ../../../third_party/dart/runtime/bin/socket_base.cc -FILE: ../../../third_party/dart/runtime/bin/socket_base.h -FILE: ../../../third_party/dart/runtime/bin/sync_socket.cc -FILE: ../../../third_party/dart/runtime/bin/sync_socket.h -FILE: ../../../third_party/dart/runtime/bin/sync_socket_android.cc -FILE: ../../../third_party/dart/runtime/bin/sync_socket_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/sync_socket_linux.cc -FILE: ../../../third_party/dart/runtime/bin/sync_socket_macos.cc -FILE: ../../../third_party/dart/runtime/bin/sync_socket_win.cc -FILE: ../../../third_party/dart/runtime/lib/async.cc -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/containers/search_bar.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/reload.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/singletargetcache_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/singletargetcache_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/subtypetestcache_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/subtypetestcache_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/timeline/dashboard.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unlinkedcall_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unlinkedcall_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/service.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/single_target_cache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/subtype_test_cache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/timeline.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/unlinked_call.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/single_target_cache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/subtype_test_cache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/timeline.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/unlinked_call.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/vm.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/single_target_cache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/subtype_test_cache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/unlinked_call.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/vm.dart -FILE: ../../../third_party/dart/runtime/platform/allocation.h -FILE: ../../../third_party/dart/runtime/platform/growable_array.h -FILE: ../../../third_party/dart/runtime/vm/compilation_trace.cc -FILE: ../../../third_party/dart/runtime/vm/compilation_trace.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations_helpers.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations_helpers_arm.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations_helpers_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/call_specializer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/call_specializer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/prologue_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/prologue_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/jit/jit_call_specializer.h -FILE: ../../../third_party/dart/runtime/vm/constants_x86.h -FILE: ../../../third_party/dart/runtime/vm/debugger_api_impl_test.h -FILE: ../../../third_party/dart/runtime/vm/dwarf.cc -FILE: ../../../third_party/dart/runtime/vm/dwarf.h -FILE: ../../../third_party/dart/runtime/vm/fixed_cache.h -FILE: ../../../third_party/dart/runtime/vm/fixed_cache_test.cc -FILE: ../../../third_party/dart/runtime/vm/gdb_helpers.cc -FILE: ../../../third_party/dart/runtime/vm/heap/compactor.cc -FILE: ../../../third_party/dart/runtime/vm/heap/compactor.h -FILE: ../../../third_party/dart/runtime/vm/image_snapshot.cc -FILE: ../../../third_party/dart/runtime/vm/image_snapshot.h -FILE: ../../../third_party/dart/runtime/vm/json_writer.cc -FILE: ../../../third_party/dart/runtime/vm/json_writer.h -FILE: ../../../third_party/dart/runtime/vm/kernel_binary.h -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks.h -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_arm.cc -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_tcmalloc.cc -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_test.cc -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_unsupported.cc -FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_x64.cc -FILE: ../../../third_party/dart/runtime/vm/mixin_test.cc -FILE: ../../../third_party/dart/runtime/vm/stack_trace.cc -FILE: ../../../third_party/dart/runtime/vm/stack_trace.h -FILE: ../../../third_party/dart/runtime/vm/timeline_android.cc -FILE: ../../../third_party/dart/runtime/vm/timeline_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/timeline_linux.cc -FILE: ../../../third_party/dart/runtime/vm/zone_text_buffer.cc -FILE: ../../../third_party/dart/runtime/vm/zone_text_buffer.h -FILE: ../../../third_party/dart/sdk/lib/_http/overrides.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/custom_hash_map.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/identity_hash_map.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/profile.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/cli_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/namespace_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/sync_socket_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/class_id_fasta.dart -FILE: ../../../third_party/dart/sdk/lib/cli/cli.dart -FILE: ../../../third_party/dart/sdk/lib/cli/wait_for.dart -FILE: ../../../third_party/dart/sdk/lib/core/bigint.dart -FILE: ../../../third_party/dart/sdk/lib/internal/linked_list.dart -FILE: ../../../third_party/dart/sdk/lib/internal/patch.dart -FILE: ../../../third_party/dart/sdk/lib/io/embedder_config.dart -FILE: ../../../third_party/dart/sdk/lib/io/namespace_impl.dart -FILE: ../../../third_party/dart/sdk/lib/io/overrides.dart -FILE: ../../../third_party/dart/sdk/lib/io/sync_socket.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/named_lookup.dart -FILE: ../../../third_party/dart/utils/bazel/kernel_worker.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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/console.h + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/bin/console.h -FILE: ../../../third_party/dart/runtime/bin/console_posix.cc -FILE: ../../../third_party/dart/runtime/bin/console_win.cc ----------------------------------------------------------------------------------------------------- -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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/crashpad.h + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/bin/crashpad.h -FILE: ../../../third_party/dart/runtime/bin/dart_embedder_api_impl.cc -FILE: ../../../third_party/dart/runtime/bin/typed_data_utils.cc -FILE: ../../../third_party/dart/runtime/bin/typed_data_utils.h -FILE: ../../../third_party/dart/runtime/include/dart_embedder_api.h -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz.dart -FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_test.dart -FILE: ../../../third_party/dart/runtime/vm/base64.cc -FILE: ../../../third_party/dart/runtime/vm/base64.h -FILE: ../../../third_party/dart/runtime/vm/base64_test.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_kbc.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_kbc.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_kbc.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/code_statistics.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/code_statistics.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/compile_type.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_elimination_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot.h -FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_pass.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_pass.h -FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_state.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_state.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/base_flow_graph_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/base_flow_graph_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_reader.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_scope_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_reader.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_reader.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_fingerprints.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_fingerprints.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_translation_helper.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_translation_helper.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/scope_builder.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/scope_builder.h -FILE: ../../../third_party/dart/runtime/vm/compiler/relocation.h -FILE: ../../../third_party/dart/runtime/vm/constants.h -FILE: ../../../third_party/dart/runtime/vm/constants_kbc.h -FILE: ../../../third_party/dart/runtime/vm/datastream.cc -FILE: ../../../third_party/dart/runtime/vm/finalizable_data.h -FILE: ../../../third_party/dart/runtime/vm/hash.h -FILE: ../../../third_party/dart/runtime/vm/instructions_kbc.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_kbc.h -FILE: ../../../third_party/dart/runtime/vm/interpreter.cc -FILE: ../../../third_party/dart/runtime/vm/interpreter.h -FILE: ../../../third_party/dart/runtime/vm/raw_object_fields.cc -FILE: ../../../third_party/dart/runtime/vm/raw_object_fields.h -FILE: ../../../third_party/dart/runtime/vm/reverse_pc_lookup_cache.cc -FILE: ../../../third_party/dart/runtime/vm/reverse_pc_lookup_cache.h -FILE: ../../../third_party/dart/runtime/vm/stack_frame_kbc.h -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs.cc -FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs.h -FILE: ../../../third_party/dart/runtime/vm/v8_snapshot_writer.cc -FILE: ../../../third_party/dart/runtime/vm/v8_snapshot_writer.h -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/instantiation.dart -FILE: ../../../third_party/dart/sdk/lib/js/_js.dart -FILE: ../../../third_party/dart/sdk/lib/js/_js_client.dart -FILE: ../../../third_party/dart/sdk/lib/js/_js_server.dart -FILE: ../../../third_party/dart/sdk/lib/typed_data/unmodifiable_typed_data.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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/crypto_fuchsia.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/bin/crypto_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/crypto_test.cc -FILE: ../../../third_party/dart/runtime/bin/directory_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/directory_test.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/eventhandler_fuchsia.h -FILE: ../../../third_party/dart/runtime/bin/extensions_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/file_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/file_support.cc -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/loader.cc -FILE: ../../../third_party/dart/runtime/bin/loader.h -FILE: ../../../third_party/dart/runtime/bin/platform_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/process_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/reference_counting.h -FILE: ../../../third_party/dart/runtime/bin/root_certificates_unsupported.cc -FILE: ../../../third_party/dart/runtime/bin/socket_base_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/socket_base_fuchsia.h -FILE: ../../../third_party/dart/runtime/bin/socket_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/stdio_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/thread_fuchsia.cc -FILE: ../../../third_party/dart/runtime/bin/thread_fuchsia.h -FILE: ../../../third_party/dart/runtime/bin/utils_fuchsia.cc -FILE: ../../../third_party/dart/runtime/lib/stacktrace.h -FILE: ../../../third_party/dart/runtime/observatory/lib/event.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/models.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/repositories.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/notification.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/containers/virtual_collection.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/containers/virtual_tree.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/error_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/general_error.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/custom_element.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/nav_bar.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/nav_menu.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/rendering_queue.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/rendering_scheduler.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/uris.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/counter_chart.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/location.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/run_state.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/shared_summary.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/summary.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/metric/details.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/metric/graph.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/class_menu.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/isolate_menu.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/library_menu.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/menu_item.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/notify.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/notify_event.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/notify_exception.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/refresh.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/top_menu.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/vm_menu.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/source_link.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/strongly_reachable_instances.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_connect_target.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/exceptions.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/breakpoint.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/class.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/code.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/context.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/error.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/event.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/extension_data.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/field.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/flag.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/frame.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/function.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/guarded.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/heap_space.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/icdata.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/instance.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/isolate.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/library.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/local_var_descriptors.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/map_association.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/megamorphiccache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/metric.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/notification.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/object.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/objectpool.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/objectstore.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/pc_descriptors.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/ports.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/script.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/sentinel.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/source_location.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/target.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/thread.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/timeline_event.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/type_arguments.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/unknown.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/vm.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/zone.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/breakpoint.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/class.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/context.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/editor.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/eval.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/event.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/field.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/flag.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/function.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/icdata.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/instance.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/isolate.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/library.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/megamorphiccache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/metric.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/notification.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/object.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/objectpool.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/objectstore.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/ports.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/reachable_size.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/retained_size.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/script.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/strongly_reachable_instances.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/target.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/type_arguments.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/breakpoint.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/class.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/context.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/editor.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/eval.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/event.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/field.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/flag.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/function.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/icdata.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/inbound_references.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/instance.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/isolate.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/library.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/megamorphiccache.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/metric.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/notification.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/object.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/objectpool.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/objectstore.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/persistent_handles.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/ports.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/reachable_size.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/retained_size.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/retaining_path.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/sample_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/script.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/settings.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/strongly_reachable_instances.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/target.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/type_arguments.dart -FILE: ../../../third_party/dart/runtime/observatory/web/timeline_message_handler.js -FILE: ../../../third_party/dart/runtime/platform/syslog_fuchsia.cc -FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.cc -FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.h -FILE: ../../../third_party/dart/runtime/vm/canonical_tables.h -FILE: ../../../third_party/dart/runtime/vm/clustered_snapshot.cc -FILE: ../../../third_party/dart/runtime/vm/clustered_snapshot.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/branch_optimizer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/branch_optimizer.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_elimination.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_elimination.h -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_to_il.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_to_il.h -FILE: ../../../third_party/dart/runtime/vm/cpuinfo_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/dart_api_state.cc -FILE: ../../../third_party/dart/runtime/vm/heap/become.cc -FILE: ../../../third_party/dart/runtime/vm/heap/become.h -FILE: ../../../third_party/dart/runtime/vm/heap/safepoint.cc -FILE: ../../../third_party/dart/runtime/vm/heap/safepoint.h -FILE: ../../../third_party/dart/runtime/vm/isolate_reload.cc -FILE: ../../../third_party/dart/runtime/vm/isolate_reload.h -FILE: ../../../third_party/dart/runtime/vm/isolate_reload_test.cc -FILE: ../../../third_party/dart/runtime/vm/kernel.cc -FILE: ../../../third_party/dart/runtime/vm/kernel.h -FILE: ../../../third_party/dart/runtime/vm/kernel_binary.cc -FILE: ../../../third_party/dart/runtime/vm/kernel_isolate.cc -FILE: ../../../third_party/dart/runtime/vm/kernel_isolate.h -FILE: ../../../third_party/dart/runtime/vm/kernel_loader.cc -FILE: ../../../third_party/dart/runtime/vm/kernel_loader.h -FILE: ../../../third_party/dart/runtime/vm/lockers.cc -FILE: ../../../third_party/dart/runtime/vm/native_symbol_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/object_reload.cc -FILE: ../../../third_party/dart/runtime/vm/object_service.cc -FILE: ../../../third_party/dart/runtime/vm/os_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/os_thread_fuchsia.h -FILE: ../../../third_party/dart/runtime/vm/signal_handler_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_fuchsia.cc -FILE: ../../../third_party/dart/runtime/vm/token_position.cc -FILE: ../../../third_party/dart/runtime/vm/token_position.h -FILE: ../../../third_party/dart/runtime/vm/uri.cc -FILE: ../../../third_party/dart/runtime/vm/uri.h -FILE: ../../../third_party/dart/runtime/vm/uri_test.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory_fuchsia.cc -FILE: ../../../third_party/dart/sdk/lib/developer/service.dart -FILE: ../../../third_party/dart/sdk/lib/js_util/js_util.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/devfs.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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/eventhandler_win.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/WATCHLISTS -FILE: ../../../third_party/dart/runtime/bin/eventhandler_win.cc -FILE: ../../../third_party/dart/runtime/bin/file.cc -FILE: ../../../third_party/dart/runtime/bin/file.h -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher.cc -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher.h -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_android.cc -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_linux.cc -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_macos.cc -FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_win.cc -FILE: ../../../third_party/dart/runtime/bin/filter.cc -FILE: ../../../third_party/dart/runtime/bin/filter.h -FILE: ../../../third_party/dart/runtime/bin/gen_snapshot.cc -FILE: ../../../third_party/dart/runtime/bin/io_natives.cc -FILE: ../../../third_party/dart/runtime/bin/io_service.cc -FILE: ../../../third_party/dart/runtime/bin/io_service.h -FILE: ../../../third_party/dart/runtime/bin/io_service_no_ssl.cc -FILE: ../../../third_party/dart/runtime/bin/io_service_no_ssl.h -FILE: ../../../third_party/dart/runtime/bin/process.cc -FILE: ../../../third_party/dart/runtime/bin/socket.cc -FILE: ../../../third_party/dart/runtime/bin/socket.h -FILE: ../../../third_party/dart/runtime/bin/socket_base_linux.cc -FILE: ../../../third_party/dart/runtime/bin/socket_base_macos.cc -FILE: ../../../third_party/dart/runtime/bin/socket_base_win.cc -FILE: ../../../third_party/dart/runtime/bin/socket_linux.cc -FILE: ../../../third_party/dart/runtime/bin/socket_macos.cc -FILE: ../../../third_party/dart/runtime/bin/socket_win.cc -FILE: ../../../third_party/dart/runtime/bin/stdio.cc -FILE: ../../../third_party/dart/runtime/bin/stdio.h -FILE: ../../../third_party/dart/runtime/bin/stdio_android.cc -FILE: ../../../third_party/dart/runtime/bin/stdio_linux.cc -FILE: ../../../third_party/dart/runtime/bin/stdio_macos.cc -FILE: ../../../third_party/dart/runtime/bin/stdio_win.cc -FILE: ../../../third_party/dart/runtime/bin/vmservice_impl.cc -FILE: ../../../third_party/dart/runtime/bin/vmservice_impl.h -FILE: ../../../third_party/dart/runtime/include/dart_native_api.h -FILE: ../../../third_party/dart/runtime/lib/invocation_mirror.h -FILE: ../../../third_party/dart/runtime/lib/libgen_in.cc -FILE: ../../../third_party/dart/runtime/lib/simd128.cc -FILE: ../../../third_party/dart/runtime/lib/stacktrace.cc -FILE: ../../../third_party/dart/runtime/lib/typed_data.cc -FILE: ../../../third_party/dart/runtime/lib/uri.cc -FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/application.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/location_manager.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_instances.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/code_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/code_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/context_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/context_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/cpu_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/cpu_profile/virtual_tree.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/cpu_profile_table.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/curly_block.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/error_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/eval_box.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/field_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/field_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/flag_list.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/function_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/function_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/heap_snapshot.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/any_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/icdata_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/icdata_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/instance_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/instance_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/json_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/library_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/library_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/local_var_descriptors_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/megamorphiccache_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/native_memory_profiler.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/object_common.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/object_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/objectpool_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/objectstore_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/observatory_application.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/pc_descriptors_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/sample_buffer_control.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/script_inset.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/script_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/script_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/sentinel_value.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/sentinel_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/source_inset.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/stack_trace_tree_config.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/type_arguments_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unknown_ref.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_view.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/tracer.dart -FILE: ../../../third_party/dart/runtime/platform/atomic.h -FILE: ../../../third_party/dart/runtime/platform/signal_blocker.h -FILE: ../../../third_party/dart/runtime/vm/allocation.h -FILE: ../../../third_party/dart/runtime/vm/class_finalizer.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/aot/aot_call_specializer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm64_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/block_scheduler.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/block_scheduler.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/constant_propagator.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_x64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/inliner.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/linearscan.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/linearscan.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator.h -FILE: ../../../third_party/dart/runtime/vm/compiler/jit/jit_call_specializer.cc -FILE: ../../../third_party/dart/runtime/vm/constants_arm.h -FILE: ../../../third_party/dart/runtime/vm/constants_ia32.h -FILE: ../../../third_party/dart/runtime/vm/constants_x64.h -FILE: ../../../third_party/dart/runtime/vm/dart.cc -FILE: ../../../third_party/dart/runtime/vm/dart_api_impl.cc -FILE: ../../../third_party/dart/runtime/vm/deferred_objects.cc -FILE: ../../../third_party/dart/runtime/vm/deferred_objects.h -FILE: ../../../third_party/dart/runtime/vm/deopt_instructions.cc -FILE: ../../../third_party/dart/runtime/vm/deopt_instructions.h -FILE: ../../../third_party/dart/runtime/vm/guard_field_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/weak_table.cc -FILE: ../../../third_party/dart/runtime/vm/heap/weak_table.h -FILE: ../../../third_party/dart/runtime/vm/instructions.h -FILE: ../../../third_party/dart/runtime/vm/instructions_arm.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_arm.h -FILE: ../../../third_party/dart/runtime/vm/instructions_arm_test.cc -FILE: ../../../third_party/dart/runtime/vm/isolate.cc -FILE: ../../../third_party/dart/runtime/vm/isolate.h -FILE: ../../../third_party/dart/runtime/vm/json_stream.cc -FILE: ../../../third_party/dart/runtime/vm/json_stream.h -FILE: ../../../third_party/dart/runtime/vm/native_api_impl.cc -FILE: ../../../third_party/dart/runtime/vm/native_symbol.h -FILE: ../../../third_party/dart/runtime/vm/native_symbol_android.cc -FILE: ../../../third_party/dart/runtime/vm/native_symbol_linux.cc -FILE: ../../../third_party/dart/runtime/vm/native_symbol_macos.cc -FILE: ../../../third_party/dart/runtime/vm/native_symbol_win.cc -FILE: ../../../third_party/dart/runtime/vm/object_id_ring.cc -FILE: ../../../third_party/dart/runtime/vm/object_id_ring.h -FILE: ../../../third_party/dart/runtime/vm/object_id_ring_test.cc -FILE: ../../../third_party/dart/runtime/vm/object_store.cc -FILE: ../../../third_party/dart/runtime/vm/os_test.cc -FILE: ../../../third_party/dart/runtime/vm/profiler.cc -FILE: ../../../third_party/dart/runtime/vm/profiler.h -FILE: ../../../third_party/dart/runtime/vm/profiler_test.cc -FILE: ../../../third_party/dart/runtime/vm/random.cc -FILE: ../../../third_party/dart/runtime/vm/random.h -FILE: ../../../third_party/dart/runtime/vm/reusable_handles.h -FILE: ../../../third_party/dart/runtime/vm/service.cc -FILE: ../../../third_party/dart/runtime/vm/service.h -FILE: ../../../third_party/dart/runtime/vm/service_isolate.h -FILE: ../../../third_party/dart/runtime/vm/service_test.cc -FILE: ../../../third_party/dart/runtime/vm/signal_handler.h -FILE: ../../../third_party/dart/runtime/vm/signal_handler_android.cc -FILE: ../../../third_party/dart/runtime/vm/signal_handler_linux.cc -FILE: ../../../third_party/dart/runtime/vm/signal_handler_macos.cc -FILE: ../../../third_party/dart/runtime/vm/signal_handler_win.cc -FILE: ../../../third_party/dart/runtime/vm/simulator.h -FILE: ../../../third_party/dart/runtime/vm/simulator_arm.cc -FILE: ../../../third_party/dart/runtime/vm/simulator_arm.h -FILE: ../../../third_party/dart/runtime/vm/stack_frame_arm.h -FILE: ../../../third_party/dart/runtime/vm/stack_frame_ia32.h -FILE: ../../../third_party/dart/runtime/vm/stack_frame_x64.h -FILE: ../../../third_party/dart/runtime/vm/stub_code_arm_test.cc -FILE: ../../../third_party/dart/runtime/vm/tags.h -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter.cc -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter.h -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_android.cc -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_linux.cc -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_macos.cc -FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_win.cc -FILE: ../../../third_party/dart/sdk/lib/_http/http.dart -FILE: ../../../third_party/dart/sdk/lib/_http/http_headers.dart -FILE: ../../../third_party/dart/sdk/lib/_http/http_impl.dart -FILE: ../../../third_party/dart/sdk/lib/_http/http_parser.dart -FILE: ../../../third_party/dart/sdk/lib/_http/http_session.dart -FILE: ../../../third_party/dart/sdk/lib/_http/websocket.dart -FILE: ../../../third_party/dart/sdk/lib/_http/websocket_impl.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/collection_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/convert_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/internal_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/typed_data_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/annotations.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_primitives.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_rti.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/mirror_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/native_typed_data.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/annotations.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/collection_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/convert_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/internal_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/io_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_helper.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_names.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_primitives.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/native_typed_data.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/typed_data_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/file_system_entity_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/filter_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/io_service_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/process_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/socket_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/stdio_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/vmservice_io.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/vmservice_server.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/collection_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/core_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/deferred_load_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/function.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/internal_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/mirror_reference.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/null_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/stacktrace.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/symbol_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/timer_impl.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/typed_data_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/uri_patch.dart -FILE: ../../../third_party/dart/sdk/lib/async/deferred_load.dart -FILE: ../../../third_party/dart/sdk/lib/async/schedule_microtask.dart -FILE: ../../../third_party/dart/sdk/lib/async/stream.dart -FILE: ../../../third_party/dart/sdk/lib/async/stream_transformers.dart -FILE: ../../../third_party/dart/sdk/lib/async/zone.dart -FILE: ../../../third_party/dart/sdk/lib/collection/collections.dart -FILE: ../../../third_party/dart/sdk/lib/collection/hash_map.dart -FILE: ../../../third_party/dart/sdk/lib/collection/hash_set.dart -FILE: ../../../third_party/dart/sdk/lib/collection/linked_hash_map.dart -FILE: ../../../third_party/dart/sdk/lib/collection/linked_hash_set.dart -FILE: ../../../third_party/dart/sdk/lib/collection/linked_list.dart -FILE: ../../../third_party/dart/sdk/lib/collection/list.dart -FILE: ../../../third_party/dart/sdk/lib/convert/ascii.dart -FILE: ../../../third_party/dart/sdk/lib/convert/byte_conversion.dart -FILE: ../../../third_party/dart/sdk/lib/convert/chunked_conversion.dart -FILE: ../../../third_party/dart/sdk/lib/convert/codec.dart -FILE: ../../../third_party/dart/sdk/lib/convert/convert.dart -FILE: ../../../third_party/dart/sdk/lib/convert/converter.dart -FILE: ../../../third_party/dart/sdk/lib/convert/encoding.dart -FILE: ../../../third_party/dart/sdk/lib/convert/html_escape.dart -FILE: ../../../third_party/dart/sdk/lib/convert/json.dart -FILE: ../../../third_party/dart/sdk/lib/convert/latin1.dart -FILE: ../../../third_party/dart/sdk/lib/convert/line_splitter.dart -FILE: ../../../third_party/dart/sdk/lib/convert/string_conversion.dart -FILE: ../../../third_party/dart/sdk/lib/convert/utf.dart -FILE: ../../../third_party/dart/sdk/lib/core/annotations.dart -FILE: ../../../third_party/dart/sdk/lib/core/null.dart -FILE: ../../../third_party/dart/sdk/lib/core/stacktrace.dart -FILE: ../../../third_party/dart/sdk/lib/core/string_sink.dart -FILE: ../../../third_party/dart/sdk/lib/core/symbol.dart -FILE: ../../../third_party/dart/sdk/lib/internal/bytes_builder.dart -FILE: ../../../third_party/dart/sdk/lib/internal/list.dart -FILE: ../../../third_party/dart/sdk/lib/internal/print.dart -FILE: ../../../third_party/dart/sdk/lib/internal/symbol.dart -FILE: ../../../third_party/dart/sdk/lib/io/data_transformer.dart -FILE: ../../../third_party/dart/sdk/lib/io/file.dart -FILE: ../../../third_party/dart/sdk/lib/io/file_impl.dart -FILE: ../../../third_party/dart/sdk/lib/io/file_system_entity.dart -FILE: ../../../third_party/dart/sdk/lib/io/io_service.dart -FILE: ../../../third_party/dart/sdk/lib/io/io_sink.dart -FILE: ../../../third_party/dart/sdk/lib/io/link.dart -FILE: ../../../third_party/dart/sdk/lib/io/secure_socket.dart -FILE: ../../../third_party/dart/sdk/lib/io/socket.dart -FILE: ../../../third_party/dart/sdk/lib/io/stdio.dart -FILE: ../../../third_party/dart/sdk/lib/io/string_transformer.dart -FILE: ../../../third_party/dart/sdk/lib/js/js.dart -FILE: ../../../third_party/dart/sdk/lib/math/jenkins_smi_hash.dart -FILE: ../../../third_party/dart/sdk/lib/math/point.dart -FILE: ../../../third_party/dart/sdk/lib/math/rectangle.dart -FILE: ../../../third_party/dart/sdk/lib/mirrors/mirrors.dart -FILE: ../../../third_party/dart/sdk/lib/typed_data/typed_data.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/client.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/constants.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/message.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/message_router.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/running_isolate.dart -FILE: ../../../third_party/dart/sdk/lib/vmservice/running_isolates.dart -FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart -FILE: ../../../third_party/dart/utils/compiler/create_snapshot_entry.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: dart -ORIGIN: ../../../third_party/dart/runtime/bin/process_test.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/bin/process_test.cc -FILE: ../../../third_party/dart/runtime/bin/snapshot_empty.cc -FILE: ../../../third_party/dart/runtime/bin/snapshot_in.cc -FILE: ../../../third_party/dart/runtime/include/dart_tools_api.h -FILE: ../../../third_party/dart/runtime/lib/double.cc -FILE: ../../../third_party/dart/runtime/lib/math.cc -FILE: ../../../third_party/dart/runtime/lib/mirrors.h -FILE: ../../../third_party/dart/runtime/lib/object.cc -FILE: ../../../third_party/dart/runtime/lib/string.cc -FILE: ../../../third_party/dart/runtime/vm/bitfield.h -FILE: ../../../third_party/dart/runtime/vm/code_patcher_ia32_test.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_x64_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_test.cc -FILE: ../../../third_party/dart/runtime/vm/cpu_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/custom_isolate_test.cc -FILE: ../../../third_party/dart/runtime/vm/dart.h -FILE: ../../../third_party/dart/runtime/vm/dart_api_impl.h -FILE: ../../../third_party/dart/runtime/vm/dart_entry.cc -FILE: ../../../third_party/dart/runtime/vm/dart_entry.h -FILE: ../../../third_party/dart/runtime/vm/debugger_arm.cc -FILE: ../../../third_party/dart/runtime/vm/debugger_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/debugger_x64.cc -FILE: ../../../third_party/dart/runtime/vm/double_conversion.cc -FILE: ../../../third_party/dart/runtime/vm/double_conversion.h -FILE: ../../../third_party/dart/runtime/vm/exceptions.cc -FILE: ../../../third_party/dart/runtime/vm/exceptions.h -FILE: ../../../third_party/dart/runtime/vm/handle_visitor.h -FILE: ../../../third_party/dart/runtime/vm/handles.h -FILE: ../../../third_party/dart/runtime/vm/heap/freelist.cc -FILE: ../../../third_party/dart/runtime/vm/heap/marker.cc -FILE: ../../../third_party/dart/runtime/vm/heap/marker.h -FILE: ../../../third_party/dart/runtime/vm/heap/pages.h -FILE: ../../../third_party/dart/runtime/vm/heap/scavenger.cc -FILE: ../../../third_party/dart/runtime/vm/heap/sweeper.cc -FILE: ../../../third_party/dart/runtime/vm/heap/sweeper.h -FILE: ../../../third_party/dart/runtime/vm/heap/verifier.h -FILE: ../../../third_party/dart/runtime/vm/longjump.cc -FILE: ../../../third_party/dart/runtime/vm/longjump_test.cc -FILE: ../../../third_party/dart/runtime/vm/memory_region.cc -FILE: ../../../third_party/dart/runtime/vm/message.cc -FILE: ../../../third_party/dart/runtime/vm/message.h -FILE: ../../../third_party/dart/runtime/vm/message_handler.cc -FILE: ../../../third_party/dart/runtime/vm/message_handler.h -FILE: ../../../third_party/dart/runtime/vm/native_entry.cc -FILE: ../../../third_party/dart/runtime/vm/native_entry.h -FILE: ../../../third_party/dart/runtime/vm/native_entry_test.cc -FILE: ../../../third_party/dart/runtime/vm/native_entry_test.h -FILE: ../../../third_party/dart/runtime/vm/native_function.h -FILE: ../../../third_party/dart/runtime/vm/object_store_test.cc -FILE: ../../../third_party/dart/runtime/vm/os.h -FILE: ../../../third_party/dart/runtime/vm/port.h -FILE: ../../../third_party/dart/runtime/vm/resolver.cc -FILE: ../../../third_party/dart/runtime/vm/resolver.h -FILE: ../../../third_party/dart/runtime/vm/runtime_entry.cc -FILE: ../../../third_party/dart/runtime/vm/runtime_entry.h -FILE: ../../../third_party/dart/runtime/vm/runtime_entry_arm.cc -FILE: ../../../third_party/dart/runtime/vm/runtime_entry_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/runtime_entry_list.h -FILE: ../../../third_party/dart/runtime/vm/runtime_entry_x64.cc -FILE: ../../../third_party/dart/runtime/vm/stack_frame.h -FILE: ../../../third_party/dart/runtime/vm/stub_code.h -FILE: ../../../third_party/dart/runtime/vm/stub_code_ia32_test.cc -FILE: ../../../third_party/dart/runtime/vm/stub_code_x64_test.cc -FILE: ../../../third_party/dart/runtime/vm/timer.cc -FILE: ../../../third_party/dart/runtime/vm/timer.h -FILE: ../../../third_party/dart/runtime/vm/token.cc -FILE: ../../../third_party/dart/runtime/vm/unicode_data.cc -FILE: ../../../third_party/dart/runtime/vm/unicode_test.cc -FILE: ../../../third_party/dart/runtime/vm/unit_test.h -FILE: ../../../third_party/dart/runtime/vm/visitor.h -FILE: ../../../third_party/dart/sdk/lib/collection/queue.dart -FILE: ../../../third_party/dart/sdk/lib/core/comparable.dart -FILE: ../../../third_party/dart/sdk/lib/core/date_time.dart -FILE: ../../../third_party/dart/sdk/lib/core/duration.dart -FILE: ../../../third_party/dart/sdk/lib/core/function.dart -FILE: ../../../third_party/dart/sdk/lib/core/iterable.dart -FILE: ../../../third_party/dart/sdk/lib/core/map.dart -FILE: ../../../third_party/dart/sdk/lib/core/pattern.dart -FILE: ../../../third_party/dart/sdk/lib/core/set.dart -FILE: ../../../third_party/dart/sdk/lib/core/stopwatch.dart -FILE: ../../../third_party/dart/sdk/lib/core/string_buffer.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/device.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/filtered_element_list.dart -FILE: ../../../third_party/dart/sdk/lib/html/html_common/lists.dart -FILE: ../../../third_party/dart/sdk/lib/internal/iterable.dart -FILE: ../../../third_party/dart/sdk/lib/internal/sort.dart -FILE: ../../../third_party/dart/utils/peg/pegparser.dart ----------------------------------------------------------------------------------------------------- -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: dart -ORIGIN: ../../../third_party/dart/runtime/lib/class_id.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/lib/class_id.cc -FILE: ../../../third_party/dart/runtime/lib/linked_hash_map.cc -FILE: ../../../third_party/dart/runtime/lib/profiler.cc -FILE: ../../../third_party/dart/runtime/observatory/bin/shell.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/app.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/object_graph.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/service.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/service_common.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/service_html.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/service_io.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/page.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/settings.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/view_model.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/allocation_profile.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_tree.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/debugger.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/heap_map.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate_reconnect.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/metrics.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_connect.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/src/service/object.dart -FILE: ../../../third_party/dart/runtime/observatory/lib/utils.dart -FILE: ../../../third_party/dart/runtime/observatory/web/main.dart -FILE: ../../../third_party/dart/runtime/platform/address_sanitizer.h -FILE: ../../../third_party/dart/runtime/platform/memory_sanitizer.h -FILE: ../../../third_party/dart/runtime/platform/safe_stack.h -FILE: ../../../third_party/dart/runtime/tools/verbose_gc_to_bmu.dart -FILE: ../../../third_party/dart/runtime/vm/bit_set_test.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm64_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm64.h -FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/range_analysis.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/range_analysis.h -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/range_analysis_test.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/method_recognizer.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/method_recognizer.h -FILE: ../../../third_party/dart/runtime/vm/constants_arm64.h -FILE: ../../../third_party/dart/runtime/vm/cpu_arm.h -FILE: ../../../third_party/dart/runtime/vm/cpu_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/cpu_arm64.h -FILE: ../../../third_party/dart/runtime/vm/cpu_ia32.h -FILE: ../../../third_party/dart/runtime/vm/cpu_x64.h -FILE: ../../../third_party/dart/runtime/vm/cpuid.cc -FILE: ../../../third_party/dart/runtime/vm/cpuid.h -FILE: ../../../third_party/dart/runtime/vm/cpuinfo.h -FILE: ../../../third_party/dart/runtime/vm/cpuinfo_android.cc -FILE: ../../../third_party/dart/runtime/vm/cpuinfo_test.cc -FILE: ../../../third_party/dart/runtime/vm/cpuinfo_win.cc -FILE: ../../../third_party/dart/runtime/vm/debugger_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/hash_table.h -FILE: ../../../third_party/dart/runtime/vm/hash_table_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/scavenger_test.cc -FILE: ../../../third_party/dart/runtime/vm/heap/spaces.h -FILE: ../../../third_party/dart/runtime/vm/heap/weak_code.cc -FILE: ../../../third_party/dart/runtime/vm/heap/weak_code.h -FILE: ../../../third_party/dart/runtime/vm/instructions_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/instructions_arm64.h -FILE: ../../../third_party/dart/runtime/vm/instructions_arm64_test.cc -FILE: ../../../third_party/dart/runtime/vm/metrics.cc -FILE: ../../../third_party/dart/runtime/vm/metrics.h -FILE: ../../../third_party/dart/runtime/vm/metrics_test.cc -FILE: ../../../third_party/dart/runtime/vm/object_arm64_test.cc -FILE: ../../../third_party/dart/runtime/vm/object_graph.cc -FILE: ../../../third_party/dart/runtime/vm/object_graph.h -FILE: ../../../third_party/dart/runtime/vm/object_graph_test.cc -FILE: ../../../third_party/dart/runtime/vm/regexp.cc -FILE: ../../../third_party/dart/runtime/vm/regexp.h -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler.cc -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler.h -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_ir.cc -FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_ir.h -FILE: ../../../third_party/dart/runtime/vm/regexp_ast.cc -FILE: ../../../third_party/dart/runtime/vm/regexp_ast.h -FILE: ../../../third_party/dart/runtime/vm/regexp_parser.cc -FILE: ../../../third_party/dart/runtime/vm/regexp_parser.h -FILE: ../../../third_party/dart/runtime/vm/regexp_test.cc -FILE: ../../../third_party/dart/runtime/vm/report.cc -FILE: ../../../third_party/dart/runtime/vm/report.h -FILE: ../../../third_party/dart/runtime/vm/ring_buffer.h -FILE: ../../../third_party/dart/runtime/vm/ring_buffer_test.cc -FILE: ../../../third_party/dart/runtime/vm/runtime_entry_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/simulator_arm64.cc -FILE: ../../../third_party/dart/runtime/vm/simulator_arm64.h -FILE: ../../../third_party/dart/runtime/vm/stack_frame_arm64.h -FILE: ../../../third_party/dart/runtime/vm/stub_code_arm64_test.cc -FILE: ../../../third_party/dart/runtime/vm/tags.cc -FILE: ../../../third_party/dart/runtime/vm/unibrow-inl.h -FILE: ../../../third_party/dart/runtime/vm/unibrow.cc -FILE: ../../../third_party/dart/runtime/vm/unibrow.h -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/d8.js -FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/jsshell.js -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/linked_hash_map.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/class_id.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/convert_patch.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/lib_prefix.dart -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/profiler.dart -FILE: ../../../third_party/dart/sdk/lib/collection/set.dart -FILE: ../../../third_party/dart/sdk/lib/core/sink.dart -FILE: ../../../third_party/dart/sdk/lib/developer/profiler.dart -FILE: ../../../third_party/dart/sdk/lib/io/process.dart -FILE: ../../../third_party/dart/sdk/lib/io/service_object.dart -FILE: ../../../third_party/dart/sdk/lib/isolate/capability.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: dart -ORIGIN: ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014 The Polymer 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: dart -ORIGIN: ../../../third_party/dart/runtime/platform/splay-tree-inl.h + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/platform/splay-tree-inl.h -FILE: ../../../third_party/dart/runtime/platform/splay-tree.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2010, 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: dart -ORIGIN: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_scope_builder.h + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/bytecode_scope_builder.h -FILE: ../../../third_party/dart/sdk/lib/io/network_profiling.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2019, 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: dart -ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart -TYPE: LicenseType.mit -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2003-2005 Tom Wu -Copyright (c) 2012 Adam Singer (adam@solvr.io) -All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, -EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY -WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - -IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, -INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER -RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF -THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -In addition, the following condition applies: - -All redistributions must retain an intact copy of this copyright notice -and disclaimer. -==================================================================================================== - -==================================================================================================== -LIBRARY: dart -ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart + ../../../third_party/boringssl/src/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart ----------------------------------------------------------------------------------------------------- -Copyright 2009 The Go Authors. All rights reserved. -Use of this source code is governed by a BSD-style -license that can be found in the LICENSE file -==================================================================================================== - -==================================================================================================== -LIBRARY: double-conversion -LIBRARY: icu -ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum-dtoa.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum-dtoa.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum-dtoa.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/utils.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-diy-fp.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-utils.h ----------------------------------------------------------------------------------------------------- -Copyright 2010 the V8 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: double-conversion -LIBRARY: icu -ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.cc -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.cc -FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2006-2008 the V8 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: double-conversion -LIBRARY: icu -ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.cc -FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/ieee.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-ieee.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.h -FILE: ../../../third_party/icu/source/i18n/double-conversion.h ----------------------------------------------------------------------------------------------------- -Copyright 2012 the V8 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: files -ORIGIN: ../../../third_party/expat/files/COPYING -TYPE: LicenseType.mit -FILE: ../../../third_party/expat/files/Changes -FILE: ../../../third_party/expat/files/MANIFEST -FILE: ../../../third_party/expat/files/lib/amigaconfig.h -FILE: ../../../third_party/expat/files/lib/expat_config.h -FILE: ../../../third_party/expat/files/lib/internal.h -FILE: ../../../third_party/expat/files/lib/libexpat.def -FILE: ../../../third_party/expat/files/lib/libexpatw.def -FILE: ../../../third_party/expat/files/lib/nametab.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - and Clark Cooper -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: files -ORIGIN: ../../../third_party/expat/files/lib/ascii.h + ../../../third_party/expat/files/COPYING -TYPE: LicenseType.mit -FILE: ../../../third_party/expat/files/lib/ascii.h -FILE: ../../../third_party/expat/files/lib/asciitab.h -FILE: ../../../third_party/expat/files/lib/iasciitab.h -FILE: ../../../third_party/expat/files/lib/latin1tab.h -FILE: ../../../third_party/expat/files/lib/utf8tab.h -FILE: ../../../third_party/expat/files/lib/xmlrole.c -FILE: ../../../third_party/expat/files/lib/xmlrole.h -FILE: ../../../third_party/expat/files/lib/xmltok.c -FILE: ../../../third_party/expat/files/lib/xmltok.h -FILE: ../../../third_party/expat/files/lib/xmltok_impl.c -FILE: ../../../third_party/expat/files/lib/xmltok_impl.c.original -FILE: ../../../third_party/expat/files/lib/xmltok_impl.h -FILE: ../../../third_party/expat/files/lib/xmltok_ns.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: files -ORIGIN: ../../../third_party/expat/files/lib/expat.h + ../../../third_party/expat/files/COPYING -TYPE: LicenseType.mit -FILE: ../../../third_party/expat/files/lib/expat.h -FILE: ../../../third_party/expat/files/lib/expat_external.h -FILE: ../../../third_party/expat/files/lib/xmlparse.c -FILE: ../../../third_party/expat/files/lib/xmlparse.c.original ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: files -ORIGIN: ../../../third_party/expat/files/lib/macconfig.h + ../../../third_party/expat/files/COPYING -TYPE: LicenseType.mit -FILE: ../../../third_party/expat/files/lib/macconfig.h -FILE: ../../../third_party/expat/files/lib/winconfig.h -FILE: ../../../third_party/expat/files/lib/winconfig.h.original ----------------------------------------------------------------------------------------------------- -Copyright 2000, Clark Cooper -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/docs/FTL.TXT -TYPE: LicenseType.freetype -FILE: ../../../third_party/freetype2/.mailmap -FILE: ../../../third_party/freetype2/Jamfile -FILE: ../../../third_party/freetype2/Jamrules -FILE: ../../../third_party/freetype2/devel/ft2build.h -FILE: ../../../third_party/freetype2/devel/ftoption.h -FILE: ../../../third_party/freetype2/include/freetype-fuchsia-config/ftmodule.h -FILE: ../../../third_party/freetype2/include/freetype-fuchsia-config/ftoption.h -FILE: ../../../third_party/freetype2/include/freetype/config/ftconfig.h -FILE: ../../../third_party/freetype2/include/freetype/config/ftheader.h -FILE: ../../../third_party/freetype2/include/freetype/config/ftmodule.h -FILE: ../../../third_party/freetype2/include/freetype/config/ftoption.h -FILE: ../../../third_party/freetype2/include/freetype/config/ftstdlib.h -FILE: ../../../third_party/freetype2/include/freetype/freetype.h -FILE: ../../../third_party/freetype2/include/freetype/ftadvanc.h -FILE: ../../../third_party/freetype2/include/freetype/ftbbox.h -FILE: ../../../third_party/freetype2/include/freetype/ftbdf.h -FILE: ../../../third_party/freetype2/include/freetype/ftbitmap.h -FILE: ../../../third_party/freetype2/include/freetype/ftbzip2.h -FILE: ../../../third_party/freetype2/include/freetype/ftcache.h -FILE: ../../../third_party/freetype2/include/freetype/ftchapters.h -FILE: ../../../third_party/freetype2/include/freetype/ftcid.h -FILE: ../../../third_party/freetype2/include/freetype/ftdriver.h -FILE: ../../../third_party/freetype2/include/freetype/fterrdef.h -FILE: ../../../third_party/freetype2/include/freetype/fterrors.h -FILE: ../../../third_party/freetype2/include/freetype/ftfntfmt.h -FILE: ../../../third_party/freetype2/include/freetype/ftgasp.h -FILE: ../../../third_party/freetype2/include/freetype/ftglyph.h -FILE: ../../../third_party/freetype2/include/freetype/ftgxval.h -FILE: ../../../third_party/freetype2/include/freetype/ftgzip.h -FILE: ../../../third_party/freetype2/include/freetype/ftimage.h -FILE: ../../../third_party/freetype2/include/freetype/ftincrem.h -FILE: ../../../third_party/freetype2/include/freetype/ftlcdfil.h -FILE: ../../../third_party/freetype2/include/freetype/ftlist.h -FILE: ../../../third_party/freetype2/include/freetype/ftlzw.h -FILE: ../../../third_party/freetype2/include/freetype/ftmac.h -FILE: ../../../third_party/freetype2/include/freetype/ftmm.h -FILE: ../../../third_party/freetype2/include/freetype/ftmodapi.h -FILE: ../../../third_party/freetype2/include/freetype/ftmoderr.h -FILE: ../../../third_party/freetype2/include/freetype/ftotval.h -FILE: ../../../third_party/freetype2/include/freetype/ftoutln.h -FILE: ../../../third_party/freetype2/include/freetype/ftparams.h -FILE: ../../../third_party/freetype2/include/freetype/ftpfr.h -FILE: ../../../third_party/freetype2/include/freetype/ftrender.h -FILE: ../../../third_party/freetype2/include/freetype/ftsizes.h -FILE: ../../../third_party/freetype2/include/freetype/ftsnames.h -FILE: ../../../third_party/freetype2/include/freetype/ftstroke.h -FILE: ../../../third_party/freetype2/include/freetype/ftsynth.h -FILE: ../../../third_party/freetype2/include/freetype/ftsystem.h -FILE: ../../../third_party/freetype2/include/freetype/fttrigon.h -FILE: ../../../third_party/freetype2/include/freetype/fttypes.h -FILE: ../../../third_party/freetype2/include/freetype/ftwinfnt.h -FILE: ../../../third_party/freetype2/include/freetype/internal/autohint.h -FILE: ../../../third_party/freetype2/include/freetype/internal/cffotypes.h -FILE: ../../../third_party/freetype2/include/freetype/internal/cfftypes.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftcalc.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftdebug.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftdrv.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftgloadr.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftmemory.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftobjs.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftpic.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftpsprop.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftrfork.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftserv.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftstream.h -FILE: ../../../third_party/freetype2/include/freetype/internal/fttrace.h -FILE: ../../../third_party/freetype2/include/freetype/internal/ftvalid.h -FILE: ../../../third_party/freetype2/include/freetype/internal/internal.h -FILE: ../../../third_party/freetype2/include/freetype/internal/psaux.h -FILE: ../../../third_party/freetype2/include/freetype/internal/pshints.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svbdf.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svcfftl.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svcid.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svfntfmt.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svgldict.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svgxval.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svkern.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svmetric.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svmm.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svotval.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpfr.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpostnm.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svprop.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpscmap.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpsinfo.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svsfnt.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svttcmap.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svtteng.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svttglyf.h -FILE: ../../../third_party/freetype2/include/freetype/internal/services/svwinfnt.h -FILE: ../../../third_party/freetype2/include/freetype/internal/sfnt.h -FILE: ../../../third_party/freetype2/include/freetype/internal/t1types.h -FILE: ../../../third_party/freetype2/include/freetype/internal/tttypes.h -FILE: ../../../third_party/freetype2/include/freetype/t1tables.h -FILE: ../../../third_party/freetype2/include/freetype/ttnameid.h -FILE: ../../../third_party/freetype2/include/freetype/tttables.h -FILE: ../../../third_party/freetype2/include/freetype/tttags.h -FILE: ../../../third_party/freetype2/include/ft2build.h -FILE: ../../../third_party/freetype2/modules.cfg -FILE: ../../../third_party/freetype2/src/Jamfile -FILE: ../../../third_party/freetype2/src/autofit/Jamfile -FILE: ../../../third_party/freetype2/src/autofit/afangles.c -FILE: ../../../third_party/freetype2/src/autofit/afangles.h -FILE: ../../../third_party/freetype2/src/autofit/afblue.c -FILE: ../../../third_party/freetype2/src/autofit/afblue.cin -FILE: ../../../third_party/freetype2/src/autofit/afblue.dat -FILE: ../../../third_party/freetype2/src/autofit/afblue.h -FILE: ../../../third_party/freetype2/src/autofit/afblue.hin -FILE: ../../../third_party/freetype2/src/autofit/afcjk.c -FILE: ../../../third_party/freetype2/src/autofit/afcjk.h -FILE: ../../../third_party/freetype2/src/autofit/afcover.h -FILE: ../../../third_party/freetype2/src/autofit/afdummy.c -FILE: ../../../third_party/freetype2/src/autofit/afdummy.h -FILE: ../../../third_party/freetype2/src/autofit/aferrors.h -FILE: ../../../third_party/freetype2/src/autofit/afglobal.c -FILE: ../../../third_party/freetype2/src/autofit/afglobal.h -FILE: ../../../third_party/freetype2/src/autofit/afhints.c -FILE: ../../../third_party/freetype2/src/autofit/afhints.h -FILE: ../../../third_party/freetype2/src/autofit/afindic.c -FILE: ../../../third_party/freetype2/src/autofit/afindic.h -FILE: ../../../third_party/freetype2/src/autofit/aflatin.c -FILE: ../../../third_party/freetype2/src/autofit/aflatin.h -FILE: ../../../third_party/freetype2/src/autofit/aflatin2.c -FILE: ../../../third_party/freetype2/src/autofit/aflatin2.h -FILE: ../../../third_party/freetype2/src/autofit/afloader.c -FILE: ../../../third_party/freetype2/src/autofit/afloader.h -FILE: ../../../third_party/freetype2/src/autofit/afmodule.c -FILE: ../../../third_party/freetype2/src/autofit/afmodule.h -FILE: ../../../third_party/freetype2/src/autofit/afpic.c -FILE: ../../../third_party/freetype2/src/autofit/afpic.h -FILE: ../../../third_party/freetype2/src/autofit/afranges.c -FILE: ../../../third_party/freetype2/src/autofit/afranges.h -FILE: ../../../third_party/freetype2/src/autofit/afscript.h -FILE: ../../../third_party/freetype2/src/autofit/afshaper.c -FILE: ../../../third_party/freetype2/src/autofit/afshaper.h -FILE: ../../../third_party/freetype2/src/autofit/afstyles.h -FILE: ../../../third_party/freetype2/src/autofit/aftypes.h -FILE: ../../../third_party/freetype2/src/autofit/afwarp.c -FILE: ../../../third_party/freetype2/src/autofit/afwarp.h -FILE: ../../../third_party/freetype2/src/autofit/afwrtsys.h -FILE: ../../../third_party/freetype2/src/autofit/autofit.c -FILE: ../../../third_party/freetype2/src/base/Jamfile -FILE: ../../../third_party/freetype2/src/base/basepic.c -FILE: ../../../third_party/freetype2/src/base/basepic.h -FILE: ../../../third_party/freetype2/src/base/ftadvanc.c -FILE: ../../../third_party/freetype2/src/base/ftapi.c -FILE: ../../../third_party/freetype2/src/base/ftbase.c -FILE: ../../../third_party/freetype2/src/base/ftbase.h -FILE: ../../../third_party/freetype2/src/base/ftbbox.c -FILE: ../../../third_party/freetype2/src/base/ftbdf.c -FILE: ../../../third_party/freetype2/src/base/ftbitmap.c -FILE: ../../../third_party/freetype2/src/base/ftcalc.c -FILE: ../../../third_party/freetype2/src/base/ftcid.c -FILE: ../../../third_party/freetype2/src/base/ftdbgmem.c -FILE: ../../../third_party/freetype2/src/base/ftdebug.c -FILE: ../../../third_party/freetype2/src/base/ftfntfmt.c -FILE: ../../../third_party/freetype2/src/base/ftfstype.c -FILE: ../../../third_party/freetype2/src/base/ftgasp.c -FILE: ../../../third_party/freetype2/src/base/ftgloadr.c -FILE: ../../../third_party/freetype2/src/base/ftglyph.c -FILE: ../../../third_party/freetype2/src/base/ftgxval.c -FILE: ../../../third_party/freetype2/src/base/ftinit.c -FILE: ../../../third_party/freetype2/src/base/ftlcdfil.c -FILE: ../../../third_party/freetype2/src/base/ftmac.c -FILE: ../../../third_party/freetype2/src/base/ftmm.c -FILE: ../../../third_party/freetype2/src/base/ftobjs.c -FILE: ../../../third_party/freetype2/src/base/ftotval.c -FILE: ../../../third_party/freetype2/src/base/ftoutln.c -FILE: ../../../third_party/freetype2/src/base/ftpatent.c -FILE: ../../../third_party/freetype2/src/base/ftpfr.c -FILE: ../../../third_party/freetype2/src/base/ftpic.c -FILE: ../../../third_party/freetype2/src/base/ftpsprop.c -FILE: ../../../third_party/freetype2/src/base/ftrfork.c -FILE: ../../../third_party/freetype2/src/base/ftsnames.c -FILE: ../../../third_party/freetype2/src/base/ftstream.c -FILE: ../../../third_party/freetype2/src/base/ftstroke.c -FILE: ../../../third_party/freetype2/src/base/ftsynth.c -FILE: ../../../third_party/freetype2/src/base/ftsystem.c -FILE: ../../../third_party/freetype2/src/base/fttrigon.c -FILE: ../../../third_party/freetype2/src/base/fttype1.c -FILE: ../../../third_party/freetype2/src/base/ftutil.c -FILE: ../../../third_party/freetype2/src/base/ftver.rc -FILE: ../../../third_party/freetype2/src/base/ftwinfnt.c -FILE: ../../../third_party/freetype2/src/base/md5.c -FILE: ../../../third_party/freetype2/src/base/md5.h -FILE: ../../../third_party/freetype2/src/bdf/Jamfile -FILE: ../../../third_party/freetype2/src/bzip2/Jamfile -FILE: ../../../third_party/freetype2/src/bzip2/ftbzip2.c -FILE: ../../../third_party/freetype2/src/cache/Jamfile -FILE: ../../../third_party/freetype2/src/cache/ftcache.c -FILE: ../../../third_party/freetype2/src/cache/ftcbasic.c -FILE: ../../../third_party/freetype2/src/cache/ftccache.c -FILE: ../../../third_party/freetype2/src/cache/ftccache.h -FILE: ../../../third_party/freetype2/src/cache/ftccback.h -FILE: ../../../third_party/freetype2/src/cache/ftccmap.c -FILE: ../../../third_party/freetype2/src/cache/ftcerror.h -FILE: ../../../third_party/freetype2/src/cache/ftcglyph.c -FILE: ../../../third_party/freetype2/src/cache/ftcglyph.h -FILE: ../../../third_party/freetype2/src/cache/ftcimage.c -FILE: ../../../third_party/freetype2/src/cache/ftcimage.h -FILE: ../../../third_party/freetype2/src/cache/ftcmanag.c -FILE: ../../../third_party/freetype2/src/cache/ftcmanag.h -FILE: ../../../third_party/freetype2/src/cache/ftcmru.c -FILE: ../../../third_party/freetype2/src/cache/ftcmru.h -FILE: ../../../third_party/freetype2/src/cache/ftcsbits.c -FILE: ../../../third_party/freetype2/src/cache/ftcsbits.h -FILE: ../../../third_party/freetype2/src/cff/Jamfile -FILE: ../../../third_party/freetype2/src/cff/cff.c -FILE: ../../../third_party/freetype2/src/cff/cffcmap.c -FILE: ../../../third_party/freetype2/src/cff/cffcmap.h -FILE: ../../../third_party/freetype2/src/cff/cffdrivr.c -FILE: ../../../third_party/freetype2/src/cff/cffdrivr.h -FILE: ../../../third_party/freetype2/src/cff/cfferrs.h -FILE: ../../../third_party/freetype2/src/cff/cffgload.c -FILE: ../../../third_party/freetype2/src/cff/cffgload.h -FILE: ../../../third_party/freetype2/src/cff/cffload.c -FILE: ../../../third_party/freetype2/src/cff/cffload.h -FILE: ../../../third_party/freetype2/src/cff/cffobjs.c -FILE: ../../../third_party/freetype2/src/cff/cffobjs.h -FILE: ../../../third_party/freetype2/src/cff/cffparse.c -FILE: ../../../third_party/freetype2/src/cff/cffparse.h -FILE: ../../../third_party/freetype2/src/cff/cffpic.c -FILE: ../../../third_party/freetype2/src/cff/cffpic.h -FILE: ../../../third_party/freetype2/src/cff/cfftoken.h -FILE: ../../../third_party/freetype2/src/cid/Jamfile -FILE: ../../../third_party/freetype2/src/cid/ciderrs.h -FILE: ../../../third_party/freetype2/src/cid/cidgload.c -FILE: ../../../third_party/freetype2/src/cid/cidgload.h -FILE: ../../../third_party/freetype2/src/cid/cidload.c -FILE: ../../../third_party/freetype2/src/cid/cidload.h -FILE: ../../../third_party/freetype2/src/cid/cidobjs.c -FILE: ../../../third_party/freetype2/src/cid/cidobjs.h -FILE: ../../../third_party/freetype2/src/cid/cidparse.c -FILE: ../../../third_party/freetype2/src/cid/cidparse.h -FILE: ../../../third_party/freetype2/src/cid/cidriver.c -FILE: ../../../third_party/freetype2/src/cid/cidriver.h -FILE: ../../../third_party/freetype2/src/cid/cidtoken.h -FILE: ../../../third_party/freetype2/src/cid/type1cid.c -FILE: ../../../third_party/freetype2/src/gxvalid/Jamfile -FILE: ../../../third_party/freetype2/src/gxvalid/gxvalid.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvalid.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxvbsln.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvcommn.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvcommn.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxverror.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxvfeat.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvfeat.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxvfgen.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvjust.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvkern.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvlcar.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmod.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmod.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort0.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort1.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort2.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort4.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort5.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx.h -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx0.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx1.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx2.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx4.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx5.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvopbd.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvprop.c -FILE: ../../../third_party/freetype2/src/gxvalid/gxvtrak.c -FILE: ../../../third_party/freetype2/src/gzip/Jamfile -FILE: ../../../third_party/freetype2/src/gzip/ftgzip.c -FILE: ../../../third_party/freetype2/src/lzw/Jamfile -FILE: ../../../third_party/freetype2/src/lzw/ftlzw.c -FILE: ../../../third_party/freetype2/src/lzw/ftzopen.c -FILE: ../../../third_party/freetype2/src/lzw/ftzopen.h -FILE: ../../../third_party/freetype2/src/otvalid/Jamfile -FILE: ../../../third_party/freetype2/src/otvalid/otvalid.c -FILE: ../../../third_party/freetype2/src/otvalid/otvalid.h -FILE: ../../../third_party/freetype2/src/otvalid/otvbase.c -FILE: ../../../third_party/freetype2/src/otvalid/otvcommn.c -FILE: ../../../third_party/freetype2/src/otvalid/otvcommn.h -FILE: ../../../third_party/freetype2/src/otvalid/otverror.h -FILE: ../../../third_party/freetype2/src/otvalid/otvgdef.c -FILE: ../../../third_party/freetype2/src/otvalid/otvgpos.c -FILE: ../../../third_party/freetype2/src/otvalid/otvgpos.h -FILE: ../../../third_party/freetype2/src/otvalid/otvgsub.c -FILE: ../../../third_party/freetype2/src/otvalid/otvjstf.c -FILE: ../../../third_party/freetype2/src/otvalid/otvmath.c -FILE: ../../../third_party/freetype2/src/otvalid/otvmod.c -FILE: ../../../third_party/freetype2/src/otvalid/otvmod.h -FILE: ../../../third_party/freetype2/src/pcf/Jamfile -FILE: ../../../third_party/freetype2/src/pcf/pcferror.h -FILE: ../../../third_party/freetype2/src/pfr/Jamfile -FILE: ../../../third_party/freetype2/src/pfr/pfr.c -FILE: ../../../third_party/freetype2/src/pfr/pfrcmap.c -FILE: ../../../third_party/freetype2/src/pfr/pfrcmap.h -FILE: ../../../third_party/freetype2/src/pfr/pfrdrivr.c -FILE: ../../../third_party/freetype2/src/pfr/pfrdrivr.h -FILE: ../../../third_party/freetype2/src/pfr/pfrerror.h -FILE: ../../../third_party/freetype2/src/pfr/pfrgload.c -FILE: ../../../third_party/freetype2/src/pfr/pfrgload.h -FILE: ../../../third_party/freetype2/src/pfr/pfrload.c -FILE: ../../../third_party/freetype2/src/pfr/pfrload.h -FILE: ../../../third_party/freetype2/src/pfr/pfrobjs.c -FILE: ../../../third_party/freetype2/src/pfr/pfrobjs.h -FILE: ../../../third_party/freetype2/src/pfr/pfrsbit.c -FILE: ../../../third_party/freetype2/src/pfr/pfrsbit.h -FILE: ../../../third_party/freetype2/src/pfr/pfrtypes.h -FILE: ../../../third_party/freetype2/src/psaux/Jamfile -FILE: ../../../third_party/freetype2/src/psaux/afmparse.c -FILE: ../../../third_party/freetype2/src/psaux/afmparse.h -FILE: ../../../third_party/freetype2/src/psaux/cffdecode.c -FILE: ../../../third_party/freetype2/src/psaux/cffdecode.h -FILE: ../../../third_party/freetype2/src/psaux/psarrst.c -FILE: ../../../third_party/freetype2/src/psaux/psarrst.h -FILE: ../../../third_party/freetype2/src/psaux/psaux.c -FILE: ../../../third_party/freetype2/src/psaux/psauxerr.h -FILE: ../../../third_party/freetype2/src/psaux/psauxmod.c -FILE: ../../../third_party/freetype2/src/psaux/psauxmod.h -FILE: ../../../third_party/freetype2/src/psaux/psblues.c -FILE: ../../../third_party/freetype2/src/psaux/psblues.h -FILE: ../../../third_party/freetype2/src/psaux/psconv.c -FILE: ../../../third_party/freetype2/src/psaux/psconv.h -FILE: ../../../third_party/freetype2/src/psaux/pserror.c -FILE: ../../../third_party/freetype2/src/psaux/pserror.h -FILE: ../../../third_party/freetype2/src/psaux/psfixed.h -FILE: ../../../third_party/freetype2/src/psaux/psfont.c -FILE: ../../../third_party/freetype2/src/psaux/psfont.h -FILE: ../../../third_party/freetype2/src/psaux/psft.c -FILE: ../../../third_party/freetype2/src/psaux/psft.h -FILE: ../../../third_party/freetype2/src/psaux/psglue.h -FILE: ../../../third_party/freetype2/src/psaux/pshints.c -FILE: ../../../third_party/freetype2/src/psaux/pshints.h -FILE: ../../../third_party/freetype2/src/psaux/psintrp.c -FILE: ../../../third_party/freetype2/src/psaux/psintrp.h -FILE: ../../../third_party/freetype2/src/psaux/psobjs.c -FILE: ../../../third_party/freetype2/src/psaux/psobjs.h -FILE: ../../../third_party/freetype2/src/psaux/psread.c -FILE: ../../../third_party/freetype2/src/psaux/psread.h -FILE: ../../../third_party/freetype2/src/psaux/psstack.c -FILE: ../../../third_party/freetype2/src/psaux/psstack.h -FILE: ../../../third_party/freetype2/src/psaux/pstypes.h -FILE: ../../../third_party/freetype2/src/psaux/t1cmap.c -FILE: ../../../third_party/freetype2/src/psaux/t1cmap.h -FILE: ../../../third_party/freetype2/src/psaux/t1decode.c -FILE: ../../../third_party/freetype2/src/psaux/t1decode.h -FILE: ../../../third_party/freetype2/src/pshinter/Jamfile -FILE: ../../../third_party/freetype2/src/pshinter/pshalgo.c -FILE: ../../../third_party/freetype2/src/pshinter/pshalgo.h -FILE: ../../../third_party/freetype2/src/pshinter/pshglob.c -FILE: ../../../third_party/freetype2/src/pshinter/pshglob.h -FILE: ../../../third_party/freetype2/src/pshinter/pshinter.c -FILE: ../../../third_party/freetype2/src/pshinter/pshmod.c -FILE: ../../../third_party/freetype2/src/pshinter/pshmod.h -FILE: ../../../third_party/freetype2/src/pshinter/pshnterr.h -FILE: ../../../third_party/freetype2/src/pshinter/pshpic.c -FILE: ../../../third_party/freetype2/src/pshinter/pshpic.h -FILE: ../../../third_party/freetype2/src/pshinter/pshrec.c -FILE: ../../../third_party/freetype2/src/pshinter/pshrec.h -FILE: ../../../third_party/freetype2/src/psnames/Jamfile -FILE: ../../../third_party/freetype2/src/psnames/psmodule.c -FILE: ../../../third_party/freetype2/src/psnames/psmodule.h -FILE: ../../../third_party/freetype2/src/psnames/psnamerr.h -FILE: ../../../third_party/freetype2/src/psnames/psnames.c -FILE: ../../../third_party/freetype2/src/psnames/pspic.c -FILE: ../../../third_party/freetype2/src/psnames/pspic.h -FILE: ../../../third_party/freetype2/src/psnames/pstables.h -FILE: ../../../third_party/freetype2/src/raster/Jamfile -FILE: ../../../third_party/freetype2/src/raster/ftmisc.h -FILE: ../../../third_party/freetype2/src/raster/ftraster.c -FILE: ../../../third_party/freetype2/src/raster/ftraster.h -FILE: ../../../third_party/freetype2/src/raster/ftrend1.c -FILE: ../../../third_party/freetype2/src/raster/ftrend1.h -FILE: ../../../third_party/freetype2/src/raster/raster.c -FILE: ../../../third_party/freetype2/src/raster/rasterrs.h -FILE: ../../../third_party/freetype2/src/raster/rastpic.c -FILE: ../../../third_party/freetype2/src/raster/rastpic.h -FILE: ../../../third_party/freetype2/src/sfnt/Jamfile -FILE: ../../../third_party/freetype2/src/sfnt/pngshim.c -FILE: ../../../third_party/freetype2/src/sfnt/pngshim.h -FILE: ../../../third_party/freetype2/src/sfnt/sfdriver.c -FILE: ../../../third_party/freetype2/src/sfnt/sfdriver.h -FILE: ../../../third_party/freetype2/src/sfnt/sferrors.h -FILE: ../../../third_party/freetype2/src/sfnt/sfnt.c -FILE: ../../../third_party/freetype2/src/sfnt/sfntpic.c -FILE: ../../../third_party/freetype2/src/sfnt/sfntpic.h -FILE: ../../../third_party/freetype2/src/sfnt/sfobjs.c -FILE: ../../../third_party/freetype2/src/sfnt/sfobjs.h -FILE: ../../../third_party/freetype2/src/sfnt/ttbdf.c -FILE: ../../../third_party/freetype2/src/sfnt/ttbdf.h -FILE: ../../../third_party/freetype2/src/sfnt/ttcmap.c -FILE: ../../../third_party/freetype2/src/sfnt/ttcmap.h -FILE: ../../../third_party/freetype2/src/sfnt/ttcmapc.h -FILE: ../../../third_party/freetype2/src/sfnt/ttkern.c -FILE: ../../../third_party/freetype2/src/sfnt/ttkern.h -FILE: ../../../third_party/freetype2/src/sfnt/ttload.c -FILE: ../../../third_party/freetype2/src/sfnt/ttload.h -FILE: ../../../third_party/freetype2/src/sfnt/ttmtx.c -FILE: ../../../third_party/freetype2/src/sfnt/ttmtx.h -FILE: ../../../third_party/freetype2/src/sfnt/ttpost.c -FILE: ../../../third_party/freetype2/src/sfnt/ttpost.h -FILE: ../../../third_party/freetype2/src/sfnt/ttsbit.c -FILE: ../../../third_party/freetype2/src/sfnt/ttsbit.h -FILE: ../../../third_party/freetype2/src/smooth/Jamfile -FILE: ../../../third_party/freetype2/src/smooth/ftgrays.c -FILE: ../../../third_party/freetype2/src/smooth/ftgrays.h -FILE: ../../../third_party/freetype2/src/smooth/ftsmerrs.h -FILE: ../../../third_party/freetype2/src/smooth/ftsmooth.c -FILE: ../../../third_party/freetype2/src/smooth/ftsmooth.h -FILE: ../../../third_party/freetype2/src/smooth/ftspic.c -FILE: ../../../third_party/freetype2/src/smooth/ftspic.h -FILE: ../../../third_party/freetype2/src/smooth/smooth.c -FILE: ../../../third_party/freetype2/src/truetype/Jamfile -FILE: ../../../third_party/freetype2/src/truetype/truetype.c -FILE: ../../../third_party/freetype2/src/truetype/ttdriver.c -FILE: ../../../third_party/freetype2/src/truetype/ttdriver.h -FILE: ../../../third_party/freetype2/src/truetype/tterrors.h -FILE: ../../../third_party/freetype2/src/truetype/ttgload.c -FILE: ../../../third_party/freetype2/src/truetype/ttgload.h -FILE: ../../../third_party/freetype2/src/truetype/ttgxvar.c -FILE: ../../../third_party/freetype2/src/truetype/ttgxvar.h -FILE: ../../../third_party/freetype2/src/truetype/ttinterp.c -FILE: ../../../third_party/freetype2/src/truetype/ttinterp.h -FILE: ../../../third_party/freetype2/src/truetype/ttobjs.c -FILE: ../../../third_party/freetype2/src/truetype/ttobjs.h -FILE: ../../../third_party/freetype2/src/truetype/ttpic.c -FILE: ../../../third_party/freetype2/src/truetype/ttpic.h -FILE: ../../../third_party/freetype2/src/truetype/ttpload.c -FILE: ../../../third_party/freetype2/src/truetype/ttpload.h -FILE: ../../../third_party/freetype2/src/truetype/ttsubpix.c -FILE: ../../../third_party/freetype2/src/truetype/ttsubpix.h -FILE: ../../../third_party/freetype2/src/type1/Jamfile -FILE: ../../../third_party/freetype2/src/type1/t1afm.c -FILE: ../../../third_party/freetype2/src/type1/t1afm.h -FILE: ../../../third_party/freetype2/src/type1/t1driver.c -FILE: ../../../third_party/freetype2/src/type1/t1driver.h -FILE: ../../../third_party/freetype2/src/type1/t1errors.h -FILE: ../../../third_party/freetype2/src/type1/t1gload.c -FILE: ../../../third_party/freetype2/src/type1/t1gload.h -FILE: ../../../third_party/freetype2/src/type1/t1load.c -FILE: ../../../third_party/freetype2/src/type1/t1load.h -FILE: ../../../third_party/freetype2/src/type1/t1objs.c -FILE: ../../../third_party/freetype2/src/type1/t1objs.h -FILE: ../../../third_party/freetype2/src/type1/t1parse.c -FILE: ../../../third_party/freetype2/src/type1/t1parse.h -FILE: ../../../third_party/freetype2/src/type1/t1tokens.h -FILE: ../../../third_party/freetype2/src/type1/type1.c -FILE: ../../../third_party/freetype2/src/type42/Jamfile -FILE: ../../../third_party/freetype2/src/type42/t42drivr.c -FILE: ../../../third_party/freetype2/src/type42/t42drivr.h -FILE: ../../../third_party/freetype2/src/type42/t42error.h -FILE: ../../../third_party/freetype2/src/type42/t42objs.c -FILE: ../../../third_party/freetype2/src/type42/t42objs.h -FILE: ../../../third_party/freetype2/src/type42/t42parse.c -FILE: ../../../third_party/freetype2/src/type42/t42parse.h -FILE: ../../../third_party/freetype2/src/type42/t42types.h -FILE: ../../../third_party/freetype2/src/type42/type42.c -FILE: ../../../third_party/freetype2/src/winfonts/Jamfile -FILE: ../../../third_party/freetype2/src/winfonts/fnterrs.h -FILE: ../../../third_party/freetype2/src/winfonts/winfnt.c -FILE: ../../../third_party/freetype2/src/winfonts/winfnt.h -FILE: ../../../third_party/freetype2/version.sed ----------------------------------------------------------------------------------------------------- -The FreeType Project LICENSE - - 2006-Jan-27 - -Copyright 1996-2002, 2006 by -David Turner, Robert Wilhelm, and Werner Lemberg - -Introduction -============ - - The FreeType Project is distributed in several archive packages; - some of them may contain, in addition to the FreeType font engine, - various tools and contributions which rely on, or relate to, the - FreeType Project. - - This license applies to all files found in such packages, and - which do not fall under their own explicit license. The license - affects thus the FreeType font engine, the test programs, - documentation and makefiles, at the very least. - - This license was inspired by the BSD, Artistic, and IJG - (Independent JPEG Group) licenses, which all encourage inclusion - and use of free software in commercial and freeware products - alike. As a consequence, its main points are that: - - o We don't promise that this software works. However, we will be - interested in any kind of bug reports. (`as is' distribution) - - o You can use this software for whatever you want, in parts or - full form, without having to pay us. (`royalty-free' usage) - - o You may not pretend that you wrote this software. If you use - it, or only parts of it, in a program, you must acknowledge - somewhere in your documentation that you have used the - FreeType code. (`credits') - - We specifically permit and encourage the inclusion of this - software, with or without modifications, in commercial products. - We disclaim all warranties covering The FreeType Project and - assume no liability related to The FreeType Project. - - Finally, many people asked us for a preferred form for a - credit/disclaimer to use in compliance with this license. We thus - encourage you to use the following text: - - Portions of this software are copyright © The FreeType - Project (www.freetype.org). All rights reserved. - - Please replace with the value from the FreeType version you - actually use. - -Legal Terms -=========== - -0. Definitions - - Throughout this license, the terms `package', `FreeType Project', - and `FreeType archive' refer to the set of files originally - distributed by the authors (David Turner, Robert Wilhelm, and - Werner Lemberg) as the `FreeType Project', be they named as alpha, - beta or final release. - - `You' refers to the licensee, or person using the project, where - `using' is a generic term including compiling the project's source - code as well as linking it to form a `program' or `executable'. - This program is referred to as `a program using the FreeType - engine'. - - This license applies to all files distributed in the original - FreeType Project, including all source code, binaries and - documentation, unless otherwise stated in the file in its - original, unmodified form as distributed in the original archive. - If you are unsure whether or not a particular file is covered by - this license, you must contact us to verify this. - - The FreeType Project is copyright (C) 1996-2000 by David Turner, - Robert Wilhelm, and Werner Lemberg. All rights reserved except as - specified below. - -1. No Warranty - - THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO - USE, OF THE FREETYPE PROJECT. - -2. Redistribution - - This license grants a worldwide, royalty-free, perpetual and - irrevocable right and license to use, execute, perform, compile, - display, copy, create derivative works of, distribute and - sublicense the FreeType Project (in both source and object code - forms) and derivative works thereof for any purpose; and to - authorize others to exercise some or all of the rights granted - herein, subject to the following conditions: - - o Redistribution of source code must retain this license file - (`FTL.TXT') unaltered; any additions, deletions or changes to - the original files must be clearly indicated in accompanying - documentation. The copyright notices of the unaltered, - original files must be preserved in all copies of source - files. - - o Redistribution in binary form must provide a disclaimer that - states that the software is based in part of the work of the - FreeType Team, in the distribution documentation. We also - encourage you to put an URL to the FreeType web page in your - documentation, though this isn't mandatory. - - These conditions apply to any software derived from or based on - the FreeType Project, not just the unmodified files. If you use - our work, you must acknowledge us. However, no fee need be paid - to us. - -3. Advertising - - Neither the FreeType authors and contributors nor you shall use - the name of the other for commercial, advertising, or promotional - purposes without specific prior written permission. - - We suggest, but do not require, that you use one or more of the - following phrases to refer to this software in your documentation - or advertising materials: `FreeType Project', `FreeType Engine', - `FreeType library', or `FreeType Distribution'. - - As you have not signed this license, you are not required to - accept it. However, as the FreeType Project is copyrighted - material, only this license, or another one contracted with the - authors, grants you the right to use, distribute, and modify it. - Therefore, by using, distributing, or modifying the FreeType - Project, you indicate that you understand and accept all the terms - of this license. - -4. Contacts - - There are two mailing lists related to FreeType: - - o freetype@nongnu.org - - Discusses general use and applications of FreeType, as well as - future and wanted additions to the library and distribution. - If you are looking for support, start in this list if you - haven't found anything to help you in the documentation. - - o freetype-devel@nongnu.org - - Discusses bugs, as well as engine internals, design issues, - specific licenses, porting, etc. - - Our home page can be found at - - https://www.freetype.org - ---- end of FTL.TXT --- -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/include/freetype/internal/fthash.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/include/freetype/internal/fthash.h -FILE: ../../../third_party/freetype2/src/base/fthash.c ----------------------------------------------------------------------------------------------------- -Copyright 2000 Computing Research Labs, New Mexico State University -Copyright 2001-2015 - Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/bdf/bdf.c -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/bdf/bdf.c ----------------------------------------------------------------------------------------------------- -Copyright (C) 2001, 2002 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/bdf/bdf.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/bdf/bdf.h ----------------------------------------------------------------------------------------------------- -Copyright 2000 Computing Research Labs, New Mexico State University -Copyright 2001-2004, 2011 Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/bdf/bdfdrivr.c -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/bdf/bdfdrivr.c ----------------------------------------------------------------------------------------------------- -Copyright (C) 2001-2008, 2011, 2013, 2014 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/bdf/bdfdrivr.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/bdf/bdfdrivr.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 2001, 2002, 2003, 2004 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/bdf/bdferror.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/bdf/bdferror.h ----------------------------------------------------------------------------------------------------- -Copyright 2001, 2002, 2012 Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/bdf/bdflib.c -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/bdf/bdflib.c ----------------------------------------------------------------------------------------------------- -Copyright 2000 Computing Research Labs, New Mexico State University -Copyright 2001-2014 - Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/gzip/adler32.c + ../../../third_party/freetype2/src/gzip/zlib.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/freetype2/src/gzip/adler32.c -FILE: ../../../third_party/freetype2/src/gzip/infblock.c -FILE: ../../../third_party/freetype2/src/gzip/infblock.h -FILE: ../../../third_party/freetype2/src/gzip/infcodes.c -FILE: ../../../third_party/freetype2/src/gzip/infcodes.h -FILE: ../../../third_party/freetype2/src/gzip/inflate.c -FILE: ../../../third_party/freetype2/src/gzip/inftrees.c -FILE: ../../../third_party/freetype2/src/gzip/inftrees.h -FILE: ../../../third_party/freetype2/src/gzip/infutil.c -FILE: ../../../third_party/freetype2/src/gzip/infutil.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 1995-2002 Mark Adler - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/gzip/ftzconf.h + ../../../third_party/freetype2/src/gzip/zlib.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/freetype2/src/gzip/ftzconf.h -FILE: ../../../third_party/freetype2/src/gzip/zutil.c -FILE: ../../../third_party/freetype2/src/gzip/zutil.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 1995-2002 Jean-loup Gailly. - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/gzip/zlib.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/freetype2/src/gzip/inffixed.h -FILE: ../../../third_party/freetype2/src/gzip/zlib.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcf.c -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcf.c ----------------------------------------------------------------------------------------------------- -Copyright 2000-2001, 2003 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcf.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcf.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 2000, 2001, 2002, 2003, 2006, 2010 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcfdrivr.c -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcfdrivr.c ----------------------------------------------------------------------------------------------------- -Copyright (C) 2000-2004, 2006-2011, 2013, 2014 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcfdrivr.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcfdrivr.h ----------------------------------------------------------------------------------------------------- -Copyright 2000-2001, 2002 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcfread.c -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcfread.c ----------------------------------------------------------------------------------------------------- -Copyright 2000-2010, 2012-2014 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcfread.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcfread.h ----------------------------------------------------------------------------------------------------- -Copyright 2003 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcfutil.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/freetype2/src/pcf/pcfutil.c ----------------------------------------------------------------------------------------------------- -Copyright 1990, 1994, 1998 The Open Group - -Permission to use, copy, modify, distribute, and sell this software and its -documentation for any purpose is hereby granted without fee, provided that -the above copyright notice appear in all copies and that both that -copyright notice and this permission notice appear in supporting -documentation. - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of The Open Group shall not be -used in advertising or otherwise to promote the sale, use or other dealings -in this Software without prior written authorization from The Open Group. -==================================================================================================== - -==================================================================================================== -LIBRARY: freetype2 -ORIGIN: ../../../third_party/freetype2/src/pcf/pcfutil.h -TYPE: LicenseType.mit -FILE: ../../../third_party/freetype2/src/pcf/pcfutil.h ----------------------------------------------------------------------------------------------------- -Copyright 2000, 2001, 2004 by -Francesco Zappa Nardelli - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/COPYING.txt -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/.appveyor.yml -FILE: ../../../third_party/glfw/CMake/MacOSXBundleInfo.plist.in -FILE: ../../../third_party/glfw/cmake_uninstall.cmake.in -FILE: ../../../third_party/glfw/docs/Doxyfile.in -FILE: ../../../third_party/glfw/docs/DoxygenLayout.xml -FILE: ../../../third_party/glfw/docs/build.dox -FILE: ../../../third_party/glfw/docs/compat.dox -FILE: ../../../third_party/glfw/docs/compile.dox -FILE: ../../../third_party/glfw/docs/context.dox -FILE: ../../../third_party/glfw/docs/extra.less -FILE: ../../../third_party/glfw/docs/footer.html -FILE: ../../../third_party/glfw/docs/header.html -FILE: ../../../third_party/glfw/docs/input.dox -FILE: ../../../third_party/glfw/docs/internal.dox -FILE: ../../../third_party/glfw/docs/intro.dox -FILE: ../../../third_party/glfw/docs/main.dox -FILE: ../../../third_party/glfw/docs/monitor.dox -FILE: ../../../third_party/glfw/docs/moving.dox -FILE: ../../../third_party/glfw/docs/news.dox -FILE: ../../../third_party/glfw/docs/quick.dox -FILE: ../../../third_party/glfw/docs/spaces.svg -FILE: ../../../third_party/glfw/docs/vulkan.dox -FILE: ../../../third_party/glfw/docs/window.dox -FILE: ../../../third_party/glfw/include/GLFW/glfw3.h -FILE: ../../../third_party/glfw/include/GLFW/glfw3native.h -FILE: ../../../third_party/glfw/src/cocoa_monitor.m -FILE: ../../../third_party/glfw/src/context.c -FILE: ../../../third_party/glfw/src/egl_context.c -FILE: ../../../third_party/glfw/src/egl_context.h -FILE: ../../../third_party/glfw/src/glfw3.pc.in -FILE: ../../../third_party/glfw/src/glfw3Config.cmake.in -FILE: ../../../third_party/glfw/src/glx_context.c -FILE: ../../../third_party/glfw/src/glx_context.h -FILE: ../../../third_party/glfw/src/init.c -FILE: ../../../third_party/glfw/src/input.c -FILE: ../../../third_party/glfw/src/internal.h -FILE: ../../../third_party/glfw/src/linux_joystick.c -FILE: ../../../third_party/glfw/src/monitor.c -FILE: ../../../third_party/glfw/src/posix_time.c -FILE: ../../../third_party/glfw/src/posix_time.h -FILE: ../../../third_party/glfw/src/posix_tls.c -FILE: ../../../third_party/glfw/src/posix_tls.h -FILE: ../../../third_party/glfw/src/vulkan.c -FILE: ../../../third_party/glfw/src/wgl_context.c -FILE: ../../../third_party/glfw/src/wgl_context.h -FILE: ../../../third_party/glfw/src/win32_init.c -FILE: ../../../third_party/glfw/src/win32_joystick.c -FILE: ../../../third_party/glfw/src/win32_monitor.c -FILE: ../../../third_party/glfw/src/win32_platform.h -FILE: ../../../third_party/glfw/src/win32_time.c -FILE: ../../../third_party/glfw/src/win32_tls.c -FILE: ../../../third_party/glfw/src/win32_window.c -FILE: ../../../third_party/glfw/src/x11_init.c -FILE: ../../../third_party/glfw/src/x11_monitor.c -FILE: ../../../third_party/glfw/src/x11_platform.h -FILE: ../../../third_party/glfw/src/x11_window.c -FILE: ../../../third_party/glfw/src/xkb_unicode.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2016 Camilla Berglund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_init.m -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/cocoa_init.m -FILE: ../../../third_party/glfw/src/cocoa_platform.h -FILE: ../../../third_party/glfw/src/cocoa_time.c -FILE: ../../../third_party/glfw/src/cocoa_window.m -FILE: ../../../third_party/glfw/src/nsgl_context.h -FILE: ../../../third_party/glfw/src/nsgl_context.m ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2016 Camilla Berglund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_joystick.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/cocoa_joystick.h -FILE: ../../../third_party/glfw/src/win32_joystick.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2006-2016 Camilla Berglund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/cocoa_joystick.m -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/cocoa_joystick.m ----------------------------------------------------------------------------------------------------- -Copyright (c) 2009-2016 Camilla Berglund -Copyright (c) 2012 Torsten Walluhn - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/glfw_config.h.in -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/glfw_config.h.in ----------------------------------------------------------------------------------------------------- -Copyright (c) 2010-2016 Camilla Berglund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/linux_joystick.h -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/linux_joystick.h -FILE: ../../../third_party/glfw/src/wl_init.c -FILE: ../../../third_party/glfw/src/wl_monitor.c -FILE: ../../../third_party/glfw/src/wl_platform.h -FILE: ../../../third_party/glfw/src/wl_window.c -FILE: ../../../third_party/glfw/src/xkb_unicode.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014 Jonas Ådahl - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/mir_init.c -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/mir_init.c -FILE: ../../../third_party/glfw/src/mir_monitor.c -FILE: ../../../third_party/glfw/src/mir_platform.h -FILE: ../../../third_party/glfw/src/mir_window.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014-2015 Brandon Schaefer - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: glfw -ORIGIN: ../../../third_party/glfw/src/window.c -TYPE: LicenseType.zlib -FILE: ../../../third_party/glfw/src/window.c ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2016 Camilla Berglund -Copyright (c) 2012 Torsten Walluhn - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/COPYING -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/.circleci/config.yml -FILE: ../../../third_party/harfbuzz/.codecov.yml -FILE: ../../../third_party/harfbuzz/.editorconfig -FILE: ../../../third_party/harfbuzz/THANKS -FILE: ../../../third_party/harfbuzz/TODO -FILE: ../../../third_party/harfbuzz/docs/HarfBuzz.png -FILE: ../../../third_party/harfbuzz/docs/HarfBuzz.svg -FILE: ../../../third_party/harfbuzz/docs/harfbuzz-docs.xml -FILE: ../../../third_party/harfbuzz/docs/meson.build -FILE: ../../../third_party/harfbuzz/docs/usermanual-buffers-language-script-and-direction.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-clusters.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-fonts-and-faces.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-getting-started.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-glyph-information.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-install-harfbuzz.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-integration.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-object-model.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-opentype-features.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-shaping-concepts.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-utilities.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-what-is-harfbuzz.xml -FILE: ../../../third_party/harfbuzz/docs/version.xml.in -FILE: ../../../third_party/harfbuzz/harfbuzz.doap -FILE: ../../../third_party/harfbuzz/meson-cc-tests/intel-atomic-primitives-test.c -FILE: ../../../third_party/harfbuzz/meson-cc-tests/solaris-atomic-operations.c -FILE: ../../../third_party/harfbuzz/meson.build -FILE: ../../../third_party/harfbuzz/perf/fonts/Amiri-Regular.ttf -FILE: ../../../third_party/harfbuzz/perf/fonts/NotoNastaliqUrdu-Regular.ttf -FILE: ../../../third_party/harfbuzz/perf/fonts/NotoSansDevanagari-Regular.ttf -FILE: ../../../third_party/harfbuzz/perf/fonts/Roboto-Regular.ttf -FILE: ../../../third_party/harfbuzz/perf/meson.build -FILE: ../../../third_party/harfbuzz/perf/perf-draw.hh -FILE: ../../../third_party/harfbuzz/perf/perf-extents.hh -FILE: ../../../third_party/harfbuzz/perf/perf-shaping.hh -FILE: ../../../third_party/harfbuzz/perf/perf.cc -FILE: ../../../third_party/harfbuzz/src/Makefile.sources -FILE: ../../../third_party/harfbuzz/src/harfbuzz-config.cmake.in -FILE: ../../../third_party/harfbuzz/src/harfbuzz-gobject.pc.in -FILE: ../../../third_party/harfbuzz/src/harfbuzz-icu.pc.in -FILE: ../../../third_party/harfbuzz/src/harfbuzz-subset.pc.in -FILE: ../../../third_party/harfbuzz/src/harfbuzz.cc -FILE: ../../../third_party/harfbuzz/src/harfbuzz.pc.in -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-joining-list.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-table.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use-table.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-tag-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ucd-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-unicode-emoji-table.hh -FILE: ../../../third_party/harfbuzz/src/meson.build -FILE: ../../../third_party/harfbuzz/src/update-unicode-tables.make -FILE: ../../../third_party/harfbuzz/subprojects/cairo.wrap -FILE: ../../../third_party/harfbuzz/subprojects/expat.wrap -FILE: ../../../third_party/harfbuzz/subprojects/fontconfig.wrap -FILE: ../../../third_party/harfbuzz/subprojects/freetype2.wrap -FILE: ../../../third_party/harfbuzz/subprojects/glib.wrap -FILE: ../../../third_party/harfbuzz/subprojects/google-benchmark.wrap -FILE: ../../../third_party/harfbuzz/subprojects/libffi.wrap -FILE: ../../../third_party/harfbuzz/subprojects/libpng.wrap -FILE: ../../../third_party/harfbuzz/subprojects/pixman.wrap -FILE: ../../../third_party/harfbuzz/subprojects/proxy-libintl.wrap -FILE: ../../../third_party/harfbuzz/subprojects/ttf-parser.wrap -FILE: ../../../third_party/harfbuzz/subprojects/zlib.wrap ----------------------------------------------------------------------------------------------------- -HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. -For parts of HarfBuzz that are licensed under different licenses see individual -files names COPYING in subdirectories where applicable. - -Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. -Copyright © 2018,2019,2020 Ebrahim Byagowi -Copyright © 2019,2020 Facebook, Inc. -Copyright © 2012 Mozilla Foundation -Copyright © 2011 Codethink Limited -Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) -Copyright © 2009 Keith Stribley -Copyright © 2009 Martin Hosken and SIL International -Copyright © 2007 Chris Wilson -Copyright © 2006 Behdad Esfahbod -Copyright © 2005 David Turner -Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. -Copyright © 1998-2004 David Turner and Werner Lemberg - -For full copyright notices consult the individual files in the package. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/dump-indic-data.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/dump-indic-data.cc -FILE: ../../../third_party/harfbuzz/src/dump-khmer-data.cc -FILE: ../../../third_party/harfbuzz/src/dump-myanmar-data.cc -FILE: ../../../third_party/harfbuzz/src/dump-use-data.cc -FILE: ../../../third_party/harfbuzz/src/hb-aat-map.hh -FILE: ../../../third_party/harfbuzz/src/hb-array.hh -FILE: ../../../third_party/harfbuzz/src/hb-map.cc -FILE: ../../../third_party/harfbuzz/src/hb-map.h -FILE: ../../../third_party/harfbuzz/src/hb-map.hh -FILE: ../../../third_party/harfbuzz/src/hb-meta.hh -FILE: ../../../third_party/harfbuzz/src/hb-null.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-deprecated.h -FILE: ../../../third_party/harfbuzz/src/hb-ot-face.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-hdmx-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-name-language-static.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-name-language.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-name.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-os2-unicode-ranges.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.hh -FILE: ../../../third_party/harfbuzz/src/hb-static.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset-input.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset-input.hh -FILE: ../../../third_party/harfbuzz/src/hb-subset-plan.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset-plan.hh -FILE: ../../../third_party/harfbuzz/src/hb-subset.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset.h -FILE: ../../../third_party/harfbuzz/src/hb-subset.hh -FILE: ../../../third_party/harfbuzz/src/test-iter.cc -FILE: ../../../third_party/harfbuzz/src/test-ot-name.cc -FILE: ../../../third_party/harfbuzz/src/test-unicode-ranges.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/failing-alloc.c -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/failing-alloc.c -FILE: ../../../third_party/harfbuzz/src/hb-draw.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2020 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-ankr-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-ankr-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-bsln-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-feat-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-just-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.h -FILE: ../../../third_party/harfbuzz/src/hb-aat-ltag-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat.h -FILE: ../../../third_party/harfbuzz/src/hb-ot-color-svg-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-gasp-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-metrics.h -FILE: ../../../third_party/harfbuzz/src/hb-ot-metrics.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-stat-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-common.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-common.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-morx-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.hh -FILE: ../../../third_party/harfbuzz/src/hb-debug.hh -FILE: ../../../third_party/harfbuzz/src/hb-kern.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-kern-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-post-macroman.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-var-avar-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-var-fvar-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-var-hvar-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-var-mvar-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-var.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-var.h -FILE: ../../../third_party/harfbuzz/src/hb-string-array.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2017 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-kerx-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-kerx-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-trak-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Ebrahim Byagowi -Copyright © 2018 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-opbd-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-opbd-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-gdi.cc -FILE: ../../../third_party/harfbuzz/src/hb-gdi.h -FILE: ../../../third_party/harfbuzz/src/hb-number-parser.hh -FILE: ../../../third_party/harfbuzz/src/hb-number-parser.rl -FILE: ../../../third_party/harfbuzz/src/hb-number.cc -FILE: ../../../third_party/harfbuzz/src/hb-number.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-meta-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-meta.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-meta.h -FILE: ../../../third_party/harfbuzz/src/hb-style.cc -FILE: ../../../third_party/harfbuzz/src/hb-style.h -FILE: ../../../third_party/harfbuzz/src/test-number.cc -FILE: ../../../third_party/harfbuzz/src/test-ot-meta.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2019 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2017 Google, Inc. -Copyright © 2018 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-map.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-aat-map.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-map.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009,2010 Red Hat, Inc. -Copyright © 2010,2011,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-algs.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-algs.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2017 Google, Inc. -Copyright © 2019 Facebook, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-atomic.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-atomic.hh -FILE: ../../../third_party/harfbuzz/src/hb-mutex.hh -FILE: ../../../third_party/harfbuzz/src/hb-object.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007 Chris Wilson -Copyright © 2009,2010 Red Hat, Inc. -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-bimap.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-bimap.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2019 Adobe Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-blob.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-blob.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2018 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-blob.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-blob.h -FILE: ../../../third_party/harfbuzz/src/hb-face.h -FILE: ../../../third_party/harfbuzz/src/hb-font.h -FILE: ../../../third_party/harfbuzz/src/hb-ot.h -FILE: ../../../third_party/harfbuzz/src/hb.h ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-blob.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-blob.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2018 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-json.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-json.hh -FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-json.rl -FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-text.hh -FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-text.rl -FILE: ../../../third_party/harfbuzz/src/hb-deprecated.h -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-jstf-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-hangul.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer-serialize.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-buffer-serialize.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-buffer.cc -FILE: ../../../third_party/harfbuzz/src/hb-buffer.hh ----------------------------------------------------------------------------------------------------- -Copyright © 1998-2004 David Turner and Werner Lemberg -Copyright © 2004,2007,2009,2010 Red Hat, Inc. -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-buffer.h ----------------------------------------------------------------------------------------------------- -Copyright © 1998-2004 David Turner and Werner Lemberg -Copyright © 2004,2007,2009 Red Hat, Inc. -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-cache.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-cache.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize.hh -FILE: ../../../third_party/harfbuzz/src/hb-set-digest.hh -FILE: ../../../third_party/harfbuzz/src/hb-set.cc -FILE: ../../../third_party/harfbuzz/src/hb-set.h -FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.cc -FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.h -FILE: ../../../third_party/harfbuzz/src/hb-shaper-impl.hh -FILE: ../../../third_party/harfbuzz/src/hb-shaper-list.hh -FILE: ../../../third_party/harfbuzz/src/hb-shaper.cc -FILE: ../../../third_party/harfbuzz/src/hb-shaper.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-cff-interp-common.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-cff-interp-common.hh -FILE: ../../../third_party/harfbuzz/src/hb-cff-interp-cs-common.hh -FILE: ../../../third_party/harfbuzz/src/hb-cff-interp-dict-common.hh -FILE: ../../../third_party/harfbuzz/src/hb-cff1-interp-cs.hh -FILE: ../../../third_party/harfbuzz/src/hb-cff2-interp-cs.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-cff-common.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-cff1-table.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-cff1-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-cff2-table.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-cff2-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-vorg-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-subset-cff-common.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset-cff-common.hh -FILE: ../../../third_party/harfbuzz/src/hb-subset-cff1.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset-cff1.hh -FILE: ../../../third_party/harfbuzz/src/hb-subset-cff2.cc -FILE: ../../../third_party/harfbuzz/src/hb-subset-cff2.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Adobe Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-common.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-common.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009,2010 Red Hat, Inc. -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-common.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-common.h -FILE: ../../../third_party/harfbuzz/src/hb.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-config.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-config.hh -FILE: ../../../third_party/harfbuzz/src/hb-pool.hh -FILE: ../../../third_party/harfbuzz/src/test-algs.cc -FILE: ../../../third_party/harfbuzz/src/test-meta.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2019 Facebook, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-coretext.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-coretext.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2012,2013 Mozilla Foundation. -Copyright © 2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-coretext.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-coretext.h ----------------------------------------------------------------------------------------------------- -Copyright © 2012 Mozilla Foundation. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-directwrite.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-directwrite.cc -FILE: ../../../third_party/harfbuzz/src/hb-directwrite.h ----------------------------------------------------------------------------------------------------- -Copyright © 2015-2019 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-dispatch.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-dispatch.hh -FILE: ../../../third_party/harfbuzz/src/hb-machinery.hh -FILE: ../../../third_party/harfbuzz/src/hb-sanitize.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009,2010 Red Hat, Inc. -Copyright © 2012,2018 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-draw.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-draw.cc -FILE: ../../../third_party/harfbuzz/src/hb-draw.h ----------------------------------------------------------------------------------------------------- -Copyright © 2019-2020 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-face.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-face.cc -FILE: ../../../third_party/harfbuzz/src/hb-font.cc -FILE: ../../../third_party/harfbuzz/src/hb-shape.cc -FILE: ../../../third_party/harfbuzz/src/hb-shape.h ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-face.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-face.hh -FILE: ../../../third_party/harfbuzz/src/hb-font.hh -FILE: ../../../third_party/harfbuzz/src/hb-glib.cc -FILE: ../../../third_party/harfbuzz/src/hb-glib.h -FILE: ../../../third_party/harfbuzz/src/hb-icu.h -FILE: ../../../third_party/harfbuzz/src/hb-ot-tag.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2011 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-fallback-shape.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-fallback-shape.cc -FILE: ../../../third_party/harfbuzz/src/hb-gobject-structs.cc -FILE: ../../../third_party/harfbuzz/src/hb-uniscribe.h -FILE: ../../../third_party/harfbuzz/src/hb-version.h -FILE: ../../../third_party/harfbuzz/src/hb-version.h.in ----------------------------------------------------------------------------------------------------- -Copyright © 2011 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ft.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ft.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2009 Keith Stribley -Copyright © 2015 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ft.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ft.h ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2015 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-gobject-enums.cc.tmpl -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-gobject-enums.cc.tmpl -FILE: ../../../third_party/harfbuzz/src/hb-gobject-structs.h -FILE: ../../../third_party/harfbuzz/src/hb-gobject.h ----------------------------------------------------------------------------------------------------- -Copyright (C) 2011 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-gobject-enums.h.tmpl -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-gobject-enums.h.tmpl ----------------------------------------------------------------------------------------------------- -Copyright (C) 2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-graphite2.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-graphite2.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2011 Martin Hosken -Copyright © 2011 SIL International -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-graphite2.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-graphite2.h ----------------------------------------------------------------------------------------------------- -Copyright © 2011 Martin Hosken -Copyright © 2011 SIL International - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-icu.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-icu.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2009 Keith Stribley -Copyright © 2011 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-iter.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-iter.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Google, Inc. -Copyright © 2019 Facebook, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-open-file.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-open-file.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-open-type.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-open-type.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009,2010 Red Hat, Inc. -Copyright © 2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-cff1-std-str.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-cff1-std-str.hh -FILE: ../../../third_party/harfbuzz/src/test-bimap.cc -FILE: ../../../third_party/harfbuzz/src/test-ot-glyphname.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2019 Adobe, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-cmap-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-cmap-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-font.h -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2014 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color-cbdt-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-color-cbdt-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-post-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2016 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color-colr-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-color-colr-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-color-sbix-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Ebrahim Byagowi -Copyright © 2020 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color-cpal-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-color-cpal-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-color.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2016 Google, Inc. -Copyright © 2018 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-color.h ----------------------------------------------------------------------------------------------------- -Copyright © 2016 Google, Inc. -Copyright © 2018 Khaled Hosny -Copyright © 2018 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-face-table-list.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-face-table-list.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2012,2013 Google, Inc. -Copyright © 2019, Facebook Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-face.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-face.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-font.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-font.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2011,2014 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-glyf-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-glyf-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2015 Google, Inc. -Copyright © 2019 Adobe Inc. -Copyright © 2019 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-head-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-head-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2010 Red Hat, Inc. -Copyright © 2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-hhea-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-hhea-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-hmtx-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-maxp-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-name-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer-machine.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer-machine.rl -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-base-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-base-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2016 Elie Roux -Copyright © 2018 Google, Inc. -Copyright © 2018-2019 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-common.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-common.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2010,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gdef-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gdef-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2010,2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gpos-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gpos-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gsub-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009,2010 Red Hat, Inc. -Copyright © 2010,2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009,2010 Red Hat, Inc. -Copyright © 2010,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout.cc ----------------------------------------------------------------------------------------------------- -Copyright © 1998-2004 David Turner and Werner Lemberg -Copyright © 2006 Behdad Esfahbod -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout.h ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-map.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-map.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2009,2010 Red Hat, Inc. -Copyright © 2010,2011,2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-math-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-math-table.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-math.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-math.h ----------------------------------------------------------------------------------------------------- -Copyright © 2016 Igalia S.L. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-metrics.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-metrics.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2018-2019 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-name.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-name.h ----------------------------------------------------------------------------------------------------- -Copyright © 2018 Ebrahim Byagowi. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-os2-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-os2-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2011,2012 Google, Inc. -Copyright © 2018 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-default.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-hebrew.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-thai.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2010,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use-machine.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use-machine.rl -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use.cc -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2015 Mozilla Foundation. -Copyright © 2015 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar.cc -FILE: ../../../third_party/harfbuzz/src/hb-uniscribe.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2011,2012,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2010,2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2009,2010 Red Hat, Inc. -Copyright © 2010,2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.h ----------------------------------------------------------------------------------------------------- -Copyright © 2013 Red Hat, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2010 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-var-gvar-table.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-var-gvar-table.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2019 Adobe Inc. -Copyright © 2019 Ebrahim Byagowi - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-serialize.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-serialize.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009,2010 Red Hat, Inc. -Copyright © 2012,2018 Google, Inc. -Copyright © 2019 Facebook, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-set.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-set.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2012,2017 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-shape-plan.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2012,2018 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ucd.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ucd.cc ----------------------------------------------------------------------------------------------------- -Copyright (C) 2012 Grigori Goronzy - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-unicode.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-unicode.cc -FILE: ../../../third_party/harfbuzz/src/hb-unicode.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2011 Codethink Limited -Copyright © 2010,2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-unicode.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-unicode.h ----------------------------------------------------------------------------------------------------- -Copyright © 2009 Red Hat, Inc. -Copyright © 2011 Codethink Limited -Copyright © 2011,2012 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-utf.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-utf.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2011,2012,2014 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-vector.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-vector.hh ----------------------------------------------------------------------------------------------------- -Copyright © 2017,2018 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/main.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/main.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2007,2008,2009 Red Hat, Inc. -Copyright © 2018,2019,2020 Ebrahim Byagowi -Copyright © 2018 Khaled Hosny - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/test-array.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/test-array.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2020 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2010,2011,2013 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/test-gpos-size-params.cc -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/test-gpos-size-params.cc -FILE: ../../../third_party/harfbuzz/src/test-gsub-would-substitute.cc -FILE: ../../../third_party/harfbuzz/src/test.cc ----------------------------------------------------------------------------------------------------- -Copyright © 2010,2011 Google, Inc. - - This is part of HarfBuzz, a text shaping library. - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, copy, modify, and distribute this -software and its documentation for any purpose, provided that the -above copyright notice and the following two paragraphs appear in -all copies of this software. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN -IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO -PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright 2006-2011, the V8 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: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999 Computer Systems and Communication Lab, - Institute of Information Science, Academia - * Sinica. 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 the Computer Systems and Communication Lab - 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 -REGENTS 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: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999 TaBE Project. -Copyright (c) 1999 Pai-Hsiang Hsiao. -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 the TaBE Project 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 -REGENTS 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: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013 International Business Machines Corporation -and others. All Rights Reserved. - -Project: http://code.google.com/p/lao-dictionary -Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt -License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt - (copied below) - - This file is derived from the above dictionary, with slight - modifications. - - Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. - 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. - -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 HOLDER 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: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014 International Business Machines Corporation -and others. All Rights Reserved. - -This list is part of a project hosted at: - github.com/kanyawtech/myanmar-karen-word-lists - -Copyright (c) 2013, LeRoy Benjamin Sharon -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 Myanmar Karen Word Lists, 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 HOLDER 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: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -The BSD License -http://opensource.org/licenses/bsd-license.php -Copyright (C) 2006-2008, 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: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.icu -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright (c) 1995-2016 International Business Machines Corporation and others -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, provided that the above -copyright notice(s) and this permission notice appear in all copies of -the Software and that both the above copyright notice(s) and this -permission notice appear in supporting documentation. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY -SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER -RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF -CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, use -or other dealings in this Software without prior written authorization -of the copyright holder. - -All trademarks and registered trademarks mentioned herein are the -property of their respective owners. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright 1996 Chih-Hao Tsai @ Beckman Institute, - University of Illinois -c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright 2000, 2001, 2002, 2003 Nara Institute of Science -and Technology. All Rights Reserved. - -Use, reproduction, and distribution of this software is permitted. -Any copy of this software, whether in its original form or modified, -must include both the above copyright notice and the following -paragraphs. - -Nara Institute of Science and Technology (NAIST), -the copyright holders, disclaims all warranties with regard to this -software, including all implied warranties of merchantability and -fitness, in no event shall NAIST be liable for -any special, indirect or consequential damages or any damages -whatsoever resulting from loss of use, data or profits, whether in an -action of contract, negligence or other tortuous action, arising out -of or in connection with the use or performance of this software. - -A large portion of the dictionary entries -originate from ICOT Free Software. The following conditions for ICOT -Free Software applies to the current dictionary as well. - -Each User may also freely distribute the Program, whether in its -original form or modified, to any third party or parties, PROVIDED -that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear -on, or be attached to, the Program, which is distributed substantially -in the same form as set out herein and that such intended -distribution, if actually made, will neither violate or otherwise -contravene any of the laws and regulations of the countries having -jurisdiction over the User or the intended distribution itself. - -NO WARRANTY - -The program was produced on an experimental basis in the course of the -research and development conducted during the project and is provided -to users as so produced on an experimental basis. Accordingly, the -program is provided without any warranty whatsoever, whether express, -implied, statutory or otherwise. The term "warranty" used herein -includes, but is not limited to, any warranty of the quality, -performance, merchantability and fitness for a particular purpose of -the program and the nonexistence of any infringement or violation of -any right of any third party. - -Each user of the program will agree and understand, and be deemed to -have agreed and understood, that there is no warranty whatsoever for -the program and, accordingly, the entire risk arising from or -otherwise connected with the program is assumed by the user. - -Therefore, neither ICOT, the copyright holder, or any other -organization that participated in or was otherwise related to the -development of the program and their respective officials, directors, -officers and other employees shall be held liable for any and all -damages, including, without limitation, general, special, incidental -and consequential damages, arising out of or otherwise in connection -with the use or inability to use the program or any product, material -or result produced or otherwise obtained by using the program, -regardless of whether they have been advised of, or otherwise had -knowledge of, the possibility of such damages at any time during the -project or thereafter. Each user will be deemed to have agreed to the -foregoing by his or her commencement of use of the program. The term -"use" as used herein includes, but is not limited to, the use, -modification, copying and distribution of the program and the -production of secondary products from the program. - -In the case where the program, whether in its original form or -modified, was distributed or delivered to or received by a user from -any person, organization or entity other than ICOT, unless it makes or -grants independently of ICOT any specific warranty to the user in -writing, such person, organization or entity, will also be exempted -from and not be held liable to the user for any such damages as noted -above as far as the program is concerned. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../third_party/icu/android/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl.dat -FILE: ../../../third_party/icu/android_small/icudtl_extra.dat -FILE: ../../../third_party/icu/cast/brkitr.patch -FILE: ../../../third_party/icu/cast/icudtl.dat -FILE: ../../../third_party/icu/chromeos/icudtl.dat -FILE: ../../../third_party/icu/common/icudtb.dat -FILE: ../../../third_party/icu/common/icudtl.dat -FILE: ../../../third_party/icu/flutter/brkitr.patch -FILE: ../../../third_party/icu/flutter/icudtl.dat -FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h -FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc -FILE: ../../../third_party/icu/ios/icudtl.dat -FILE: ../../../third_party/icu/patches/cjdict.patch -FILE: ../../../third_party/icu/patches/configure.patch -FILE: ../../../third_party/icu/patches/data_symb.patch -FILE: ../../../third_party/icu/patches/formatted_string_builder.patch -FILE: ../../../third_party/icu/patches/gb_table.patch -FILE: ../../../third_party/icu/patches/grouping_digits.patch -FILE: ../../../third_party/icu/patches/icupkg.patch -FILE: ../../../third_party/icu/patches/iso2022jp.patch -FILE: ../../../third_party/icu/patches/isvalidenum.patch -FILE: ../../../third_party/icu/patches/khmer-dictbe.patch -FILE: ../../../third_party/icu/patches/locale1.patch -FILE: ../../../third_party/icu/patches/localeAddLikely.patch -FILE: ../../../third_party/icu/patches/locale_google.patch -FILE: ../../../third_party/icu/patches/restrace.patch -FILE: ../../../third_party/icu/patches/turnOffNewCodeInLocaleCanonical.patch -FILE: ../../../third_party/icu/patches/wordbrk.patch -FILE: ../../../third_party/icu/patches/wpo.patch -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu -FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu -FILE: ../../../third_party/icu/source/data/in/nfc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc.nrm -FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm -FILE: ../../../third_party/icu/source/data/in/pnames.icu -FILE: ../../../third_party/icu/source/data/in/ubidi.icu -FILE: ../../../third_party/icu/source/data/in/ucase.icu -FILE: ../../../third_party/icu/source/data/in/ulayout.icu -FILE: ../../../third_party/icu/source/data/in/unames.icu -FILE: ../../../third_party/icu/source/data/in/uprops.icu -FILE: ../../../third_party/icu/source/data/in/uts46.nrm -FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw -FILE: ../../../third_party/icu/source/samples/ucnv/data02.bin -FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c -FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c -FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c -FILE: ../../../third_party/icu/source/tools/tzcode/private.h -FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c -FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h -FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh -FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c -FILE: ../../../third_party/icu/source/tools/tzcode/zic.c -FILE: ../../../third_party/icu/tzres/metaZones.res -FILE: ../../../third_party/icu/tzres/timezoneTypes.res -FILE: ../../../third_party/icu/tzres/zoneinfo64.res ----------------------------------------------------------------------------------------------------- -Copyright © 1991-2020 Unicode, Inc. All rights reserved. -Distributed under the Terms of Use in https://www.unicode.org/copyright.html. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Unicode data files and any associated documentation -(the "Data Files") or Unicode software and any associated documentation -(the "Software") to deal in the Data Files or Software -without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, and/or sell copies of -the Data Files or Software, and to permit persons to whom the Data Files -or Software are furnished to do so, provided that either -(a) this copyright and permission notice appear with all copies -of the Data Files or Software, or -(b) this copyright and permission notice appear in associated -Documentation. - -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT OF THIRD PARTY RIGHTS. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS -NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL -DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THE DATA FILES OR SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in these Data Files or Software without prior -written authorization of the copyright holder. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/filters/android.json + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/filters/android.json -FILE: ../../../third_party/icu/filters/android_extra.json -FILE: ../../../third_party/icu/filters/android_small.json -FILE: ../../../third_party/icu/filters/cast.json -FILE: ../../../third_party/icu/filters/chromeos.json -FILE: ../../../third_party/icu/filters/common.json -FILE: ../../../third_party/icu/filters/flutter.json -FILE: ../../../third_party/icu/filters/ios.json ----------------------------------------------------------------------------------------------------- -Copyright 2019 the V8 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: icu -ORIGIN: ../../../third_party/icu/fuzzers/icu_converter_fuzzer.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/fuzzers/icu_converter_fuzzer.cc -FILE: ../../../third_party/icu/fuzzers/icu_regex.dict -FILE: ../../../third_party/icu/fuzzers/icu_uregex_open_fuzzer.cc ----------------------------------------------------------------------------------------------------- -Copyright 2016 The Chromium 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: icu -ORIGIN: ../../../third_party/icu/scripts/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/fuzzers/icu_unicode_string_codepage_create_fuzzer.cc -FILE: ../../../third_party/icu/scripts/accept_lang.list -FILE: ../../../third_party/icu/scripts/chrome_ui_languages.list -FILE: ../../../third_party/icu/scripts/currencies.list ----------------------------------------------------------------------------------------------------- -Copyright 2015 The Chromium 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: icu -ORIGIN: ../../../third_party/icu/source/common/unicode/appendable.h -TYPE: LicenseType.icu -FILE: ../../../third_party/icu/APIChangeReport.html -FILE: ../../../third_party/icu/source/Doxyfile.in -FILE: ../../../third_party/icu/source/common/appendable.cpp -FILE: ../../../third_party/icu/source/common/bmpset.cpp -FILE: ../../../third_party/icu/source/common/bmpset.h -FILE: ../../../third_party/icu/source/common/brkeng.cpp -FILE: ../../../third_party/icu/source/common/brkeng.h -FILE: ../../../third_party/icu/source/common/brkiter.cpp -FILE: ../../../third_party/icu/source/common/bytesinkutil.cpp -FILE: ../../../third_party/icu/source/common/bytesinkutil.h -FILE: ../../../third_party/icu/source/common/bytestream.cpp -FILE: ../../../third_party/icu/source/common/bytestrie.cpp -FILE: ../../../third_party/icu/source/common/bytestriebuilder.cpp -FILE: ../../../third_party/icu/source/common/bytestrieiterator.cpp -FILE: ../../../third_party/icu/source/common/caniter.cpp -FILE: ../../../third_party/icu/source/common/capi_helper.h -FILE: ../../../third_party/icu/source/common/characterproperties.cpp -FILE: ../../../third_party/icu/source/common/chariter.cpp -FILE: ../../../third_party/icu/source/common/charstr.cpp -FILE: ../../../third_party/icu/source/common/charstr.h -FILE: ../../../third_party/icu/source/common/cmemory.cpp -FILE: ../../../third_party/icu/source/common/cmemory.h -FILE: ../../../third_party/icu/source/common/common.rc -FILE: ../../../third_party/icu/source/common/cpputils.h -FILE: ../../../third_party/icu/source/common/cstr.cpp -FILE: ../../../third_party/icu/source/common/cstr.h -FILE: ../../../third_party/icu/source/common/cstring.cpp -FILE: ../../../third_party/icu/source/common/cstring.h -FILE: ../../../third_party/icu/source/common/cwchar.cpp -FILE: ../../../third_party/icu/source/common/cwchar.h -FILE: ../../../third_party/icu/source/common/dictbe.cpp -FILE: ../../../third_party/icu/source/common/dictbe.h -FILE: ../../../third_party/icu/source/common/dictionarydata.cpp -FILE: ../../../third_party/icu/source/common/dictionarydata.h -FILE: ../../../third_party/icu/source/common/dtintrv.cpp -FILE: ../../../third_party/icu/source/common/edits.cpp -FILE: ../../../third_party/icu/source/common/errorcode.cpp -FILE: ../../../third_party/icu/source/common/filteredbrk.cpp -FILE: ../../../third_party/icu/source/common/filterednormalizer2.cpp -FILE: ../../../third_party/icu/source/common/hash.h -FILE: ../../../third_party/icu/source/common/icudataver.cpp -FILE: ../../../third_party/icu/source/common/icuplug.cpp -FILE: ../../../third_party/icu/source/common/icuplugimp.h -FILE: ../../../third_party/icu/source/common/loadednormalizer2impl.cpp -FILE: ../../../third_party/icu/source/common/localebuilder.cpp -FILE: ../../../third_party/icu/source/common/localematcher.cpp -FILE: ../../../third_party/icu/source/common/localeprioritylist.cpp -FILE: ../../../third_party/icu/source/common/localeprioritylist.h -FILE: ../../../third_party/icu/source/common/localsvc.h -FILE: ../../../third_party/icu/source/common/locavailable.cpp -FILE: ../../../third_party/icu/source/common/locbased.cpp -FILE: ../../../third_party/icu/source/common/locbased.h -FILE: ../../../third_party/icu/source/common/locdispnames.cpp -FILE: ../../../third_party/icu/source/common/locdistance.cpp -FILE: ../../../third_party/icu/source/common/locdistance.h -FILE: ../../../third_party/icu/source/common/locdspnm.cpp -FILE: ../../../third_party/icu/source/common/locid.cpp -FILE: ../../../third_party/icu/source/common/loclikely.cpp -FILE: ../../../third_party/icu/source/common/loclikelysubtags.cpp -FILE: ../../../third_party/icu/source/common/loclikelysubtags.h -FILE: ../../../third_party/icu/source/common/locmap.cpp -FILE: ../../../third_party/icu/source/common/locmap.h -FILE: ../../../third_party/icu/source/common/locresdata.cpp -FILE: ../../../third_party/icu/source/common/locutil.cpp -FILE: ../../../third_party/icu/source/common/locutil.h -FILE: ../../../third_party/icu/source/common/lsr.cpp -FILE: ../../../third_party/icu/source/common/lsr.h -FILE: ../../../third_party/icu/source/common/messageimpl.h -FILE: ../../../third_party/icu/source/common/messagepattern.cpp -FILE: ../../../third_party/icu/source/common/msvcres.h -FILE: ../../../third_party/icu/source/common/mutex.h -FILE: ../../../third_party/icu/source/common/norm2_nfc_data.h -FILE: ../../../third_party/icu/source/common/norm2allmodes.h -FILE: ../../../third_party/icu/source/common/normalizer2.cpp -FILE: ../../../third_party/icu/source/common/normalizer2impl.cpp -FILE: ../../../third_party/icu/source/common/normalizer2impl.h -FILE: ../../../third_party/icu/source/common/normlzr.cpp -FILE: ../../../third_party/icu/source/common/parsepos.cpp -FILE: ../../../third_party/icu/source/common/patternprops.cpp -FILE: ../../../third_party/icu/source/common/patternprops.h -FILE: ../../../third_party/icu/source/common/pluralmap.cpp -FILE: ../../../third_party/icu/source/common/pluralmap.h -FILE: ../../../third_party/icu/source/common/propname.cpp -FILE: ../../../third_party/icu/source/common/propname.h -FILE: ../../../third_party/icu/source/common/propname_data.h -FILE: ../../../third_party/icu/source/common/propsvec.cpp -FILE: ../../../third_party/icu/source/common/propsvec.h -FILE: ../../../third_party/icu/source/common/punycode.cpp -FILE: ../../../third_party/icu/source/common/punycode.h -FILE: ../../../third_party/icu/source/common/putil.cpp -FILE: ../../../third_party/icu/source/common/putilimp.h -FILE: ../../../third_party/icu/source/common/rbbi.cpp -FILE: ../../../third_party/icu/source/common/rbbi_cache.cpp -FILE: ../../../third_party/icu/source/common/rbbi_cache.h -FILE: ../../../third_party/icu/source/common/rbbidata.cpp -FILE: ../../../third_party/icu/source/common/rbbidata.h -FILE: ../../../third_party/icu/source/common/rbbinode.cpp -FILE: ../../../third_party/icu/source/common/rbbinode.h -FILE: ../../../third_party/icu/source/common/rbbirb.cpp -FILE: ../../../third_party/icu/source/common/rbbirb.h -FILE: ../../../third_party/icu/source/common/rbbirpt.h -FILE: ../../../third_party/icu/source/common/rbbiscan.cpp -FILE: ../../../third_party/icu/source/common/rbbiscan.h -FILE: ../../../third_party/icu/source/common/rbbisetb.cpp -FILE: ../../../third_party/icu/source/common/rbbisetb.h -FILE: ../../../third_party/icu/source/common/rbbistbl.cpp -FILE: ../../../third_party/icu/source/common/rbbitblb.cpp -FILE: ../../../third_party/icu/source/common/rbbitblb.h -FILE: ../../../third_party/icu/source/common/resbund.cpp -FILE: ../../../third_party/icu/source/common/resbund_cnv.cpp -FILE: ../../../third_party/icu/source/common/resource.cpp -FILE: ../../../third_party/icu/source/common/resource.h -FILE: ../../../third_party/icu/source/common/restrace.cpp -FILE: ../../../third_party/icu/source/common/restrace.h -FILE: ../../../third_party/icu/source/common/ruleiter.cpp -FILE: ../../../third_party/icu/source/common/ruleiter.h -FILE: ../../../third_party/icu/source/common/schriter.cpp -FILE: ../../../third_party/icu/source/common/serv.cpp -FILE: ../../../third_party/icu/source/common/serv.h -FILE: ../../../third_party/icu/source/common/servlk.cpp -FILE: ../../../third_party/icu/source/common/servlkf.cpp -FILE: ../../../third_party/icu/source/common/servloc.h -FILE: ../../../third_party/icu/source/common/servls.cpp -FILE: ../../../third_party/icu/source/common/servnotf.cpp -FILE: ../../../third_party/icu/source/common/servnotf.h -FILE: ../../../third_party/icu/source/common/servrbf.cpp -FILE: ../../../third_party/icu/source/common/servslkf.cpp -FILE: ../../../third_party/icu/source/common/sharedobject.cpp -FILE: ../../../third_party/icu/source/common/sharedobject.h -FILE: ../../../third_party/icu/source/common/simpleformatter.cpp -FILE: ../../../third_party/icu/source/common/sprpimpl.h -FILE: ../../../third_party/icu/source/common/static_unicode_sets.cpp -FILE: ../../../third_party/icu/source/common/static_unicode_sets.h -FILE: ../../../third_party/icu/source/common/stringpiece.cpp -FILE: ../../../third_party/icu/source/common/stringtriebuilder.cpp -FILE: ../../../third_party/icu/source/common/uarrsort.cpp -FILE: ../../../third_party/icu/source/common/uarrsort.h -FILE: ../../../third_party/icu/source/common/uassert.h -FILE: ../../../third_party/icu/source/common/ubidi.cpp -FILE: ../../../third_party/icu/source/common/ubidi_props.cpp -FILE: ../../../third_party/icu/source/common/ubidi_props.h -FILE: ../../../third_party/icu/source/common/ubidi_props_data.h -FILE: ../../../third_party/icu/source/common/ubidiimp.h -FILE: ../../../third_party/icu/source/common/ubidiln.cpp -FILE: ../../../third_party/icu/source/common/ubiditransform.cpp -FILE: ../../../third_party/icu/source/common/ubidiwrt.cpp -FILE: ../../../third_party/icu/source/common/ubrk.cpp -FILE: ../../../third_party/icu/source/common/ubrkimpl.h -FILE: ../../../third_party/icu/source/common/ucase.cpp -FILE: ../../../third_party/icu/source/common/ucase.h -FILE: ../../../third_party/icu/source/common/ucase_props_data.h -FILE: ../../../third_party/icu/source/common/ucasemap.cpp -FILE: ../../../third_party/icu/source/common/ucasemap_imp.h -FILE: ../../../third_party/icu/source/common/ucasemap_titlecase_brkiter.cpp -FILE: ../../../third_party/icu/source/common/ucat.cpp -FILE: ../../../third_party/icu/source/common/uchar.cpp -FILE: ../../../third_party/icu/source/common/uchar_props_data.h -FILE: ../../../third_party/icu/source/common/ucharstrie.cpp -FILE: ../../../third_party/icu/source/common/ucharstriebuilder.cpp -FILE: ../../../third_party/icu/source/common/ucharstrieiterator.cpp -FILE: ../../../third_party/icu/source/common/uchriter.cpp -FILE: ../../../third_party/icu/source/common/ucln.h -FILE: ../../../third_party/icu/source/common/ucln_cmn.cpp -FILE: ../../../third_party/icu/source/common/ucln_cmn.h -FILE: ../../../third_party/icu/source/common/ucln_imp.h -FILE: ../../../third_party/icu/source/common/ucmndata.cpp -FILE: ../../../third_party/icu/source/common/ucmndata.h -FILE: ../../../third_party/icu/source/common/ucnv.cpp -FILE: ../../../third_party/icu/source/common/ucnv2022.cpp -FILE: ../../../third_party/icu/source/common/ucnv_bld.cpp -FILE: ../../../third_party/icu/source/common/ucnv_bld.h -FILE: ../../../third_party/icu/source/common/ucnv_cb.cpp -FILE: ../../../third_party/icu/source/common/ucnv_cnv.cpp -FILE: ../../../third_party/icu/source/common/ucnv_cnv.h -FILE: ../../../third_party/icu/source/common/ucnv_ct.cpp -FILE: ../../../third_party/icu/source/common/ucnv_err.cpp -FILE: ../../../third_party/icu/source/common/ucnv_ext.cpp -FILE: ../../../third_party/icu/source/common/ucnv_ext.h -FILE: ../../../third_party/icu/source/common/ucnv_imp.h -FILE: ../../../third_party/icu/source/common/ucnv_io.cpp -FILE: ../../../third_party/icu/source/common/ucnv_io.h -FILE: ../../../third_party/icu/source/common/ucnv_lmb.cpp -FILE: ../../../third_party/icu/source/common/ucnv_set.cpp -FILE: ../../../third_party/icu/source/common/ucnv_u16.cpp -FILE: ../../../third_party/icu/source/common/ucnv_u32.cpp -FILE: ../../../third_party/icu/source/common/ucnv_u7.cpp -FILE: ../../../third_party/icu/source/common/ucnv_u8.cpp -FILE: ../../../third_party/icu/source/common/ucnvbocu.cpp -FILE: ../../../third_party/icu/source/common/ucnvdisp.cpp -FILE: ../../../third_party/icu/source/common/ucnvhz.cpp -FILE: ../../../third_party/icu/source/common/ucnvisci.cpp -FILE: ../../../third_party/icu/source/common/ucnvlat1.cpp -FILE: ../../../third_party/icu/source/common/ucnvmbcs.cpp -FILE: ../../../third_party/icu/source/common/ucnvmbcs.h -FILE: ../../../third_party/icu/source/common/ucnvscsu.cpp -FILE: ../../../third_party/icu/source/common/ucnvsel.cpp -FILE: ../../../third_party/icu/source/common/ucol_data.h -FILE: ../../../third_party/icu/source/common/ucol_swp.cpp -FILE: ../../../third_party/icu/source/common/ucol_swp.h -FILE: ../../../third_party/icu/source/common/ucptrie.cpp -FILE: ../../../third_party/icu/source/common/ucptrie_impl.h -FILE: ../../../third_party/icu/source/common/ucurr.cpp -FILE: ../../../third_party/icu/source/common/ucurrimp.h -FILE: ../../../third_party/icu/source/common/udata.cpp -FILE: ../../../third_party/icu/source/common/udatamem.cpp -FILE: ../../../third_party/icu/source/common/udatamem.h -FILE: ../../../third_party/icu/source/common/udataswp.cpp -FILE: ../../../third_party/icu/source/common/udataswp.h -FILE: ../../../third_party/icu/source/common/uelement.h -FILE: ../../../third_party/icu/source/common/uenum.cpp -FILE: ../../../third_party/icu/source/common/uenumimp.h -FILE: ../../../third_party/icu/source/common/uhash.cpp -FILE: ../../../third_party/icu/source/common/uhash.h -FILE: ../../../third_party/icu/source/common/uhash_us.cpp -FILE: ../../../third_party/icu/source/common/uidna.cpp -FILE: ../../../third_party/icu/source/common/uinit.cpp -FILE: ../../../third_party/icu/source/common/uinvchar.cpp -FILE: ../../../third_party/icu/source/common/uinvchar.h -FILE: ../../../third_party/icu/source/common/uiter.cpp -FILE: ../../../third_party/icu/source/common/ulayout_props.h -FILE: ../../../third_party/icu/source/common/ulist.cpp -FILE: ../../../third_party/icu/source/common/ulist.h -FILE: ../../../third_party/icu/source/common/uloc.cpp -FILE: ../../../third_party/icu/source/common/uloc_keytype.cpp -FILE: ../../../third_party/icu/source/common/uloc_tag.cpp -FILE: ../../../third_party/icu/source/common/ulocimp.h -FILE: ../../../third_party/icu/source/common/umapfile.cpp -FILE: ../../../third_party/icu/source/common/umapfile.h -FILE: ../../../third_party/icu/source/common/umath.cpp -FILE: ../../../third_party/icu/source/common/umutablecptrie.cpp -FILE: ../../../third_party/icu/source/common/umutex.cpp -FILE: ../../../third_party/icu/source/common/umutex.h -FILE: ../../../third_party/icu/source/common/unames.cpp -FILE: ../../../third_party/icu/source/common/unicode/appendable.h -FILE: ../../../third_party/icu/source/common/unicode/brkiter.h -FILE: ../../../third_party/icu/source/common/unicode/bytestream.h -FILE: ../../../third_party/icu/source/common/unicode/bytestrie.h -FILE: ../../../third_party/icu/source/common/unicode/bytestriebuilder.h -FILE: ../../../third_party/icu/source/common/unicode/caniter.h -FILE: ../../../third_party/icu/source/common/unicode/casemap.h -FILE: ../../../third_party/icu/source/common/unicode/char16ptr.h -FILE: ../../../third_party/icu/source/common/unicode/chariter.h -FILE: ../../../third_party/icu/source/common/unicode/dbbi.h -FILE: ../../../third_party/icu/source/common/unicode/docmain.h -FILE: ../../../third_party/icu/source/common/unicode/dtintrv.h -FILE: ../../../third_party/icu/source/common/unicode/edits.h -FILE: ../../../third_party/icu/source/common/unicode/enumset.h -FILE: ../../../third_party/icu/source/common/unicode/errorcode.h -FILE: ../../../third_party/icu/source/common/unicode/filteredbrk.h -FILE: ../../../third_party/icu/source/common/unicode/icudataver.h -FILE: ../../../third_party/icu/source/common/unicode/icuplug.h -FILE: ../../../third_party/icu/source/common/unicode/idna.h -FILE: ../../../third_party/icu/source/common/unicode/localebuilder.h -FILE: ../../../third_party/icu/source/common/unicode/localematcher.h -FILE: ../../../third_party/icu/source/common/unicode/localpointer.h -FILE: ../../../third_party/icu/source/common/unicode/locdspnm.h -FILE: ../../../third_party/icu/source/common/unicode/locid.h -FILE: ../../../third_party/icu/source/common/unicode/messagepattern.h -FILE: ../../../third_party/icu/source/common/unicode/normalizer2.h -FILE: ../../../third_party/icu/source/common/unicode/normlzr.h -FILE: ../../../third_party/icu/source/common/unicode/parseerr.h -FILE: ../../../third_party/icu/source/common/unicode/parsepos.h -FILE: ../../../third_party/icu/source/common/unicode/platform.h -FILE: ../../../third_party/icu/source/common/unicode/ptypes.h -FILE: ../../../third_party/icu/source/common/unicode/putil.h -FILE: ../../../third_party/icu/source/common/unicode/rbbi.h -FILE: ../../../third_party/icu/source/common/unicode/rep.h -FILE: ../../../third_party/icu/source/common/unicode/resbund.h -FILE: ../../../third_party/icu/source/common/unicode/schriter.h -FILE: ../../../third_party/icu/source/common/unicode/simpleformatter.h -FILE: ../../../third_party/icu/source/common/unicode/std_string.h -FILE: ../../../third_party/icu/source/common/unicode/strenum.h -FILE: ../../../third_party/icu/source/common/unicode/stringoptions.h -FILE: ../../../third_party/icu/source/common/unicode/stringpiece.h -FILE: ../../../third_party/icu/source/common/unicode/stringtriebuilder.h -FILE: ../../../third_party/icu/source/common/unicode/symtable.h -FILE: ../../../third_party/icu/source/common/unicode/ubidi.h -FILE: ../../../third_party/icu/source/common/unicode/ubiditransform.h -FILE: ../../../third_party/icu/source/common/unicode/ubrk.h -FILE: ../../../third_party/icu/source/common/unicode/ucasemap.h -FILE: ../../../third_party/icu/source/common/unicode/ucat.h -FILE: ../../../third_party/icu/source/common/unicode/uchar.h -FILE: ../../../third_party/icu/source/common/unicode/ucharstrie.h -FILE: ../../../third_party/icu/source/common/unicode/ucharstriebuilder.h -FILE: ../../../third_party/icu/source/common/unicode/uchriter.h -FILE: ../../../third_party/icu/source/common/unicode/uclean.h -FILE: ../../../third_party/icu/source/common/unicode/ucnv.h -FILE: ../../../third_party/icu/source/common/unicode/ucnv_cb.h -FILE: ../../../third_party/icu/source/common/unicode/ucnv_err.h -FILE: ../../../third_party/icu/source/common/unicode/ucnvsel.h -FILE: ../../../third_party/icu/source/common/unicode/uconfig.h -FILE: ../../../third_party/icu/source/common/unicode/ucpmap.h -FILE: ../../../third_party/icu/source/common/unicode/ucptrie.h -FILE: ../../../third_party/icu/source/common/unicode/ucurr.h -FILE: ../../../third_party/icu/source/common/unicode/udata.h -FILE: ../../../third_party/icu/source/common/unicode/udisplaycontext.h -FILE: ../../../third_party/icu/source/common/unicode/uenum.h -FILE: ../../../third_party/icu/source/common/unicode/uidna.h -FILE: ../../../third_party/icu/source/common/unicode/uiter.h -FILE: ../../../third_party/icu/source/common/unicode/uldnames.h -FILE: ../../../third_party/icu/source/common/unicode/uloc.h -FILE: ../../../third_party/icu/source/common/unicode/umachine.h -FILE: ../../../third_party/icu/source/common/unicode/umisc.h -FILE: ../../../third_party/icu/source/common/unicode/umutablecptrie.h -FILE: ../../../third_party/icu/source/common/unicode/unifilt.h -FILE: ../../../third_party/icu/source/common/unicode/unifunct.h -FILE: ../../../third_party/icu/source/common/unicode/unimatch.h -FILE: ../../../third_party/icu/source/common/unicode/uniset.h -FILE: ../../../third_party/icu/source/common/unicode/unistr.h -FILE: ../../../third_party/icu/source/common/unicode/unorm.h -FILE: ../../../third_party/icu/source/common/unicode/unorm2.h -FILE: ../../../third_party/icu/source/common/unicode/uobject.h -FILE: ../../../third_party/icu/source/common/unicode/urename.h -FILE: ../../../third_party/icu/source/common/unicode/urep.h -FILE: ../../../third_party/icu/source/common/unicode/ures.h -FILE: ../../../third_party/icu/source/common/unicode/uscript.h -FILE: ../../../third_party/icu/source/common/unicode/uset.h -FILE: ../../../third_party/icu/source/common/unicode/usetiter.h -FILE: ../../../third_party/icu/source/common/unicode/ushape.h -FILE: ../../../third_party/icu/source/common/unicode/usprep.h -FILE: ../../../third_party/icu/source/common/unicode/ustring.h -FILE: ../../../third_party/icu/source/common/unicode/ustringtrie.h -FILE: ../../../third_party/icu/source/common/unicode/utext.h -FILE: ../../../third_party/icu/source/common/unicode/utf.h -FILE: ../../../third_party/icu/source/common/unicode/utf16.h -FILE: ../../../third_party/icu/source/common/unicode/utf32.h -FILE: ../../../third_party/icu/source/common/unicode/utf8.h -FILE: ../../../third_party/icu/source/common/unicode/utf_old.h -FILE: ../../../third_party/icu/source/common/unicode/utrace.h -FILE: ../../../third_party/icu/source/common/unicode/utypes.h -FILE: ../../../third_party/icu/source/common/unicode/uvernum.h -FILE: ../../../third_party/icu/source/common/unicode/uversion.h -FILE: ../../../third_party/icu/source/common/unifiedcache.cpp -FILE: ../../../third_party/icu/source/common/unifiedcache.h -FILE: ../../../third_party/icu/source/common/unifilt.cpp -FILE: ../../../third_party/icu/source/common/unifunct.cpp -FILE: ../../../third_party/icu/source/common/uniset.cpp -FILE: ../../../third_party/icu/source/common/uniset_closure.cpp -FILE: ../../../third_party/icu/source/common/uniset_props.cpp -FILE: ../../../third_party/icu/source/common/unisetspan.cpp -FILE: ../../../third_party/icu/source/common/unisetspan.h -FILE: ../../../third_party/icu/source/common/unistr.cpp -FILE: ../../../third_party/icu/source/common/unistr_case.cpp -FILE: ../../../third_party/icu/source/common/unistr_case_locale.cpp -FILE: ../../../third_party/icu/source/common/unistr_cnv.cpp -FILE: ../../../third_party/icu/source/common/unistr_props.cpp -FILE: ../../../third_party/icu/source/common/unistr_titlecase_brkiter.cpp -FILE: ../../../third_party/icu/source/common/unistrappender.h -FILE: ../../../third_party/icu/source/common/unorm.cpp -FILE: ../../../third_party/icu/source/common/unormcmp.cpp -FILE: ../../../third_party/icu/source/common/unormimp.h -FILE: ../../../third_party/icu/source/common/uobject.cpp -FILE: ../../../third_party/icu/source/common/uposixdefs.h -FILE: ../../../third_party/icu/source/common/uprops.cpp -FILE: ../../../third_party/icu/source/common/uprops.h -FILE: ../../../third_party/icu/source/common/ures_cnv.cpp -FILE: ../../../third_party/icu/source/common/uresbund.cpp -FILE: ../../../third_party/icu/source/common/uresdata.cpp -FILE: ../../../third_party/icu/source/common/uresdata.h -FILE: ../../../third_party/icu/source/common/uresimp.h -FILE: ../../../third_party/icu/source/common/ureslocs.h -FILE: ../../../third_party/icu/source/common/usc_impl.cpp -FILE: ../../../third_party/icu/source/common/usc_impl.h -FILE: ../../../third_party/icu/source/common/uscript.cpp -FILE: ../../../third_party/icu/source/common/uscript_props.cpp -FILE: ../../../third_party/icu/source/common/uset.cpp -FILE: ../../../third_party/icu/source/common/uset_imp.h -FILE: ../../../third_party/icu/source/common/uset_props.cpp -FILE: ../../../third_party/icu/source/common/usetiter.cpp -FILE: ../../../third_party/icu/source/common/ushape.cpp -FILE: ../../../third_party/icu/source/common/usprep.cpp -FILE: ../../../third_party/icu/source/common/ustack.cpp -FILE: ../../../third_party/icu/source/common/ustr_cnv.cpp -FILE: ../../../third_party/icu/source/common/ustr_cnv.h -FILE: ../../../third_party/icu/source/common/ustr_imp.h -FILE: ../../../third_party/icu/source/common/ustr_titlecase_brkiter.cpp -FILE: ../../../third_party/icu/source/common/ustr_wcs.cpp -FILE: ../../../third_party/icu/source/common/ustrcase.cpp -FILE: ../../../third_party/icu/source/common/ustrcase_locale.cpp -FILE: ../../../third_party/icu/source/common/ustrenum.cpp -FILE: ../../../third_party/icu/source/common/ustrenum.h -FILE: ../../../third_party/icu/source/common/ustrfmt.cpp -FILE: ../../../third_party/icu/source/common/ustrfmt.h -FILE: ../../../third_party/icu/source/common/ustring.cpp -FILE: ../../../third_party/icu/source/common/ustrtrns.cpp -FILE: ../../../third_party/icu/source/common/utext.cpp -FILE: ../../../third_party/icu/source/common/utf_impl.cpp -FILE: ../../../third_party/icu/source/common/util.cpp -FILE: ../../../third_party/icu/source/common/util.h -FILE: ../../../third_party/icu/source/common/util_props.cpp -FILE: ../../../third_party/icu/source/common/utrace.cpp -FILE: ../../../third_party/icu/source/common/utracimp.h -FILE: ../../../third_party/icu/source/common/utrie.cpp -FILE: ../../../third_party/icu/source/common/utrie.h -FILE: ../../../third_party/icu/source/common/utrie2.cpp -FILE: ../../../third_party/icu/source/common/utrie2.h -FILE: ../../../third_party/icu/source/common/utrie2_builder.cpp -FILE: ../../../third_party/icu/source/common/utrie2_impl.h -FILE: ../../../third_party/icu/source/common/utrie_swap.cpp -FILE: ../../../third_party/icu/source/common/uts46.cpp -FILE: ../../../third_party/icu/source/common/utypeinfo.h -FILE: ../../../third_party/icu/source/common/utypes.cpp -FILE: ../../../third_party/icu/source/common/uvector.cpp -FILE: ../../../third_party/icu/source/common/uvector.h -FILE: ../../../third_party/icu/source/common/uvectr32.cpp -FILE: ../../../third_party/icu/source/common/uvectr32.h -FILE: ../../../third_party/icu/source/common/uvectr64.cpp -FILE: ../../../third_party/icu/source/common/uvectr64.h -FILE: ../../../third_party/icu/source/common/wintz.cpp -FILE: ../../../third_party/icu/source/common/wintz.h -FILE: ../../../third_party/icu/source/config/icu-config-bottom -FILE: ../../../third_party/icu/source/config/icu-config.1.in -FILE: ../../../third_party/icu/source/config/icu.pc.in -FILE: ../../../third_party/icu/source/config/make2sh.sed -FILE: ../../../third_party/icu/source/config/mh-aix-gcc -FILE: ../../../third_party/icu/source/config/mh-aix-va -FILE: ../../../third_party/icu/source/config/mh-alpha-linux-cc -FILE: ../../../third_party/icu/source/config/mh-alpha-linux-gcc -FILE: ../../../third_party/icu/source/config/mh-alpha-osf -FILE: ../../../third_party/icu/source/config/mh-beos -FILE: ../../../third_party/icu/source/config/mh-bsd-gcc -FILE: ../../../third_party/icu/source/config/mh-cygwin -FILE: ../../../third_party/icu/source/config/mh-cygwin-msvc -FILE: ../../../third_party/icu/source/config/mh-cygwin64 -FILE: ../../../third_party/icu/source/config/mh-darwin -FILE: ../../../third_party/icu/source/config/mh-haiku -FILE: ../../../third_party/icu/source/config/mh-hpux-acc -FILE: ../../../third_party/icu/source/config/mh-hpux-gcc -FILE: ../../../third_party/icu/source/config/mh-irix -FILE: ../../../third_party/icu/source/config/mh-linux -FILE: ../../../third_party/icu/source/config/mh-linux-va -FILE: ../../../third_party/icu/source/config/mh-mingw -FILE: ../../../third_party/icu/source/config/mh-mingw64 -FILE: ../../../third_party/icu/source/config/mh-mpras -FILE: ../../../third_party/icu/source/config/mh-msys-msvc -FILE: ../../../third_party/icu/source/config/mh-os390 -FILE: ../../../third_party/icu/source/config/mh-os400 -FILE: ../../../third_party/icu/source/config/mh-qnx -FILE: ../../../third_party/icu/source/config/mh-solaris -FILE: ../../../third_party/icu/source/config/mh-solaris-gcc -FILE: ../../../third_party/icu/source/config/mh-unknown -FILE: ../../../third_party/icu/source/config/windows-update.sed.in -FILE: ../../../third_party/icu/source/data/brkitr/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/build.xml -FILE: ../../../third_party/icu/source/data/coll/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/curr/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/dtd/cldr-35.1/common/dtd/ldml.dtd -FILE: ../../../third_party/icu/source/data/dtd/cldr-35.1/common/dtd/ldmlICU.dtd -FILE: ../../../third_party/icu/source/data/icu-coll-deprecates.xml -FILE: ../../../third_party/icu/source/data/icu-config.xml -FILE: ../../../third_party/icu/source/data/icu-locale-deprecates.xml -FILE: ../../../third_party/icu/source/data/icu-rbnf-deprecates.xml -FILE: ../../../third_party/icu/source/data/icupkg.inc.in -FILE: ../../../third_party/icu/source/data/lang/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/locales/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/makedata.mak -FILE: ../../../third_party/icu/source/data/mappings/cns-11643-1992.ucm -FILE: ../../../third_party/icu/source/data/mappings/ebcdic-xml-us.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-jp-2007.ucm -FILE: ../../../third_party/icu/source/data/mappings/euc-tw-2014.ucm -FILE: ../../../third_party/icu/source/data/mappings/gsm-03.38-2009.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1006_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1025_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1026_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1047_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1051_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1089_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1097_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1098_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1112_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1122_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1123_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1124_P100-1996.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1125_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1129_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1130_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1131_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1132_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1133_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1137_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1140_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1141_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1142_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1143_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1144_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1145_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1146_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1147_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1148_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1149_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1153_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1154_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1155_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1156_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1157_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1158_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1160_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1162_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1164_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1168_P100-2002.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1250_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1251_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1252_P100-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1253_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1254_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1255_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1256_P110-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1257_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1258_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-12712_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1276_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1363_P110-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1363_P11B-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1364_P110-2007.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1371_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1373_P100-2002.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1375_P100-2008.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1383_P110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1386_P100-2001.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1388_P103-2001.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1390_P110-2003.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-1399_P110-2003.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-16684_P110-2003.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-16804_X110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-273_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-277_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-278_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-280_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-284_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-285_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-290_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-297_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-33722_P120-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-33722_P12A_P12A-2004_U2.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-33722_P12A_P12A-2009_U2.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-37_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-420_X120-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-424_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-437_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-4517_P100-2005.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-4899_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-4909_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-4971_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-500_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5012_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5123_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5346_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5347_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5348_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5349_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5350_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5351_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5352_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5353_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5354_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5471_P100-2006.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-5478_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-720_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-737_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-775_P100-1996.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-803_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-813_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-838_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-8482_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-850_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-851_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-852_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-855_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-856_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-857_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-858_P100-1997.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-860_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-861_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-862_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-863_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-864_X110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-865_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-866_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-867_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-868_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-869_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-870_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-871_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-874_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-875_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-878_P100-1996.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-9005_X110-2007.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-901_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-902_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-9067_X100-2005.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-912_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-913_P100-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-914_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-915_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-916_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-918_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-920_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-921_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-922_P100-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-923_P100-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-930_P120-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-933_P110-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-935_P110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-937_P110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-939_P120-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-942_P12A-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-943_P130-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-943_P15A-2003.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-9447_P100-2002.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-9448_X100-2005.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-9449_P100-2002.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-949_P110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-949_P11A-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-950_P110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-954_P101-2007.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-964_P110-1999.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-970_P110_P110-2006_U2.ucm -FILE: ../../../third_party/icu/source/data/mappings/ibm-971_P100-1995.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-25546.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d1.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d2.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d3.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d4.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d5.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d6.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d7.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-s1.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-s2.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-s3.ucm -FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-t.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859_10-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859_11-2001.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-8859_14-1998.ucm -FILE: ../../../third_party/icu/source/data/mappings/iso-ir-165.ucm -FILE: ../../../third_party/icu/source/data/mappings/jisx-212.ucm -FILE: ../../../third_party/icu/source/data/mappings/lmb-excp.ucm -FILE: ../../../third_party/icu/source/data/mappings/macos-0_2-10.2.ucm -FILE: ../../../third_party/icu/source/data/mappings/macos-29-10.2.ucm -FILE: ../../../third_party/icu/source/data/mappings/macos-35-10.2.ucm -FILE: ../../../third_party/icu/source/data/mappings/macos-6_2-10.4.ucm -FILE: ../../../third_party/icu/source/data/mappings/macos-7_3-10.2.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-874-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-949-2000.ucm -FILE: ../../../third_party/icu/source/data/mappings/windows-950-2000.ucm -FILE: ../../../third_party/icu/source/data/misc/icudata.rc -FILE: ../../../third_party/icu/source/data/rbnf/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/region/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/unit/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/data/zone/LOCALE_DEPS.json -FILE: ../../../third_party/icu/source/extra/scrptrun/scrptrun.cpp -FILE: ../../../third_party/icu/source/extra/scrptrun/scrptrun.h -FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.cpp -FILE: ../../../third_party/icu/source/extra/uconv/makedata.mak -FILE: ../../../third_party/icu/source/extra/uconv/pkgdata.inc.in -FILE: ../../../third_party/icu/source/extra/uconv/uconv.1.in -FILE: ../../../third_party/icu/source/extra/uconv/uconv.cpp -FILE: ../../../third_party/icu/source/extra/uconv/unicode/uwmsg.h -FILE: ../../../third_party/icu/source/extra/uconv/uwmsg.c -FILE: ../../../third_party/icu/source/i18n/alphaindex.cpp -FILE: ../../../third_party/icu/source/i18n/anytrans.cpp -FILE: ../../../third_party/icu/source/i18n/anytrans.h -FILE: ../../../third_party/icu/source/i18n/astro.cpp -FILE: ../../../third_party/icu/source/i18n/astro.h -FILE: ../../../third_party/icu/source/i18n/basictz.cpp -FILE: ../../../third_party/icu/source/i18n/bocsu.cpp -FILE: ../../../third_party/icu/source/i18n/bocsu.h -FILE: ../../../third_party/icu/source/i18n/brktrans.cpp -FILE: ../../../third_party/icu/source/i18n/brktrans.h -FILE: ../../../third_party/icu/source/i18n/buddhcal.cpp -FILE: ../../../third_party/icu/source/i18n/buddhcal.h -FILE: ../../../third_party/icu/source/i18n/calendar.cpp -FILE: ../../../third_party/icu/source/i18n/casetrn.cpp -FILE: ../../../third_party/icu/source/i18n/casetrn.h -FILE: ../../../third_party/icu/source/i18n/cecal.cpp -FILE: ../../../third_party/icu/source/i18n/cecal.h -FILE: ../../../third_party/icu/source/i18n/chnsecal.cpp -FILE: ../../../third_party/icu/source/i18n/chnsecal.h -FILE: ../../../third_party/icu/source/i18n/choicfmt.cpp -FILE: ../../../third_party/icu/source/i18n/coleitr.cpp -FILE: ../../../third_party/icu/source/i18n/coll.cpp -FILE: ../../../third_party/icu/source/i18n/collation.cpp -FILE: ../../../third_party/icu/source/i18n/collation.h -FILE: ../../../third_party/icu/source/i18n/collationbuilder.cpp -FILE: ../../../third_party/icu/source/i18n/collationbuilder.h -FILE: ../../../third_party/icu/source/i18n/collationcompare.cpp -FILE: ../../../third_party/icu/source/i18n/collationcompare.h -FILE: ../../../third_party/icu/source/i18n/collationdata.cpp -FILE: ../../../third_party/icu/source/i18n/collationdata.h -FILE: ../../../third_party/icu/source/i18n/collationdatabuilder.cpp -FILE: ../../../third_party/icu/source/i18n/collationdatabuilder.h -FILE: ../../../third_party/icu/source/i18n/collationdatareader.cpp -FILE: ../../../third_party/icu/source/i18n/collationdatareader.h -FILE: ../../../third_party/icu/source/i18n/collationdatawriter.cpp -FILE: ../../../third_party/icu/source/i18n/collationdatawriter.h -FILE: ../../../third_party/icu/source/i18n/collationfastlatin.cpp -FILE: ../../../third_party/icu/source/i18n/collationfastlatin.h -FILE: ../../../third_party/icu/source/i18n/collationfastlatinbuilder.cpp -FILE: ../../../third_party/icu/source/i18n/collationfastlatinbuilder.h -FILE: ../../../third_party/icu/source/i18n/collationfcd.cpp -FILE: ../../../third_party/icu/source/i18n/collationfcd.h -FILE: ../../../third_party/icu/source/i18n/collationiterator.cpp -FILE: ../../../third_party/icu/source/i18n/collationiterator.h -FILE: ../../../third_party/icu/source/i18n/collationkeys.cpp -FILE: ../../../third_party/icu/source/i18n/collationkeys.h -FILE: ../../../third_party/icu/source/i18n/collationroot.cpp -FILE: ../../../third_party/icu/source/i18n/collationroot.h -FILE: ../../../third_party/icu/source/i18n/collationrootelements.cpp -FILE: ../../../third_party/icu/source/i18n/collationrootelements.h -FILE: ../../../third_party/icu/source/i18n/collationruleparser.cpp -FILE: ../../../third_party/icu/source/i18n/collationruleparser.h -FILE: ../../../third_party/icu/source/i18n/collationsets.cpp -FILE: ../../../third_party/icu/source/i18n/collationsets.h -FILE: ../../../third_party/icu/source/i18n/collationsettings.cpp -FILE: ../../../third_party/icu/source/i18n/collationsettings.h -FILE: ../../../third_party/icu/source/i18n/collationtailoring.cpp -FILE: ../../../third_party/icu/source/i18n/collationtailoring.h -FILE: ../../../third_party/icu/source/i18n/collationweights.cpp -FILE: ../../../third_party/icu/source/i18n/collationweights.h -FILE: ../../../third_party/icu/source/i18n/collunsafe.h -FILE: ../../../third_party/icu/source/i18n/compactdecimalformat.cpp -FILE: ../../../third_party/icu/source/i18n/coptccal.cpp -FILE: ../../../third_party/icu/source/i18n/coptccal.h -FILE: ../../../third_party/icu/source/i18n/cpdtrans.cpp -FILE: ../../../third_party/icu/source/i18n/cpdtrans.h -FILE: ../../../third_party/icu/source/i18n/csdetect.cpp -FILE: ../../../third_party/icu/source/i18n/csdetect.h -FILE: ../../../third_party/icu/source/i18n/csmatch.cpp -FILE: ../../../third_party/icu/source/i18n/csmatch.h -FILE: ../../../third_party/icu/source/i18n/csr2022.cpp -FILE: ../../../third_party/icu/source/i18n/csr2022.h -FILE: ../../../third_party/icu/source/i18n/csrecog.cpp -FILE: ../../../third_party/icu/source/i18n/csrecog.h -FILE: ../../../third_party/icu/source/i18n/csrmbcs.cpp -FILE: ../../../third_party/icu/source/i18n/csrmbcs.h -FILE: ../../../third_party/icu/source/i18n/csrsbcs.cpp -FILE: ../../../third_party/icu/source/i18n/csrsbcs.h -FILE: ../../../third_party/icu/source/i18n/csrucode.cpp -FILE: ../../../third_party/icu/source/i18n/csrucode.h -FILE: ../../../third_party/icu/source/i18n/csrutf8.cpp -FILE: ../../../third_party/icu/source/i18n/csrutf8.h -FILE: ../../../third_party/icu/source/i18n/curramt.cpp -FILE: ../../../third_party/icu/source/i18n/currfmt.cpp -FILE: ../../../third_party/icu/source/i18n/currfmt.h -FILE: ../../../third_party/icu/source/i18n/currpinf.cpp -FILE: ../../../third_party/icu/source/i18n/currunit.cpp -FILE: ../../../third_party/icu/source/i18n/dangical.cpp -FILE: ../../../third_party/icu/source/i18n/dangical.h -FILE: ../../../third_party/icu/source/i18n/datefmt.cpp -FILE: ../../../third_party/icu/source/i18n/dayperiodrules.cpp -FILE: ../../../third_party/icu/source/i18n/dayperiodrules.h -FILE: ../../../third_party/icu/source/i18n/dcfmtsym.cpp -FILE: ../../../third_party/icu/source/i18n/decContext.cpp -FILE: ../../../third_party/icu/source/i18n/decContext.h -FILE: ../../../third_party/icu/source/i18n/decNumber.cpp -FILE: ../../../third_party/icu/source/i18n/decNumber.h -FILE: ../../../third_party/icu/source/i18n/decNumberLocal.h -FILE: ../../../third_party/icu/source/i18n/decimfmt.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-diy-fp.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-ieee.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.cpp -FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.h -FILE: ../../../third_party/icu/source/i18n/double-conversion-utils.h -FILE: ../../../third_party/icu/source/i18n/double-conversion.h -FILE: ../../../third_party/icu/source/i18n/dt_impl.h -FILE: ../../../third_party/icu/source/i18n/dtfmtsym.cpp -FILE: ../../../third_party/icu/source/i18n/dtitv_impl.h -FILE: ../../../third_party/icu/source/i18n/dtitvfmt.cpp -FILE: ../../../third_party/icu/source/i18n/dtitvinf.cpp -FILE: ../../../third_party/icu/source/i18n/dtptngen.cpp -FILE: ../../../third_party/icu/source/i18n/dtptngen_impl.h -FILE: ../../../third_party/icu/source/i18n/dtrule.cpp -FILE: ../../../third_party/icu/source/i18n/erarules.cpp -FILE: ../../../third_party/icu/source/i18n/erarules.h -FILE: ../../../third_party/icu/source/i18n/esctrn.cpp -FILE: ../../../third_party/icu/source/i18n/esctrn.h -FILE: ../../../third_party/icu/source/i18n/ethpccal.cpp -FILE: ../../../third_party/icu/source/i18n/ethpccal.h -FILE: ../../../third_party/icu/source/i18n/fmtable.cpp -FILE: ../../../third_party/icu/source/i18n/fmtable_cnv.cpp -FILE: ../../../third_party/icu/source/i18n/fmtableimp.h -FILE: ../../../third_party/icu/source/i18n/format.cpp -FILE: ../../../third_party/icu/source/i18n/formatted_string_builder.cpp -FILE: ../../../third_party/icu/source/i18n/formatted_string_builder.h -FILE: ../../../third_party/icu/source/i18n/formattedval_impl.h -FILE: ../../../third_party/icu/source/i18n/formattedval_iterimpl.cpp -FILE: ../../../third_party/icu/source/i18n/formattedval_sbimpl.cpp -FILE: ../../../third_party/icu/source/i18n/formattedvalue.cpp -FILE: ../../../third_party/icu/source/i18n/fphdlimp.cpp -FILE: ../../../third_party/icu/source/i18n/fphdlimp.h -FILE: ../../../third_party/icu/source/i18n/fpositer.cpp -FILE: ../../../third_party/icu/source/i18n/funcrepl.cpp -FILE: ../../../third_party/icu/source/i18n/funcrepl.h -FILE: ../../../third_party/icu/source/i18n/gender.cpp -FILE: ../../../third_party/icu/source/i18n/gregocal.cpp -FILE: ../../../third_party/icu/source/i18n/gregoimp.cpp -FILE: ../../../third_party/icu/source/i18n/gregoimp.h -FILE: ../../../third_party/icu/source/i18n/hebrwcal.cpp -FILE: ../../../third_party/icu/source/i18n/hebrwcal.h -FILE: ../../../third_party/icu/source/i18n/i18n.rc -FILE: ../../../third_party/icu/source/i18n/indiancal.cpp -FILE: ../../../third_party/icu/source/i18n/indiancal.h -FILE: ../../../third_party/icu/source/i18n/inputext.cpp -FILE: ../../../third_party/icu/source/i18n/inputext.h -FILE: ../../../third_party/icu/source/i18n/islamcal.cpp -FILE: ../../../third_party/icu/source/i18n/islamcal.h -FILE: ../../../third_party/icu/source/i18n/japancal.cpp -FILE: ../../../third_party/icu/source/i18n/japancal.h -FILE: ../../../third_party/icu/source/i18n/listformatter.cpp -FILE: ../../../third_party/icu/source/i18n/measfmt.cpp -FILE: ../../../third_party/icu/source/i18n/measunit.cpp -FILE: ../../../third_party/icu/source/i18n/measunit_extra.cpp -FILE: ../../../third_party/icu/source/i18n/measunit_impl.h -FILE: ../../../third_party/icu/source/i18n/measure.cpp -FILE: ../../../third_party/icu/source/i18n/msgfmt.cpp -FILE: ../../../third_party/icu/source/i18n/msgfmt_impl.h -FILE: ../../../third_party/icu/source/i18n/name2uni.cpp -FILE: ../../../third_party/icu/source/i18n/name2uni.h -FILE: ../../../third_party/icu/source/i18n/nfrlist.h -FILE: ../../../third_party/icu/source/i18n/nfrs.cpp -FILE: ../../../third_party/icu/source/i18n/nfrs.h -FILE: ../../../third_party/icu/source/i18n/nfrule.cpp -FILE: ../../../third_party/icu/source/i18n/nfrule.h -FILE: ../../../third_party/icu/source/i18n/nfsubs.cpp -FILE: ../../../third_party/icu/source/i18n/nfsubs.h -FILE: ../../../third_party/icu/source/i18n/nortrans.cpp -FILE: ../../../third_party/icu/source/i18n/nortrans.h -FILE: ../../../third_party/icu/source/i18n/nounit.cpp -FILE: ../../../third_party/icu/source/i18n/nultrans.cpp -FILE: ../../../third_party/icu/source/i18n/nultrans.h -FILE: ../../../third_party/icu/source/i18n/number_affixutils.cpp -FILE: ../../../third_party/icu/source/i18n/number_affixutils.h -FILE: ../../../third_party/icu/source/i18n/number_asformat.cpp -FILE: ../../../third_party/icu/source/i18n/number_asformat.h -FILE: ../../../third_party/icu/source/i18n/number_capi.cpp -FILE: ../../../third_party/icu/source/i18n/number_compact.cpp -FILE: ../../../third_party/icu/source/i18n/number_compact.h -FILE: ../../../third_party/icu/source/i18n/number_currencysymbols.cpp -FILE: ../../../third_party/icu/source/i18n/number_currencysymbols.h -FILE: ../../../third_party/icu/source/i18n/number_decimalquantity.cpp -FILE: ../../../third_party/icu/source/i18n/number_decimalquantity.h -FILE: ../../../third_party/icu/source/i18n/number_decimfmtprops.cpp -FILE: ../../../third_party/icu/source/i18n/number_decimfmtprops.h -FILE: ../../../third_party/icu/source/i18n/number_decnum.h -FILE: ../../../third_party/icu/source/i18n/number_fluent.cpp -FILE: ../../../third_party/icu/source/i18n/number_formatimpl.cpp -FILE: ../../../third_party/icu/source/i18n/number_formatimpl.h -FILE: ../../../third_party/icu/source/i18n/number_grouping.cpp -FILE: ../../../third_party/icu/source/i18n/number_integerwidth.cpp -FILE: ../../../third_party/icu/source/i18n/number_longnames.cpp -FILE: ../../../third_party/icu/source/i18n/number_longnames.h -FILE: ../../../third_party/icu/source/i18n/number_mapper.cpp -FILE: ../../../third_party/icu/source/i18n/number_mapper.h -FILE: ../../../third_party/icu/source/i18n/number_microprops.h -FILE: ../../../third_party/icu/source/i18n/number_modifiers.cpp -FILE: ../../../third_party/icu/source/i18n/number_modifiers.h -FILE: ../../../third_party/icu/source/i18n/number_multiplier.cpp -FILE: ../../../third_party/icu/source/i18n/number_multiplier.h -FILE: ../../../third_party/icu/source/i18n/number_notation.cpp -FILE: ../../../third_party/icu/source/i18n/number_output.cpp -FILE: ../../../third_party/icu/source/i18n/number_padding.cpp -FILE: ../../../third_party/icu/source/i18n/number_patternmodifier.cpp -FILE: ../../../third_party/icu/source/i18n/number_patternmodifier.h -FILE: ../../../third_party/icu/source/i18n/number_patternstring.cpp -FILE: ../../../third_party/icu/source/i18n/number_patternstring.h -FILE: ../../../third_party/icu/source/i18n/number_rounding.cpp -FILE: ../../../third_party/icu/source/i18n/number_roundingutils.h -FILE: ../../../third_party/icu/source/i18n/number_scientific.cpp -FILE: ../../../third_party/icu/source/i18n/number_scientific.h -FILE: ../../../third_party/icu/source/i18n/number_skeletons.cpp -FILE: ../../../third_party/icu/source/i18n/number_skeletons.h -FILE: ../../../third_party/icu/source/i18n/number_types.h -FILE: ../../../third_party/icu/source/i18n/number_utils.cpp -FILE: ../../../third_party/icu/source/i18n/number_utils.h -FILE: ../../../third_party/icu/source/i18n/number_utypes.h -FILE: ../../../third_party/icu/source/i18n/numfmt.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_affixes.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_affixes.h -FILE: ../../../third_party/icu/source/i18n/numparse_compositions.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_compositions.h -FILE: ../../../third_party/icu/source/i18n/numparse_currency.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_currency.h -FILE: ../../../third_party/icu/source/i18n/numparse_decimal.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_decimal.h -FILE: ../../../third_party/icu/source/i18n/numparse_impl.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_impl.h -FILE: ../../../third_party/icu/source/i18n/numparse_parsednumber.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_scientific.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_scientific.h -FILE: ../../../third_party/icu/source/i18n/numparse_symbols.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_symbols.h -FILE: ../../../third_party/icu/source/i18n/numparse_types.h -FILE: ../../../third_party/icu/source/i18n/numparse_utils.h -FILE: ../../../third_party/icu/source/i18n/numparse_validators.cpp -FILE: ../../../third_party/icu/source/i18n/numparse_validators.h -FILE: ../../../third_party/icu/source/i18n/numrange_fluent.cpp -FILE: ../../../third_party/icu/source/i18n/numrange_impl.cpp -FILE: ../../../third_party/icu/source/i18n/numrange_impl.h -FILE: ../../../third_party/icu/source/i18n/numsys.cpp -FILE: ../../../third_party/icu/source/i18n/numsys_impl.h -FILE: ../../../third_party/icu/source/i18n/olsontz.cpp -FILE: ../../../third_party/icu/source/i18n/olsontz.h -FILE: ../../../third_party/icu/source/i18n/persncal.cpp -FILE: ../../../third_party/icu/source/i18n/persncal.h -FILE: ../../../third_party/icu/source/i18n/plurfmt.cpp -FILE: ../../../third_party/icu/source/i18n/plurrule.cpp -FILE: ../../../third_party/icu/source/i18n/plurrule_impl.h -FILE: ../../../third_party/icu/source/i18n/quant.cpp -FILE: ../../../third_party/icu/source/i18n/quant.h -FILE: ../../../third_party/icu/source/i18n/quantityformatter.cpp -FILE: ../../../third_party/icu/source/i18n/quantityformatter.h -FILE: ../../../third_party/icu/source/i18n/rbnf.cpp -FILE: ../../../third_party/icu/source/i18n/rbt.cpp -FILE: ../../../third_party/icu/source/i18n/rbt.h -FILE: ../../../third_party/icu/source/i18n/rbt_data.cpp -FILE: ../../../third_party/icu/source/i18n/rbt_data.h -FILE: ../../../third_party/icu/source/i18n/rbt_pars.cpp -FILE: ../../../third_party/icu/source/i18n/rbt_pars.h -FILE: ../../../third_party/icu/source/i18n/rbt_rule.cpp -FILE: ../../../third_party/icu/source/i18n/rbt_rule.h -FILE: ../../../third_party/icu/source/i18n/rbt_set.cpp -FILE: ../../../third_party/icu/source/i18n/rbt_set.h -FILE: ../../../third_party/icu/source/i18n/rbtz.cpp -FILE: ../../../third_party/icu/source/i18n/regexcmp.cpp -FILE: ../../../third_party/icu/source/i18n/regexcmp.h -FILE: ../../../third_party/icu/source/i18n/regexcst.h -FILE: ../../../third_party/icu/source/i18n/regeximp.cpp -FILE: ../../../third_party/icu/source/i18n/regeximp.h -FILE: ../../../third_party/icu/source/i18n/regexst.cpp -FILE: ../../../third_party/icu/source/i18n/regexst.h -FILE: ../../../third_party/icu/source/i18n/regextxt.cpp -FILE: ../../../third_party/icu/source/i18n/regextxt.h -FILE: ../../../third_party/icu/source/i18n/region.cpp -FILE: ../../../third_party/icu/source/i18n/region_impl.h -FILE: ../../../third_party/icu/source/i18n/reldatefmt.cpp -FILE: ../../../third_party/icu/source/i18n/reldtfmt.cpp -FILE: ../../../third_party/icu/source/i18n/reldtfmt.h -FILE: ../../../third_party/icu/source/i18n/rematch.cpp -FILE: ../../../third_party/icu/source/i18n/remtrans.cpp -FILE: ../../../third_party/icu/source/i18n/remtrans.h -FILE: ../../../third_party/icu/source/i18n/repattrn.cpp -FILE: ../../../third_party/icu/source/i18n/rulebasedcollator.cpp -FILE: ../../../third_party/icu/source/i18n/scientificnumberformatter.cpp -FILE: ../../../third_party/icu/source/i18n/scriptset.cpp -FILE: ../../../third_party/icu/source/i18n/scriptset.h -FILE: ../../../third_party/icu/source/i18n/search.cpp -FILE: ../../../third_party/icu/source/i18n/selfmt.cpp -FILE: ../../../third_party/icu/source/i18n/selfmtimpl.h -FILE: ../../../third_party/icu/source/i18n/sharedbreakiterator.cpp -FILE: ../../../third_party/icu/source/i18n/sharedbreakiterator.h -FILE: ../../../third_party/icu/source/i18n/sharedcalendar.h -FILE: ../../../third_party/icu/source/i18n/shareddateformatsymbols.h -FILE: ../../../third_party/icu/source/i18n/sharednumberformat.h -FILE: ../../../third_party/icu/source/i18n/sharedpluralrules.h -FILE: ../../../third_party/icu/source/i18n/simpletz.cpp -FILE: ../../../third_party/icu/source/i18n/smpdtfmt.cpp -FILE: ../../../third_party/icu/source/i18n/smpdtfst.cpp -FILE: ../../../third_party/icu/source/i18n/smpdtfst.h -FILE: ../../../third_party/icu/source/i18n/sortkey.cpp -FILE: ../../../third_party/icu/source/i18n/standardplural.cpp -FILE: ../../../third_party/icu/source/i18n/standardplural.h -FILE: ../../../third_party/icu/source/i18n/string_segment.cpp -FILE: ../../../third_party/icu/source/i18n/string_segment.h -FILE: ../../../third_party/icu/source/i18n/strmatch.cpp -FILE: ../../../third_party/icu/source/i18n/strmatch.h -FILE: ../../../third_party/icu/source/i18n/strrepl.cpp -FILE: ../../../third_party/icu/source/i18n/strrepl.h -FILE: ../../../third_party/icu/source/i18n/stsearch.cpp -FILE: ../../../third_party/icu/source/i18n/taiwncal.cpp -FILE: ../../../third_party/icu/source/i18n/taiwncal.h -FILE: ../../../third_party/icu/source/i18n/timezone.cpp -FILE: ../../../third_party/icu/source/i18n/titletrn.cpp -FILE: ../../../third_party/icu/source/i18n/titletrn.h -FILE: ../../../third_party/icu/source/i18n/tmunit.cpp -FILE: ../../../third_party/icu/source/i18n/tmutamt.cpp -FILE: ../../../third_party/icu/source/i18n/tmutfmt.cpp -FILE: ../../../third_party/icu/source/i18n/tolowtrn.cpp -FILE: ../../../third_party/icu/source/i18n/tolowtrn.h -FILE: ../../../third_party/icu/source/i18n/toupptrn.cpp -FILE: ../../../third_party/icu/source/i18n/toupptrn.h -FILE: ../../../third_party/icu/source/i18n/translit.cpp -FILE: ../../../third_party/icu/source/i18n/transreg.cpp -FILE: ../../../third_party/icu/source/i18n/transreg.h -FILE: ../../../third_party/icu/source/i18n/tridpars.cpp -FILE: ../../../third_party/icu/source/i18n/tridpars.h -FILE: ../../../third_party/icu/source/i18n/tzfmt.cpp -FILE: ../../../third_party/icu/source/i18n/tzgnames.cpp -FILE: ../../../third_party/icu/source/i18n/tzgnames.h -FILE: ../../../third_party/icu/source/i18n/tznames.cpp -FILE: ../../../third_party/icu/source/i18n/tznames_impl.cpp -FILE: ../../../third_party/icu/source/i18n/tznames_impl.h -FILE: ../../../third_party/icu/source/i18n/tzrule.cpp -FILE: ../../../third_party/icu/source/i18n/tztrans.cpp -FILE: ../../../third_party/icu/source/i18n/ucal.cpp -FILE: ../../../third_party/icu/source/i18n/ucln_in.cpp -FILE: ../../../third_party/icu/source/i18n/ucln_in.h -FILE: ../../../third_party/icu/source/i18n/ucol.cpp -FILE: ../../../third_party/icu/source/i18n/ucol_imp.h -FILE: ../../../third_party/icu/source/i18n/ucol_res.cpp -FILE: ../../../third_party/icu/source/i18n/ucol_sit.cpp -FILE: ../../../third_party/icu/source/i18n/ucoleitr.cpp -FILE: ../../../third_party/icu/source/i18n/ucsdet.cpp -FILE: ../../../third_party/icu/source/i18n/udat.cpp -FILE: ../../../third_party/icu/source/i18n/udateintervalformat.cpp -FILE: ../../../third_party/icu/source/i18n/udatpg.cpp -FILE: ../../../third_party/icu/source/i18n/ufieldpositer.cpp -FILE: ../../../third_party/icu/source/i18n/uitercollationiterator.cpp -FILE: ../../../third_party/icu/source/i18n/uitercollationiterator.h -FILE: ../../../third_party/icu/source/i18n/ulistformatter.cpp -FILE: ../../../third_party/icu/source/i18n/ulocdata.cpp -FILE: ../../../third_party/icu/source/i18n/umsg.cpp -FILE: ../../../third_party/icu/source/i18n/umsg_imp.h -FILE: ../../../third_party/icu/source/i18n/unesctrn.cpp -FILE: ../../../third_party/icu/source/i18n/unesctrn.h -FILE: ../../../third_party/icu/source/i18n/uni2name.cpp -FILE: ../../../third_party/icu/source/i18n/uni2name.h -FILE: ../../../third_party/icu/source/i18n/unicode/alphaindex.h -FILE: ../../../third_party/icu/source/i18n/unicode/basictz.h -FILE: ../../../third_party/icu/source/i18n/unicode/calendar.h -FILE: ../../../third_party/icu/source/i18n/unicode/choicfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/coleitr.h -FILE: ../../../third_party/icu/source/i18n/unicode/coll.h -FILE: ../../../third_party/icu/source/i18n/unicode/compactdecimalformat.h -FILE: ../../../third_party/icu/source/i18n/unicode/curramt.h -FILE: ../../../third_party/icu/source/i18n/unicode/currpinf.h -FILE: ../../../third_party/icu/source/i18n/unicode/currunit.h -FILE: ../../../third_party/icu/source/i18n/unicode/datefmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/dcfmtsym.h -FILE: ../../../third_party/icu/source/i18n/unicode/decimfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/dtfmtsym.h -FILE: ../../../third_party/icu/source/i18n/unicode/dtitvfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/dtitvinf.h -FILE: ../../../third_party/icu/source/i18n/unicode/dtptngen.h -FILE: ../../../third_party/icu/source/i18n/unicode/dtrule.h -FILE: ../../../third_party/icu/source/i18n/unicode/fieldpos.h -FILE: ../../../third_party/icu/source/i18n/unicode/fmtable.h -FILE: ../../../third_party/icu/source/i18n/unicode/format.h -FILE: ../../../third_party/icu/source/i18n/unicode/formattedvalue.h -FILE: ../../../third_party/icu/source/i18n/unicode/fpositer.h -FILE: ../../../third_party/icu/source/i18n/unicode/gender.h -FILE: ../../../third_party/icu/source/i18n/unicode/gregocal.h -FILE: ../../../third_party/icu/source/i18n/unicode/listformatter.h -FILE: ../../../third_party/icu/source/i18n/unicode/measfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/measunit.h -FILE: ../../../third_party/icu/source/i18n/unicode/measure.h -FILE: ../../../third_party/icu/source/i18n/unicode/msgfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/nounit.h -FILE: ../../../third_party/icu/source/i18n/unicode/numberformatter.h -FILE: ../../../third_party/icu/source/i18n/unicode/numberrangeformatter.h -FILE: ../../../third_party/icu/source/i18n/unicode/numfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/numsys.h -FILE: ../../../third_party/icu/source/i18n/unicode/plurfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/plurrule.h -FILE: ../../../third_party/icu/source/i18n/unicode/rbnf.h -FILE: ../../../third_party/icu/source/i18n/unicode/rbtz.h -FILE: ../../../third_party/icu/source/i18n/unicode/regex.h -FILE: ../../../third_party/icu/source/i18n/unicode/region.h -FILE: ../../../third_party/icu/source/i18n/unicode/reldatefmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/scientificnumberformatter.h -FILE: ../../../third_party/icu/source/i18n/unicode/search.h -FILE: ../../../third_party/icu/source/i18n/unicode/selfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/simpletz.h -FILE: ../../../third_party/icu/source/i18n/unicode/smpdtfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/sortkey.h -FILE: ../../../third_party/icu/source/i18n/unicode/stsearch.h -FILE: ../../../third_party/icu/source/i18n/unicode/tblcoll.h -FILE: ../../../third_party/icu/source/i18n/unicode/timezone.h -FILE: ../../../third_party/icu/source/i18n/unicode/tmunit.h -FILE: ../../../third_party/icu/source/i18n/unicode/tmutamt.h -FILE: ../../../third_party/icu/source/i18n/unicode/tmutfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/translit.h -FILE: ../../../third_party/icu/source/i18n/unicode/tzfmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/tznames.h -FILE: ../../../third_party/icu/source/i18n/unicode/tzrule.h -FILE: ../../../third_party/icu/source/i18n/unicode/tztrans.h -FILE: ../../../third_party/icu/source/i18n/unicode/ucal.h -FILE: ../../../third_party/icu/source/i18n/unicode/ucol.h -FILE: ../../../third_party/icu/source/i18n/unicode/ucoleitr.h -FILE: ../../../third_party/icu/source/i18n/unicode/ucsdet.h -FILE: ../../../third_party/icu/source/i18n/unicode/udat.h -FILE: ../../../third_party/icu/source/i18n/unicode/udateintervalformat.h -FILE: ../../../third_party/icu/source/i18n/unicode/udatpg.h -FILE: ../../../third_party/icu/source/i18n/unicode/ufieldpositer.h -FILE: ../../../third_party/icu/source/i18n/unicode/uformattable.h -FILE: ../../../third_party/icu/source/i18n/unicode/uformattedvalue.h -FILE: ../../../third_party/icu/source/i18n/unicode/ugender.h -FILE: ../../../third_party/icu/source/i18n/unicode/ulistformatter.h -FILE: ../../../third_party/icu/source/i18n/unicode/ulocdata.h -FILE: ../../../third_party/icu/source/i18n/unicode/umsg.h -FILE: ../../../third_party/icu/source/i18n/unicode/unirepl.h -FILE: ../../../third_party/icu/source/i18n/unicode/unum.h -FILE: ../../../third_party/icu/source/i18n/unicode/unumberformatter.h -FILE: ../../../third_party/icu/source/i18n/unicode/unumsys.h -FILE: ../../../third_party/icu/source/i18n/unicode/upluralrules.h -FILE: ../../../third_party/icu/source/i18n/unicode/uregex.h -FILE: ../../../third_party/icu/source/i18n/unicode/uregion.h -FILE: ../../../third_party/icu/source/i18n/unicode/ureldatefmt.h -FILE: ../../../third_party/icu/source/i18n/unicode/usearch.h -FILE: ../../../third_party/icu/source/i18n/unicode/uspoof.h -FILE: ../../../third_party/icu/source/i18n/unicode/utmscale.h -FILE: ../../../third_party/icu/source/i18n/unicode/utrans.h -FILE: ../../../third_party/icu/source/i18n/unicode/vtzone.h -FILE: ../../../third_party/icu/source/i18n/unum.cpp -FILE: ../../../third_party/icu/source/i18n/unumsys.cpp -FILE: ../../../third_party/icu/source/i18n/upluralrules.cpp -FILE: ../../../third_party/icu/source/i18n/uregex.cpp -FILE: ../../../third_party/icu/source/i18n/uregexc.cpp -FILE: ../../../third_party/icu/source/i18n/uregion.cpp -FILE: ../../../third_party/icu/source/i18n/usearch.cpp -FILE: ../../../third_party/icu/source/i18n/uspoof.cpp -FILE: ../../../third_party/icu/source/i18n/uspoof_build.cpp -FILE: ../../../third_party/icu/source/i18n/uspoof_conf.cpp -FILE: ../../../third_party/icu/source/i18n/uspoof_conf.h -FILE: ../../../third_party/icu/source/i18n/uspoof_impl.cpp -FILE: ../../../third_party/icu/source/i18n/uspoof_impl.h -FILE: ../../../third_party/icu/source/i18n/usrchimp.h -FILE: ../../../third_party/icu/source/i18n/utf16collationiterator.cpp -FILE: ../../../third_party/icu/source/i18n/utf16collationiterator.h -FILE: ../../../third_party/icu/source/i18n/utf8collationiterator.cpp -FILE: ../../../third_party/icu/source/i18n/utf8collationiterator.h -FILE: ../../../third_party/icu/source/i18n/utmscale.cpp -FILE: ../../../third_party/icu/source/i18n/utrans.cpp -FILE: ../../../third_party/icu/source/i18n/vtzone.cpp -FILE: ../../../third_party/icu/source/i18n/vzone.cpp -FILE: ../../../third_party/icu/source/i18n/vzone.h -FILE: ../../../third_party/icu/source/i18n/windtfmt.cpp -FILE: ../../../third_party/icu/source/i18n/windtfmt.h -FILE: ../../../third_party/icu/source/i18n/winnmfmt.cpp -FILE: ../../../third_party/icu/source/i18n/winnmfmt.h -FILE: ../../../third_party/icu/source/i18n/wintzimpl.cpp -FILE: ../../../third_party/icu/source/i18n/wintzimpl.h -FILE: ../../../third_party/icu/source/i18n/zonemeta.cpp -FILE: ../../../third_party/icu/source/i18n/zonemeta.h -FILE: ../../../third_party/icu/source/i18n/zrule.cpp -FILE: ../../../third_party/icu/source/i18n/zrule.h -FILE: ../../../third_party/icu/source/i18n/ztrans.cpp -FILE: ../../../third_party/icu/source/i18n/ztrans.h -FILE: ../../../third_party/icu/source/icudefs.mk.in -FILE: ../../../third_party/icu/source/io/io.rc -FILE: ../../../third_party/icu/source/io/locbund.cpp -FILE: ../../../third_party/icu/source/io/locbund.h -FILE: ../../../third_party/icu/source/io/sprintf.cpp -FILE: ../../../third_party/icu/source/io/sscanf.cpp -FILE: ../../../third_party/icu/source/io/ucln_io.cpp -FILE: ../../../third_party/icu/source/io/ucln_io.h -FILE: ../../../third_party/icu/source/io/ufile.cpp -FILE: ../../../third_party/icu/source/io/ufile.h -FILE: ../../../third_party/icu/source/io/ufmt_cmn.cpp -FILE: ../../../third_party/icu/source/io/ufmt_cmn.h -FILE: ../../../third_party/icu/source/io/unicode/ustdio.h -FILE: ../../../third_party/icu/source/io/unicode/ustream.h -FILE: ../../../third_party/icu/source/io/uprintf.cpp -FILE: ../../../third_party/icu/source/io/uprintf.h -FILE: ../../../third_party/icu/source/io/uprntf_p.cpp -FILE: ../../../third_party/icu/source/io/uscanf.cpp -FILE: ../../../third_party/icu/source/io/uscanf.h -FILE: ../../../third_party/icu/source/io/uscanf_p.cpp -FILE: ../../../third_party/icu/source/io/ustdio.cpp -FILE: ../../../third_party/icu/source/io/ustream.cpp -FILE: ../../../third_party/icu/source/python/icutools/databuilder/filtration_schema.json -FILE: ../../../third_party/icu/source/samples/break/break.cpp -FILE: ../../../third_party/icu/source/samples/break/ubreak.c -FILE: ../../../third_party/icu/source/samples/cal/cal.c -FILE: ../../../third_party/icu/source/samples/cal/uprint.c -FILE: ../../../third_party/icu/source/samples/cal/uprint.h -FILE: ../../../third_party/icu/source/samples/case/case.cpp -FILE: ../../../third_party/icu/source/samples/case/ucase.c -FILE: ../../../third_party/icu/source/samples/citer/citer.cpp -FILE: ../../../third_party/icu/source/samples/coll/coll.cpp -FILE: ../../../third_party/icu/source/samples/csdet/csdet.c -FILE: ../../../third_party/icu/source/samples/date/date.c -FILE: ../../../third_party/icu/source/samples/date/uprint.c -FILE: ../../../third_party/icu/source/samples/date/uprint.h -FILE: ../../../third_party/icu/source/samples/datecal/cal.cpp -FILE: ../../../third_party/icu/source/samples/datecal/ccal.c -FILE: ../../../third_party/icu/source/samples/datefmt/answers/main_0.cpp -FILE: ../../../third_party/icu/source/samples/datefmt/answers/main_1.cpp -FILE: ../../../third_party/icu/source/samples/datefmt/answers/main_2.cpp -FILE: ../../../third_party/icu/source/samples/datefmt/answers/main_3.cpp -FILE: ../../../third_party/icu/source/samples/datefmt/main.cpp -FILE: ../../../third_party/icu/source/samples/datefmt/util.cpp -FILE: ../../../third_party/icu/source/samples/datefmt/util.h -FILE: ../../../third_party/icu/source/samples/dtitvfmtsample/dtitvfmtsample.cpp -FILE: ../../../third_party/icu/source/samples/dtptngsample/dtptngsample.cpp -FILE: ../../../third_party/icu/source/samples/layout/FontMap.GDI -FILE: ../../../third_party/icu/source/samples/layout/FontMap.Gnome -FILE: ../../../third_party/icu/source/samples/layout/FontMap.cpp -FILE: ../../../third_party/icu/source/samples/layout/FontMap.h -FILE: ../../../third_party/icu/source/samples/layout/FontTableCache.cpp -FILE: ../../../third_party/icu/source/samples/layout/FontTableCache.h -FILE: ../../../third_party/icu/source/samples/layout/GDIFontInstance.cpp -FILE: ../../../third_party/icu/source/samples/layout/GDIFontInstance.h -FILE: ../../../third_party/icu/source/samples/layout/GDIFontMap.cpp -FILE: ../../../third_party/icu/source/samples/layout/GDIFontMap.h -FILE: ../../../third_party/icu/source/samples/layout/GDIGUISupport.cpp -FILE: ../../../third_party/icu/source/samples/layout/GDIGUISupport.h -FILE: ../../../third_party/icu/source/samples/layout/GUISupport.h -FILE: ../../../third_party/icu/source/samples/layout/GnomeFontInstance.cpp -FILE: ../../../third_party/icu/source/samples/layout/GnomeFontInstance.h -FILE: ../../../third_party/icu/source/samples/layout/GnomeFontMap.cpp -FILE: ../../../third_party/icu/source/samples/layout/GnomeFontMap.h -FILE: ../../../third_party/icu/source/samples/layout/GnomeGUISupport.cpp -FILE: ../../../third_party/icu/source/samples/layout/GnomeGUISupport.h -FILE: ../../../third_party/icu/source/samples/layout/LayoutSample.rc -FILE: ../../../third_party/icu/source/samples/layout/RenderingSurface.h -FILE: ../../../third_party/icu/source/samples/layout/ScriptCompositeFontInstance.cpp -FILE: ../../../third_party/icu/source/samples/layout/ScriptCompositeFontInstance.h -FILE: ../../../third_party/icu/source/samples/layout/Surface.cpp -FILE: ../../../third_party/icu/source/samples/layout/Surface.h -FILE: ../../../third_party/icu/source/samples/layout/UnicodeReader.cpp -FILE: ../../../third_party/icu/source/samples/layout/UnicodeReader.h -FILE: ../../../third_party/icu/source/samples/layout/arraymem.h -FILE: ../../../third_party/icu/source/samples/layout/cgnomelayout.c -FILE: ../../../third_party/icu/source/samples/layout/clayout.c -FILE: ../../../third_party/icu/source/samples/layout/cmaps.cpp -FILE: ../../../third_party/icu/source/samples/layout/cmaps.h -FILE: ../../../third_party/icu/source/samples/layout/gdiglue.cpp -FILE: ../../../third_party/icu/source/samples/layout/gdiglue.h -FILE: ../../../third_party/icu/source/samples/layout/gnomeglue.cpp -FILE: ../../../third_party/icu/source/samples/layout/gnomeglue.h -FILE: ../../../third_party/icu/source/samples/layout/gnomelayout.cpp -FILE: ../../../third_party/icu/source/samples/layout/gsupport.h -FILE: ../../../third_party/icu/source/samples/layout/layout.cpp -FILE: ../../../third_party/icu/source/samples/layout/paragraph.cpp -FILE: ../../../third_party/icu/source/samples/layout/paragraph.h -FILE: ../../../third_party/icu/source/samples/layout/pflow.c -FILE: ../../../third_party/icu/source/samples/layout/pflow.h -FILE: ../../../third_party/icu/source/samples/layout/resource.h -FILE: ../../../third_party/icu/source/samples/layout/rsurface.cpp -FILE: ../../../third_party/icu/source/samples/layout/rsurface.h -FILE: ../../../third_party/icu/source/samples/layout/sfnt.h -FILE: ../../../third_party/icu/source/samples/layout/ucreader.cpp -FILE: ../../../third_party/icu/source/samples/layout/ucreader.h -FILE: ../../../third_party/icu/source/samples/legacy/legacy.cpp -FILE: ../../../third_party/icu/source/samples/legacy/newcol.cpp -FILE: ../../../third_party/icu/source/samples/legacy/oldcol.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/answers/main_0.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/answers/main_1.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/answers/main_2.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/answers/main_3.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/main.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/util.cpp -FILE: ../../../third_party/icu/source/samples/msgfmt/util.h -FILE: ../../../third_party/icu/source/samples/numfmt/capi.c -FILE: ../../../third_party/icu/source/samples/numfmt/main.cpp -FILE: ../../../third_party/icu/source/samples/numfmt/util.cpp -FILE: ../../../third_party/icu/source/samples/numfmt/util.h -FILE: ../../../third_party/icu/source/samples/plurfmtsample/plurfmtsample.cpp -FILE: ../../../third_party/icu/source/samples/props/props.cpp -FILE: ../../../third_party/icu/source/samples/strsrch/strsrch.cpp -FILE: ../../../third_party/icu/source/samples/translit/answers/main_1.cpp -FILE: ../../../third_party/icu/source/samples/translit/answers/main_2.cpp -FILE: ../../../third_party/icu/source/samples/translit/answers/main_3.cpp -FILE: ../../../third_party/icu/source/samples/translit/answers/main_4.cpp -FILE: ../../../third_party/icu/source/samples/translit/answers/unaccent.cpp -FILE: ../../../third_party/icu/source/samples/translit/answers/unaccent.h -FILE: ../../../third_party/icu/source/samples/translit/main.cpp -FILE: ../../../third_party/icu/source/samples/translit/unaccent.cpp -FILE: ../../../third_party/icu/source/samples/translit/unaccent.h -FILE: ../../../third_party/icu/source/samples/translit/util.cpp -FILE: ../../../third_party/icu/source/samples/translit/util.h -FILE: ../../../third_party/icu/source/samples/uciter8/uciter8.c -FILE: ../../../third_party/icu/source/samples/uciter8/uit_len8.c -FILE: ../../../third_party/icu/source/samples/uciter8/uit_len8.h -FILE: ../../../third_party/icu/source/samples/ucnv/convsamp.cpp -FILE: ../../../third_party/icu/source/samples/ucnv/flagcb.c -FILE: ../../../third_party/icu/source/samples/ucnv/flagcb.h -FILE: ../../../third_party/icu/source/samples/udata/reader.c -FILE: ../../../third_party/icu/source/samples/udata/writer.c -FILE: ../../../third_party/icu/source/samples/ufortune/resources/fortune_resources.mak -FILE: ../../../third_party/icu/source/samples/ufortune/ufortune.c -FILE: ../../../third_party/icu/source/samples/ugrep/ugrep.cpp -FILE: ../../../third_party/icu/source/samples/uresb/resources.mak -FILE: ../../../third_party/icu/source/samples/uresb/uresb.c -FILE: ../../../third_party/icu/source/samples/ustring/ustring.cpp -FILE: ../../../third_party/icu/source/stubdata/stubdata.cpp -FILE: ../../../third_party/icu/source/tools/ctestfw/ctest.c -FILE: ../../../third_party/icu/source/tools/ctestfw/datamap.cpp -FILE: ../../../third_party/icu/source/tools/ctestfw/testdata.cpp -FILE: ../../../third_party/icu/source/tools/ctestfw/tstdtmod.cpp -FILE: ../../../third_party/icu/source/tools/ctestfw/ucln_ct.c -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/ctest.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/datamap.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/testdata.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/testlog.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/testtype.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/tstdtmod.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/uperf.h -FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/utimer.h -FILE: ../../../third_party/icu/source/tools/ctestfw/uperf.cpp -FILE: ../../../third_party/icu/source/tools/escapesrc/cptbl.h -FILE: ../../../third_party/icu/source/tools/escapesrc/escapesrc.cpp -FILE: ../../../third_party/icu/source/tools/escapesrc/expect-simple.cpp -FILE: ../../../third_party/icu/source/tools/escapesrc/tblgen.cpp -FILE: ../../../third_party/icu/source/tools/escapesrc/test-nochange.cpp -FILE: ../../../third_party/icu/source/tools/escapesrc/test-simple.cpp -FILE: ../../../third_party/icu/source/tools/genbrk/genbrk.1.in -FILE: ../../../third_party/icu/source/tools/genbrk/genbrk.cpp -FILE: ../../../third_party/icu/source/tools/genccode/genccode.8.in -FILE: ../../../third_party/icu/source/tools/genccode/genccode.c -FILE: ../../../third_party/icu/source/tools/gencfu/gencfu.1.in -FILE: ../../../third_party/icu/source/tools/gencfu/gencfu.cpp -FILE: ../../../third_party/icu/source/tools/gencmn/gencmn.8.in -FILE: ../../../third_party/icu/source/tools/gencmn/gencmn.c -FILE: ../../../third_party/icu/source/tools/gencnval/gencnval.1.in -FILE: ../../../third_party/icu/source/tools/gencnval/gencnval.c -FILE: ../../../third_party/icu/source/tools/gencolusb/extract_unsafe_backwards.cpp -FILE: ../../../third_party/icu/source/tools/gencolusb/verify_uset.cpp -FILE: ../../../third_party/icu/source/tools/gendict/gendict.1.in -FILE: ../../../third_party/icu/source/tools/gendict/gendict.cpp -FILE: ../../../third_party/icu/source/tools/gennorm2/extradata.cpp -FILE: ../../../third_party/icu/source/tools/gennorm2/extradata.h -FILE: ../../../third_party/icu/source/tools/gennorm2/gennorm2.cpp -FILE: ../../../third_party/icu/source/tools/gennorm2/n2builder.cpp -FILE: ../../../third_party/icu/source/tools/gennorm2/n2builder.h -FILE: ../../../third_party/icu/source/tools/gennorm2/norms.cpp -FILE: ../../../third_party/icu/source/tools/gennorm2/norms.h -FILE: ../../../third_party/icu/source/tools/genrb/derb.1.in -FILE: ../../../third_party/icu/source/tools/genrb/derb.cpp -FILE: ../../../third_party/icu/source/tools/genrb/errmsg.c -FILE: ../../../third_party/icu/source/tools/genrb/errmsg.h -FILE: ../../../third_party/icu/source/tools/genrb/filterrb.cpp -FILE: ../../../third_party/icu/source/tools/genrb/filterrb.h -FILE: ../../../third_party/icu/source/tools/genrb/genrb.1.in -FILE: ../../../third_party/icu/source/tools/genrb/genrb.cpp -FILE: ../../../third_party/icu/source/tools/genrb/genrb.h -FILE: ../../../third_party/icu/source/tools/genrb/parse.cpp -FILE: ../../../third_party/icu/source/tools/genrb/parse.h -FILE: ../../../third_party/icu/source/tools/genrb/prscmnts.cpp -FILE: ../../../third_party/icu/source/tools/genrb/prscmnts.h -FILE: ../../../third_party/icu/source/tools/genrb/rbutil.c -FILE: ../../../third_party/icu/source/tools/genrb/rbutil.h -FILE: ../../../third_party/icu/source/tools/genrb/read.c -FILE: ../../../third_party/icu/source/tools/genrb/read.h -FILE: ../../../third_party/icu/source/tools/genrb/reslist.cpp -FILE: ../../../third_party/icu/source/tools/genrb/reslist.h -FILE: ../../../third_party/icu/source/tools/genrb/rle.c -FILE: ../../../third_party/icu/source/tools/genrb/rle.h -FILE: ../../../third_party/icu/source/tools/genrb/ustr.c -FILE: ../../../third_party/icu/source/tools/genrb/ustr.h -FILE: ../../../third_party/icu/source/tools/genrb/wrtjava.cpp -FILE: ../../../third_party/icu/source/tools/genrb/wrtxml.cpp -FILE: ../../../third_party/icu/source/tools/gensprep/gensprep.8.in -FILE: ../../../third_party/icu/source/tools/gensprep/gensprep.c -FILE: ../../../third_party/icu/source/tools/gensprep/gensprep.h -FILE: ../../../third_party/icu/source/tools/gensprep/store.c -FILE: ../../../third_party/icu/source/tools/gentest/genres32.c -FILE: ../../../third_party/icu/source/tools/gentest/gentest.c -FILE: ../../../third_party/icu/source/tools/gentest/gentest.h -FILE: ../../../third_party/icu/source/tools/icuinfo/icuinfo.cpp -FILE: ../../../third_party/icu/source/tools/icuinfo/testplug.c -FILE: ../../../third_party/icu/source/tools/icupkg/icupkg.8.in -FILE: ../../../third_party/icu/source/tools/icupkg/icupkg.cpp -FILE: ../../../third_party/icu/source/tools/icuswap/icuswap.cpp -FILE: ../../../third_party/icu/source/tools/makeconv/gencnvex.c -FILE: ../../../third_party/icu/source/tools/makeconv/genmbcs.cpp -FILE: ../../../third_party/icu/source/tools/makeconv/genmbcs.h -FILE: ../../../third_party/icu/source/tools/makeconv/makeconv.1.in -FILE: ../../../third_party/icu/source/tools/makeconv/makeconv.cpp -FILE: ../../../third_party/icu/source/tools/makeconv/makeconv.h -FILE: ../../../third_party/icu/source/tools/makeconv/ucnvstat.c -FILE: ../../../third_party/icu/source/tools/pkgdata/pkgdata.1.in -FILE: ../../../third_party/icu/source/tools/pkgdata/pkgdata.cpp -FILE: ../../../third_party/icu/source/tools/pkgdata/pkgtypes.c -FILE: ../../../third_party/icu/source/tools/pkgdata/pkgtypes.h -FILE: ../../../third_party/icu/source/tools/toolutil/collationinfo.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/collationinfo.h -FILE: ../../../third_party/icu/source/tools/toolutil/dbgutil.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/dbgutil.h -FILE: ../../../third_party/icu/source/tools/toolutil/denseranges.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/denseranges.h -FILE: ../../../third_party/icu/source/tools/toolutil/filestrm.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/filestrm.h -FILE: ../../../third_party/icu/source/tools/toolutil/filetools.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/filetools.h -FILE: ../../../third_party/icu/source/tools/toolutil/flagparser.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/flagparser.h -FILE: ../../../third_party/icu/source/tools/toolutil/package.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/package.h -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_genc.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_genc.h -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_gencmn.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_gencmn.h -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_icu.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_icu.h -FILE: ../../../third_party/icu/source/tools/toolutil/pkg_imp.h -FILE: ../../../third_party/icu/source/tools/toolutil/pkgitems.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/ppucd.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/ppucd.h -FILE: ../../../third_party/icu/source/tools/toolutil/swapimpl.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/swapimpl.h -FILE: ../../../third_party/icu/source/tools/toolutil/toolutil.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/toolutil.h -FILE: ../../../third_party/icu/source/tools/toolutil/ucbuf.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/ucbuf.h -FILE: ../../../third_party/icu/source/tools/toolutil/ucln_tu.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/ucm.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/ucm.h -FILE: ../../../third_party/icu/source/tools/toolutil/ucmstate.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/udbgutil.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/udbgutil.h -FILE: ../../../third_party/icu/source/tools/toolutil/unewdata.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/unewdata.h -FILE: ../../../third_party/icu/source/tools/toolutil/uoptions.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/uoptions.h -FILE: ../../../third_party/icu/source/tools/toolutil/uparse.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/uparse.h -FILE: ../../../third_party/icu/source/tools/toolutil/writesrc.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/writesrc.h -FILE: ../../../third_party/icu/source/tools/toolutil/xmlparser.cpp -FILE: ../../../third_party/icu/source/tools/toolutil/xmlparser.h -FILE: ../../../third_party/icu/source/tools/tzcode/icuregions -FILE: ../../../third_party/icu/source/tools/tzcode/icuzdump.cpp -FILE: ../../../third_party/icu/source/tools/tzcode/icuzones -FILE: ../../../third_party/icu/source/tools/tzcode/tz2icu.cpp -FILE: ../../../third_party/icu/source/tools/tzcode/tz2icu.h ----------------------------------------------------------------------------------------------------- -Unicode® Terms of Use -For the general privacy policy governing access to this site, see the Unicode Privacy Policy. For trademark usage, see the Unicode® Consortium Name and Trademark Usage Policy. - -A. Unicode Copyright. -1. Copyright © 1991-2017 Unicode, Inc. All rights reserved. -2. Certain documents and files on this website contain a legend indicating that "Modification is permitted." Any person is hereby authorized, without fee, to modify such documents and files to create derivative works conforming to the Unicode® Standard, subject to Terms and Conditions herein. -3. Any person is hereby authorized, without fee, to view, use, reproduce, and distribute all documents and files solely for informational purposes and in the creation of products supporting the Unicode Standard, subject to the Terms and Conditions herein. -4. Further specifications of rights and restrictions pertaining to the use of the particular set of data files known as the "Unicode Character Database" can be found in the License. -5. Each version of the Unicode Standard has further specifications of rights and restrictions of use. For the book editions (Unicode 5.0 and earlier), these are found on the back of the title page. The online code charts carry specific restrictions. All other files, including online documentation of the core specification for Unicode 6.0 and later, are covered under these general Terms of Use. -6. No license is granted to "mirror" the Unicode website where a fee is charged for access to the "mirror" site. -7. Modification is not permitted with respect to this document. All copies of this document must be verbatim. -B. Restricted Rights Legend. Any technical data or software which is licensed to the United States of America, its agencies and/or instrumentalities under this Agreement is commercial technical data or commercial computer software developed exclusively at private expense as defined in FAR 2.101, or DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, duplication, or disclosure by the Government is subject to restrictions as set forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov 1995) and this Agreement. For Software, in accordance with FAR 12-212 or DFARS 227-7202, as applicable, use, duplication or disclosure by the Government is subject to the restrictions set forth in this Agreement. -C. Warranties and Disclaimers. -1. This publication and/or website may include technical or typographical errors or other inaccuracies . Changes are periodically added to the information herein; these changes will be incorporated in new editions of the publication and/or website. Unicode may make improvements and/or changes in the product(s) and/or program(s) described in this publication and/or website at any time. -2. If this file has been purchased on magnetic or optical media from Unicode, Inc. the sole and exclusive remedy for any claim will be exchange of the defective media within ninety (90) days of original purchase. -3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UNICODE AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR OMISSIONS IN THIS PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH ARE REFERENCED BY OR LINKED TO THIS PUBLICATION OR THE UNICODE WEBSITE. -D. Waiver of Damages. In no event shall Unicode or its licensors be liable for any special, incidental, indirect or consequential damages of any kind, or any damages whatsoever, whether or not Unicode was advised of the possibility of the damage, including, without limitation, those resulting from the following: loss of use, data or profits, in connection with the use, modification or distribution of this information or its derivatives. -E. Trademarks & Logos. -1. The Unicode Word Mark and the Unicode Logo are trademarks of Unicode, Inc. “The Unicode Consortium” and “Unicode, Inc.” are trade names of Unicode, Inc. Use of the information and materials found on this website indicates your acknowledgement of Unicode, Inc.’s exclusive worldwide rights in the Unicode Word Mark, the Unicode Logo, and the Unicode trade names. -2. The Unicode Consortium Name and Trademark Usage Policy (“Trademark Policy”) are incorporated herein by reference and you agree to abide by the provisions of the Trademark Policy, which may be changed from time to time in the sole discretion of Unicode, Inc. -3. All third party trademarks referenced herein are the property of their respective owners. -F. Miscellaneous. -1. Jurisdiction and Venue. This server is operated from a location in the State of California, United States of America. Unicode makes no representation that the materials are appropriate for use in other locations. If you access this server from other locations, you are responsible for compliance with local laws. This Agreement, all use of this site and any claims and damages resulting from use of this site are governed solely by the laws of the State of California without regard to any principles which would apply the laws of a different jurisdiction. The user agrees that any disputes regarding this site shall be resolved solely in the courts located in Santa Clara County, California. The user agrees said courts have personal jurisdiction and agree to waive any right to transfer the dispute to any other forum. -2. Modification by Unicode Unicode shall have the right to modify this Agreement at any time by posting it to this site. The user may not assign any part of this Agreement without Unicode’s prior written consent. -3. Taxes. The user agrees to pay any taxes arising from access to this website or use of the information herein, except for those based on Unicode’s net income. -4. Severability. If any provision of this Agreement is declared invalid or unenforceable, the remaining provisions of this Agreement shall remain in effect. -5. Entire Agreement. This Agreement constitutes the entire agreement between the parties. - -EXHIBIT 1 -UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE - -Unicode Data Files include all data files under the directories -http://www.unicode.org/Public/, http://www.unicode.org/reports/, -http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and -http://www.unicode.org/utility/trac/browser/. - -Unicode Data Files do not include PDF online code charts under the -directory http://www.unicode.org/Public/. - -Software includes any source code published in the Unicode Standard -or under the directories -http://www.unicode.org/Public/, http://www.unicode.org/reports/, -http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and -http://www.unicode.org/utility/trac/browser/. - -NOTICE TO USER: Carefully read the following legal agreement. -BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S -DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), -YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE -TERMS AND CONDITIONS OF THIS AGREEMENT. -IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE -THE DATA FILES OR SOFTWARE. - -COPYRIGHT AND PERMISSION NOTICE - -Copyright © 1991-2017 Unicode, Inc. All rights reserved. -Distributed under the Terms of Use in http://www.unicode.org/copyright.html. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Unicode data files and any associated documentation -(the "Data Files") or Unicode software and any associated documentation -(the "Software") to deal in the Data Files or Software -without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, and/or sell copies of -the Data Files or Software, and to permit persons to whom the Data Files -or Software are furnished to do so, provided that either -(a) this copyright and permission notice appear with all copies -of the Data Files or Software, or -(b) this copyright and permission notice appear in associated -Documentation. - -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT OF THIRD PARTY RIGHTS. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS -NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL -DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THE DATA FILES OR SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in these Data Files or Software without prior -written authorization of the copyright holder. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/source/data/mappings/iso-8859_10-1998.ucm -TYPE: LicenseType.unknown -FILE: ../../../third_party/icu/source/data/mappings/iso-8859_10-1998.ucm ----------------------------------------------------------------------------------------------------- -Copyright (c) 1999 Unicode, Inc. All Rights reserved. - Copyright (C) 2002-2005, International Business Machines - Corporation and others. All Rights Reserved. - -This file is provided as-is by Unicode, Inc. (The Unicode Consortium). -No claims are made as to fitness for any particular purpose. No -warranties of any kind are expressed or implied. The recipient -agrees to determine applicability of information provided. If this -file has been provided on optical media by Unicode, Inc., the sole -remedy for any claim will be exchange of defective media within 90 -days of receipt. - -Unicode, Inc. hereby grants the right to freely use the information -supplied in this file in the creation of products supporting the -Unicode Standard, and to make copies of this file in any form for -internal or external distribution as long as this notice remains -attached. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/source/data/mappings/iso-8859_11-2001.ucm -TYPE: LicenseType.unknown -FILE: ../../../third_party/icu/source/data/mappings/iso-8859_11-2001.ucm ----------------------------------------------------------------------------------------------------- -Copyright (c) 2002 Unicode, Inc. All Rights reserved. - Copyright (C) 2002-2005, International Business Machines - Corporation and others. All Rights Reserved. - -This file is provided as-is by Unicode, Inc. (The Unicode Consortium). -No claims are made as to fitness for any particular purpose. No -warranties of any kind are expressed or implied. The recipient -agrees to determine applicability of information provided. If this -file has been provided on optical media by Unicode, Inc., the sole -remedy for any claim will be exchange of defective media within 90 -days of receipt. - -Unicode, Inc. hereby grants the right to freely use the information -supplied in this file in the creation of products supporting the -Unicode Standard, and to make copies of this file in any form for -internal or external distribution as long as this notice remains -attached. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/source/data/mappings/iso-8859_14-1998.ucm -TYPE: LicenseType.unknown -FILE: ../../../third_party/icu/source/data/mappings/iso-8859_14-1998.ucm ----------------------------------------------------------------------------------------------------- -Copyright (c) 1998 - 1999 Unicode, Inc. All Rights reserved. - Copyright (C) 2002-2005, International Business Machines - Corporation and others. All Rights Reserved. - -This file is provided as-is by Unicode, Inc. (The Unicode Consortium). -No claims are made as to fitness for any particular purpose. No -warranties of any kind are expressed or implied. The recipient -agrees to determine applicability of information provided. If this -file has been provided on optical media by Unicode, Inc., the sole -remedy for any claim will be exchange of defective media within 90 -days of receipt. - -Unicode, Inc. hereby grants the right to freely use the information -supplied in this file in the creation of products supporting the -Unicode Standard, and to make copies of this file in any form for -internal or external distribution as long as this notice remains -attached. -==================================================================================================== - -==================================================================================================== -LIBRARY: icu -ORIGIN: ../../../third_party/icu/source/i18n/decContext.cpp + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/source/i18n/decContext.cpp ----------------------------------------------------------------------------------------------------- -Copyright (c) IBM Corporation, 2000-2012. 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: icu -ORIGIN: ../../../third_party/icu/source/i18n/decContext.h + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/source/i18n/decContext.h ----------------------------------------------------------------------------------------------------- -Copyright (c) IBM Corporation, 2000-2011. 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: icu -ORIGIN: ../../../third_party/icu/source/i18n/decNumber.cpp + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/source/i18n/decNumber.cpp ----------------------------------------------------------------------------------------------------- -Copyright (c) IBM Corporation, 2000-2014. 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: icu -ORIGIN: ../../../third_party/icu/source/i18n/decNumber.h + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/source/i18n/decNumber.h ----------------------------------------------------------------------------------------------------- -Copyright (c) IBM Corporation, 2000-2010. 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: icu -ORIGIN: ../../../third_party/icu/source/i18n/decNumberLocal.h + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING -TYPE: LicenseType.bsd -FILE: ../../../third_party/icu/source/i18n/decNumberLocal.h ----------------------------------------------------------------------------------------------------- -Copyright (c) IBM Corporation, 2000-2016. 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: khronos -ORIGIN: ../../../third_party/khronos/KHR/khrplatform.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/khronos/KHR/khrplatform.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2008-2009 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - -==================================================================================================== -LIBRARY: khronos -ORIGIN: ../../../third_party/khronos/LICENSE -TYPE: LicenseType.mit -FILE: ../../../third_party/khronos/DEPS ----------------------------------------------------------------------------------------------------- -Copyright (c) 2007-2010 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. - -SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) - -Copyright (C) 1992 Silicon Graphics, Inc. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice including the dates of first publication and either -this permission notice or a reference to http://oss.sgi.com/projects/FreeB -shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON -GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of Silicon Graphics, Inc. shall -not be used in advertising or otherwise to promote the sale, use or other -dealings in this Software without prior written authorization from Silicon -Graphics, Inc. -==================================================================================================== - -==================================================================================================== -LIBRARY: khronos -ORIGIN: ../../../third_party/khronos/noninclude/GL/glxext.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/khronos/noninclude/GL/glxext.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2007-2012 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - -==================================================================================================== -LIBRARY: libcxx -LIBRARY: libcxxabi -ORIGIN: null -TYPE: LicenseType.bsd -FILE: ../../../third_party/libcxx/.arcconfig -FILE: ../../../third_party/libcxx/appveyor-reqs-install.cmd -FILE: ../../../third_party/libcxx/appveyor.yml -FILE: ../../../third_party/libcxx/benchmarks/CartesianBenchmarks.hpp -FILE: ../../../third_party/libcxx/benchmarks/ContainerBenchmarks.hpp -FILE: ../../../third_party/libcxx/benchmarks/GenerateInput.hpp -FILE: ../../../third_party/libcxx/benchmarks/algorithms.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/algorithms.partition_point.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/filesystem.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/function.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/lit.site.cfg.py.in -FILE: ../../../third_party/libcxx/benchmarks/ordered_set.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/string.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/stringstream.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/unordered_set_operations.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/util_smartptr.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/vector_operations.bench.cpp -FILE: ../../../third_party/libcxx/docs/BuildingLibcxx.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ABIVersioning.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/AvailabilityMarkup.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/CapturingConfigInfo.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/DebugMode.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ExperimentalFeatures.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/FeatureTestMacros.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/FileTimeType.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ThreadingSupportAPI.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/VisibilityMacros.rst -FILE: ../../../third_party/libcxx/docs/FeatureTestMacroTable.rst -FILE: ../../../third_party/libcxx/docs/Makefile.sphinx -FILE: ../../../third_party/libcxx/docs/ReleaseNotes.rst -FILE: ../../../third_party/libcxx/docs/TestingLibcxx.rst -FILE: ../../../third_party/libcxx/docs/UsingLibcxx.rst -FILE: ../../../third_party/libcxx/docs/index.rst -FILE: ../../../third_party/libcxx/fuzzing/fuzz_test.cpp -FILE: ../../../third_party/libcxx/fuzzing/fuzzing.cpp -FILE: ../../../third_party/libcxx/fuzzing/fuzzing.h -FILE: ../../../third_party/libcxx/include/__bit_reference -FILE: ../../../third_party/libcxx/include/__bsd_locale_defaults.h -FILE: ../../../third_party/libcxx/include/__bsd_locale_fallbacks.h -FILE: ../../../third_party/libcxx/include/__config -FILE: ../../../third_party/libcxx/include/__config_site.in -FILE: ../../../third_party/libcxx/include/__debug -FILE: ../../../third_party/libcxx/include/__errc -FILE: ../../../third_party/libcxx/include/__functional_03 -FILE: ../../../third_party/libcxx/include/__functional_base -FILE: ../../../third_party/libcxx/include/__functional_base_03 -FILE: ../../../third_party/libcxx/include/__hash_table -FILE: ../../../third_party/libcxx/include/__libcpp_version -FILE: ../../../third_party/libcxx/include/__locale -FILE: ../../../third_party/libcxx/include/__mutex_base -FILE: ../../../third_party/libcxx/include/__node_handle -FILE: ../../../third_party/libcxx/include/__nullptr -FILE: ../../../third_party/libcxx/include/__split_buffer -FILE: ../../../third_party/libcxx/include/__sso_allocator -FILE: ../../../third_party/libcxx/include/__std_stream -FILE: ../../../third_party/libcxx/include/__string -FILE: ../../../third_party/libcxx/include/__threading_support -FILE: ../../../third_party/libcxx/include/__tree -FILE: ../../../third_party/libcxx/include/__tuple -FILE: ../../../third_party/libcxx/include/__undef_macros -FILE: ../../../third_party/libcxx/include/algorithm -FILE: ../../../third_party/libcxx/include/any -FILE: ../../../third_party/libcxx/include/array -FILE: ../../../third_party/libcxx/include/atomic -FILE: ../../../third_party/libcxx/include/bit -FILE: ../../../third_party/libcxx/include/bitset -FILE: ../../../third_party/libcxx/include/cassert -FILE: ../../../third_party/libcxx/include/ccomplex -FILE: ../../../third_party/libcxx/include/cctype -FILE: ../../../third_party/libcxx/include/cerrno -FILE: ../../../third_party/libcxx/include/cfenv -FILE: ../../../third_party/libcxx/include/cfloat -FILE: ../../../third_party/libcxx/include/charconv -FILE: ../../../third_party/libcxx/include/chrono -FILE: ../../../third_party/libcxx/include/cinttypes -FILE: ../../../third_party/libcxx/include/ciso646 -FILE: ../../../third_party/libcxx/include/climits -FILE: ../../../third_party/libcxx/include/clocale -FILE: ../../../third_party/libcxx/include/cmath -FILE: ../../../third_party/libcxx/include/codecvt -FILE: ../../../third_party/libcxx/include/compare -FILE: ../../../third_party/libcxx/include/complex -FILE: ../../../third_party/libcxx/include/complex.h -FILE: ../../../third_party/libcxx/include/condition_variable -FILE: ../../../third_party/libcxx/include/csetjmp -FILE: ../../../third_party/libcxx/include/csignal -FILE: ../../../third_party/libcxx/include/cstdarg -FILE: ../../../third_party/libcxx/include/cstdbool -FILE: ../../../third_party/libcxx/include/cstddef -FILE: ../../../third_party/libcxx/include/cstdint -FILE: ../../../third_party/libcxx/include/cstdio -FILE: ../../../third_party/libcxx/include/cstdlib -FILE: ../../../third_party/libcxx/include/cstring -FILE: ../../../third_party/libcxx/include/ctgmath -FILE: ../../../third_party/libcxx/include/ctime -FILE: ../../../third_party/libcxx/include/ctype.h -FILE: ../../../third_party/libcxx/include/cwchar -FILE: ../../../third_party/libcxx/include/cwctype -FILE: ../../../third_party/libcxx/include/deque -FILE: ../../../third_party/libcxx/include/errno.h -FILE: ../../../third_party/libcxx/include/exception -FILE: ../../../third_party/libcxx/include/experimental/__config -FILE: ../../../third_party/libcxx/include/experimental/__memory -FILE: ../../../third_party/libcxx/include/experimental/algorithm -FILE: ../../../third_party/libcxx/include/experimental/coroutine -FILE: ../../../third_party/libcxx/include/experimental/deque -FILE: ../../../third_party/libcxx/include/experimental/filesystem -FILE: ../../../third_party/libcxx/include/experimental/forward_list -FILE: ../../../third_party/libcxx/include/experimental/functional -FILE: ../../../third_party/libcxx/include/experimental/iterator -FILE: ../../../third_party/libcxx/include/experimental/list -FILE: ../../../third_party/libcxx/include/experimental/map -FILE: ../../../third_party/libcxx/include/experimental/memory_resource -FILE: ../../../third_party/libcxx/include/experimental/propagate_const -FILE: ../../../third_party/libcxx/include/experimental/regex -FILE: ../../../third_party/libcxx/include/experimental/set -FILE: ../../../third_party/libcxx/include/experimental/simd -FILE: ../../../third_party/libcxx/include/experimental/string -FILE: ../../../third_party/libcxx/include/experimental/type_traits -FILE: ../../../third_party/libcxx/include/experimental/unordered_map -FILE: ../../../third_party/libcxx/include/experimental/unordered_set -FILE: ../../../third_party/libcxx/include/experimental/utility -FILE: ../../../third_party/libcxx/include/experimental/vector -FILE: ../../../third_party/libcxx/include/ext/__hash -FILE: ../../../third_party/libcxx/include/ext/hash_map -FILE: ../../../third_party/libcxx/include/ext/hash_set -FILE: ../../../third_party/libcxx/include/fenv.h -FILE: ../../../third_party/libcxx/include/filesystem -FILE: ../../../third_party/libcxx/include/float.h -FILE: ../../../third_party/libcxx/include/forward_list -FILE: ../../../third_party/libcxx/include/fstream -FILE: ../../../third_party/libcxx/include/functional -FILE: ../../../third_party/libcxx/include/future -FILE: ../../../third_party/libcxx/include/initializer_list -FILE: ../../../third_party/libcxx/include/inttypes.h -FILE: ../../../third_party/libcxx/include/iomanip -FILE: ../../../third_party/libcxx/include/ios -FILE: ../../../third_party/libcxx/include/iosfwd -FILE: ../../../third_party/libcxx/include/iostream -FILE: ../../../third_party/libcxx/include/istream -FILE: ../../../third_party/libcxx/include/iterator -FILE: ../../../third_party/libcxx/include/limits -FILE: ../../../third_party/libcxx/include/limits.h -FILE: ../../../third_party/libcxx/include/list -FILE: ../../../third_party/libcxx/include/locale -FILE: ../../../third_party/libcxx/include/locale.h -FILE: ../../../third_party/libcxx/include/map -FILE: ../../../third_party/libcxx/include/math.h -FILE: ../../../third_party/libcxx/include/memory -FILE: ../../../third_party/libcxx/include/module.modulemap -FILE: ../../../third_party/libcxx/include/mutex -FILE: ../../../third_party/libcxx/include/new -FILE: ../../../third_party/libcxx/include/numeric -FILE: ../../../third_party/libcxx/include/optional -FILE: ../../../third_party/libcxx/include/ostream -FILE: ../../../third_party/libcxx/include/queue -FILE: ../../../third_party/libcxx/include/random -FILE: ../../../third_party/libcxx/include/ratio -FILE: ../../../third_party/libcxx/include/regex -FILE: ../../../third_party/libcxx/include/scoped_allocator -FILE: ../../../third_party/libcxx/include/set -FILE: ../../../third_party/libcxx/include/setjmp.h -FILE: ../../../third_party/libcxx/include/shared_mutex -FILE: ../../../third_party/libcxx/include/span -FILE: ../../../third_party/libcxx/include/sstream -FILE: ../../../third_party/libcxx/include/stack -FILE: ../../../third_party/libcxx/include/stdbool.h -FILE: ../../../third_party/libcxx/include/stddef.h -FILE: ../../../third_party/libcxx/include/stdexcept -FILE: ../../../third_party/libcxx/include/stdint.h -FILE: ../../../third_party/libcxx/include/stdio.h -FILE: ../../../third_party/libcxx/include/stdlib.h -FILE: ../../../third_party/libcxx/include/streambuf -FILE: ../../../third_party/libcxx/include/string -FILE: ../../../third_party/libcxx/include/string.h -FILE: ../../../third_party/libcxx/include/string_view -FILE: ../../../third_party/libcxx/include/strstream -FILE: ../../../third_party/libcxx/include/support/android/locale_bionic.h -FILE: ../../../third_party/libcxx/include/support/fuchsia/xlocale.h -FILE: ../../../third_party/libcxx/include/support/ibm/limits.h -FILE: ../../../third_party/libcxx/include/support/ibm/locale_mgmt_aix.h -FILE: ../../../third_party/libcxx/include/support/ibm/support.h -FILE: ../../../third_party/libcxx/include/support/ibm/xlocale.h -FILE: ../../../third_party/libcxx/include/support/musl/xlocale.h -FILE: ../../../third_party/libcxx/include/support/newlib/xlocale.h -FILE: ../../../third_party/libcxx/include/support/solaris/floatingpoint.h -FILE: ../../../third_party/libcxx/include/support/solaris/wchar.h -FILE: ../../../third_party/libcxx/include/support/solaris/xlocale.h -FILE: ../../../third_party/libcxx/include/support/win32/limits_msvc_win32.h -FILE: ../../../third_party/libcxx/include/support/win32/locale_win32.h -FILE: ../../../third_party/libcxx/include/support/xlocale/__nop_locale_mgmt.h -FILE: ../../../third_party/libcxx/include/support/xlocale/__posix_l_fallback.h -FILE: ../../../third_party/libcxx/include/support/xlocale/__strtonum_fallback.h -FILE: ../../../third_party/libcxx/include/system_error -FILE: ../../../third_party/libcxx/include/tgmath.h -FILE: ../../../third_party/libcxx/include/thread -FILE: ../../../third_party/libcxx/include/tuple -FILE: ../../../third_party/libcxx/include/type_traits -FILE: ../../../third_party/libcxx/include/typeindex -FILE: ../../../third_party/libcxx/include/typeinfo -FILE: ../../../third_party/libcxx/include/unordered_map -FILE: ../../../third_party/libcxx/include/unordered_set -FILE: ../../../third_party/libcxx/include/utility -FILE: ../../../third_party/libcxx/include/valarray -FILE: ../../../third_party/libcxx/include/variant -FILE: ../../../third_party/libcxx/include/vector -FILE: ../../../third_party/libcxx/include/version -FILE: ../../../third_party/libcxx/include/wchar.h -FILE: ../../../third_party/libcxx/include/wctype.h -FILE: ../../../third_party/libcxx/lib/abi/3.9/x86_64-apple-darwin16.abilist -FILE: ../../../third_party/libcxx/lib/abi/3.9/x86_64-linux-gnu.abilist -FILE: ../../../third_party/libcxx/lib/abi/4.0/x86_64-apple-darwin16.abilist -FILE: ../../../third_party/libcxx/lib/abi/4.0/x86_64-unknown-linux-gnu.abilist -FILE: ../../../third_party/libcxx/lib/abi/5.0/x86_64-apple-darwin16.abilist -FILE: ../../../third_party/libcxx/lib/abi/5.0/x86_64-unknown-linux-gnu.abilist -FILE: ../../../third_party/libcxx/lib/abi/6.0/x86_64-apple-darwin16.abilist -FILE: ../../../third_party/libcxx/lib/abi/6.0/x86_64-unknown-linux-gnu.abilist -FILE: ../../../third_party/libcxx/lib/abi/8.0/x86_64-apple-darwin.v1.abilist -FILE: ../../../third_party/libcxx/lib/abi/8.0/x86_64-apple-darwin.v2.abilist -FILE: ../../../third_party/libcxx/lib/abi/8.0/x86_64-unknown-linux-gnu.v1.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.v1.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.v2.abilist -FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.v1.abilist -FILE: ../../../third_party/libcxx/lib/libc++abi-new-delete.exp -FILE: ../../../third_party/libcxx/lib/libc++abi.v1.exp -FILE: ../../../third_party/libcxx/lib/libc++abi.v2.exp -FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.v1.exp -FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.v2.exp -FILE: ../../../third_party/libcxx/lib/libc++unexp.exp -FILE: ../../../third_party/libcxx/lib/notweak.exp -FILE: ../../../third_party/libcxx/lib/weak.exp -FILE: ../../../third_party/libcxx/src/algorithm.cpp -FILE: ../../../third_party/libcxx/src/any.cpp -FILE: ../../../third_party/libcxx/src/bind.cpp -FILE: ../../../third_party/libcxx/src/charconv.cpp -FILE: ../../../third_party/libcxx/src/chrono.cpp -FILE: ../../../third_party/libcxx/src/condition_variable.cpp -FILE: ../../../third_party/libcxx/src/condition_variable_destructor.cpp -FILE: ../../../third_party/libcxx/src/debug.cpp -FILE: ../../../third_party/libcxx/src/exception.cpp -FILE: ../../../third_party/libcxx/src/experimental/memory_resource.cpp -FILE: ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp -FILE: ../../../third_party/libcxx/src/filesystem/filesystem_common.h -FILE: ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp -FILE: ../../../third_party/libcxx/src/filesystem/operations.cpp -FILE: ../../../third_party/libcxx/src/functional.cpp -FILE: ../../../third_party/libcxx/src/future.cpp -FILE: ../../../third_party/libcxx/src/hash.cpp -FILE: ../../../third_party/libcxx/src/include/apple_availability.h -FILE: ../../../third_party/libcxx/src/include/atomic_support.h -FILE: ../../../third_party/libcxx/src/include/config_elast.h -FILE: ../../../third_party/libcxx/src/include/refstring.h -FILE: ../../../third_party/libcxx/src/ios.cpp -FILE: ../../../third_party/libcxx/src/iostream.cpp -FILE: ../../../third_party/libcxx/src/locale.cpp -FILE: ../../../third_party/libcxx/src/memory.cpp -FILE: ../../../third_party/libcxx/src/mutex.cpp -FILE: ../../../third_party/libcxx/src/mutex_destructor.cpp -FILE: ../../../third_party/libcxx/src/new.cpp -FILE: ../../../third_party/libcxx/src/optional.cpp -FILE: ../../../third_party/libcxx/src/random.cpp -FILE: ../../../third_party/libcxx/src/regex.cpp -FILE: ../../../third_party/libcxx/src/shared_mutex.cpp -FILE: ../../../third_party/libcxx/src/stdexcept.cpp -FILE: ../../../third_party/libcxx/src/string.cpp -FILE: ../../../third_party/libcxx/src/strstream.cpp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp -FILE: ../../../third_party/libcxx/src/support/win32/locale_win32.cpp -FILE: ../../../third_party/libcxx/src/support/win32/support.cpp -FILE: ../../../third_party/libcxx/src/support/win32/thread_win32.cpp -FILE: ../../../third_party/libcxx/src/system_error.cpp -FILE: ../../../third_party/libcxx/src/thread.cpp -FILE: ../../../third_party/libcxx/src/typeinfo.cpp -FILE: ../../../third_party/libcxx/src/utility.cpp -FILE: ../../../third_party/libcxx/src/valarray.cpp -FILE: ../../../third_party/libcxx/src/variant.cpp -FILE: ../../../third_party/libcxx/src/vector.cpp -FILE: ../../../third_party/libcxx/www/atomic_design.html -FILE: ../../../third_party/libcxx/www/atomic_design_a.html -FILE: ../../../third_party/libcxx/www/atomic_design_b.html -FILE: ../../../third_party/libcxx/www/atomic_design_c.html -FILE: ../../../third_party/libcxx/www/cxx1y_status.html -FILE: ../../../third_party/libcxx/www/cxx1z_status.html -FILE: ../../../third_party/libcxx/www/cxx2a_status.html -FILE: ../../../third_party/libcxx/www/index.html -FILE: ../../../third_party/libcxx/www/ts1z_status.html -FILE: ../../../third_party/libcxx/www/type_traits_design.html -FILE: ../../../third_party/libcxx/www/upcoming_meeting.html -FILE: ../../../third_party/libcxxabi/.arcconfig -FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp -FILE: ../../../third_party/libcxxabi/include/__cxxabi_config.h -FILE: ../../../third_party/libcxxabi/include/cxxabi.h -FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp -FILE: ../../../third_party/libcxxabi/lib/new-delete.exp -FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp -FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp -FILE: ../../../third_party/libcxxabi/src/abort_message.cpp -FILE: ../../../third_party/libcxxabi/src/abort_message.h -FILE: ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_demangle.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception.hpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_guard.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_guard_impl.h -FILE: ../../../third_party/libcxxabi/src/cxa_handlers.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_handlers.hpp -FILE: ../../../third_party/libcxxabi/src/cxa_noexception.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_personality.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_unexpected.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_vector.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_virtual.cpp -FILE: ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h -FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h -FILE: ../../../third_party/libcxxabi/src/demangle/StringView.h -FILE: ../../../third_party/libcxxabi/src/demangle/Utility.h -FILE: ../../../third_party/libcxxabi/src/fallback_malloc.cpp -FILE: ../../../third_party/libcxxabi/src/fallback_malloc.h -FILE: ../../../third_party/libcxxabi/src/include/atomic_support.h -FILE: ../../../third_party/libcxxabi/src/include/refstring.h -FILE: ../../../third_party/libcxxabi/src/private_typeinfo.cpp -FILE: ../../../third_party/libcxxabi/src/private_typeinfo.h -FILE: ../../../third_party/libcxxabi/src/stdlib_exception.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp -FILE: ../../../third_party/libcxxabi/www/index.html -FILE: ../../../third_party/libcxxabi/www/spec.html +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/include/GLES/gl.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/GLES/gl.h +FILE: ../../../third_party/angle/include/WGL/wgl.h +FILE: ../../../third_party/khronos/GLES2/gl2.h +FILE: ../../../third_party/khronos/GLES2/gl2ext.h +FILE: ../../../third_party/khronos/GLES3/gl3.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/include/GLES/glext.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/GLES/glext.h +FILE: ../../../third_party/khronos/EGL/egl.h +FILE: ../../../third_party/khronos/EGL/eglext.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2017 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/src/third_party/khronos/GL/wglext.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/src/third_party/khronos/GL/wglext.h +FILE: ../../../third_party/khronos/noninclude/GL/glext.h +FILE: ../../../third_party/khronos/noninclude/GL/wglext.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2014 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: rapidjson +ORIGIN: ../../../third_party/angle/include/GLES2/gl2.h +TYPE: LicenseType.mit +FILE: ../../../third_party/angle/include/GLES2/gl2.h +FILE: ../../../third_party/angle/include/GLES2/gl2ext.h +FILE: ../../../third_party/angle/include/GLES3/gl3.h +FILE: ../../../third_party/angle/include/GLES3/gl31.h +FILE: ../../../third_party/angle/include/GLES3/gl32.h +FILE: ../../../third_party/rapidjson/include/rapidjson/allocators.h +FILE: ../../../third_party/rapidjson/include/rapidjson/cursorstreamwrapper.h +FILE: ../../../third_party/rapidjson/include/rapidjson/document.h +FILE: ../../../third_party/rapidjson/include/rapidjson/encodedstream.h +FILE: ../../../third_party/rapidjson/include/rapidjson/encodings.h +FILE: ../../../third_party/rapidjson/include/rapidjson/error/en.h +FILE: ../../../third_party/rapidjson/include/rapidjson/error/error.h +FILE: ../../../third_party/rapidjson/include/rapidjson/filereadstream.h +FILE: ../../../third_party/rapidjson/include/rapidjson/filewritestream.h +FILE: ../../../third_party/rapidjson/include/rapidjson/fwd.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/biginteger.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/diyfp.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/dtoa.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/ieee754.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/itoa.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/meta.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/pow10.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/regex.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/stack.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/strfunc.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/strtod.h +FILE: ../../../third_party/rapidjson/include/rapidjson/internal/swap.h +FILE: ../../../third_party/rapidjson/include/rapidjson/istreamwrapper.h +FILE: ../../../third_party/rapidjson/include/rapidjson/memorybuffer.h +FILE: ../../../third_party/rapidjson/include/rapidjson/memorystream.h +FILE: ../../../third_party/rapidjson/include/rapidjson/ostreamwrapper.h +FILE: ../../../third_party/rapidjson/include/rapidjson/pointer.h +FILE: ../../../third_party/rapidjson/include/rapidjson/prettywriter.h +FILE: ../../../third_party/rapidjson/include/rapidjson/rapidjson.h +FILE: ../../../third_party/rapidjson/include/rapidjson/reader.h +FILE: ../../../third_party/rapidjson/include/rapidjson/schema.h +FILE: ../../../third_party/rapidjson/include/rapidjson/stream.h +FILE: ../../../third_party/rapidjson/include/rapidjson/stringbuffer.h +FILE: ../../../third_party/rapidjson/include/rapidjson/writer.h +---------------------------------------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: xxhash +ORIGIN: ../../../third_party/angle/include/platform/Feature.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/WATCHLISTS +FILE: ../../../third_party/angle/include/platform/Feature.h +FILE: ../../../third_party/angle/include/platform/FeaturesMtl.h +FILE: ../../../third_party/angle/include/vulkan/vulkan_fuchsia_ext.h +FILE: ../../../third_party/angle/samples/capture_replay/CaptureReplay.cpp +FILE: ../../../third_party/angle/scripts/entry_point_packed_gl_enums.json +FILE: ../../../third_party/angle/src/common/PoolAlloc.cpp +FILE: ../../../third_party/angle/src/common/PoolAlloc.h +FILE: ../../../third_party/angle/src/common/PoolAlloc_unittest.cpp +FILE: ../../../third_party/angle/src/common/apple_platform_utils.h +FILE: ../../../third_party/angle/src/common/system_utils_unittest.cpp +FILE: ../../../third_party/angle/src/common/system_utils_win32.cpp +FILE: ../../../third_party/angle/src/common/system_utils_winuwp.cpp +FILE: ../../../third_party/angle/src/common/third_party/xxhash/xxhash_fuzzer.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.l +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.y +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_lex_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_tab_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/BuiltinsWorkaroundGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/BuiltinsWorkaroundGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSLForMetal.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputVulkanGLSLForMetal.mm +FILE: ../../../third_party/angle/src/compiler/translator/ParseContext_interm.h +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorMetal.cpp +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorMetal.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateAST.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateAST.h +FILE: ../../../third_party/angle/src/compiler/translator/glslang.l +FILE: ../../../third_party/angle/src/compiler/translator/glslang.y +FILE: ../../../third_party/angle/src/compiler/translator/glslang_lex_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/glslang_tab_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/span_unittest.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/RewriteRowMajorMatrices.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/RewriteRowMajorMatrices.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RemoveInactiveInterfaceVariables.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RemoveInactiveInterfaceVariables.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteArrayOfArrayOfOpaqueUniforms.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteAtomicCounters.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteAtomicCounters.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteCubeMapSamplersAs2DArray.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteCubeMapSamplersAs2DArray.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteDfdy.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteDfdy.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/BuiltIn.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindFunction.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindFunction.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceShadowingVariables.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gl.cpp +FILE: ../../../third_party/angle/src/libANGLE/EGLSync.h +FILE: ../../../third_party/angle/src/libANGLE/MemoryObject.cpp +FILE: ../../../third_party/angle/src/libANGLE/MemoryObject.h +FILE: ../../../third_party/angle/src/libANGLE/Overlay.cpp +FILE: ../../../third_party/angle/src/libANGLE/Overlay.h +FILE: ../../../third_party/angle/src/libANGLE/OverlayWidgets.cpp +FILE: ../../../third_party/angle/src/libANGLE/OverlayWidgets.h +FILE: ../../../third_party/angle/src/libANGLE/Overlay_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/Overlay_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Overlay_font_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/Overlay_font_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Semaphore.cpp +FILE: ../../../third_party/angle/src/libANGLE/Semaphore.h +FILE: ../../../third_party/angle/src/libANGLE/capture/FrameCapture.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/FrameCapture.h +FILE: ../../../third_party/angle/src/libANGLE/capture/FrameCapture_mock.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_1_0_params.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_2_0_params.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_0_params.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_1_params.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_2_params.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_ext_params.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/gl_enum_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/gl_enum_utils.h +FILE: ../../../third_party/angle/src/libANGLE/capture/gl_enum_utils_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/gl_enum_utils_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/format_map_desktop.cpp +FILE: ../../../third_party/angle/src/libANGLE/overlay_widgets.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLSyncImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/MemoryObjectImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/OverlayImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/SemaphoreImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ShaderImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicImage2DHLSL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicImage2DHLSL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/MappedSubresourceVerifier11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/MappedSubresourceVerifier11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/driver_utils_mac.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/MemoryObjectGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/MemoryObjectGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SemaphoreGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SemaphoreGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/ContextCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DeviceCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/RendererCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/SyncEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/RendererGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/glslang_wrapper_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/glslang_wrapper_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/BufferMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/CompilerMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/CompilerMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ContextMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ContextMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/DisplayMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/DisplayMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/DisplayMtl_api.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/FrameBufferMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/FrameBufferMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ProgramMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderBufferMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderBufferMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderTargetMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/RenderTargetMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ShaderMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/ShaderMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SurfaceMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SurfaceMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TextureMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TextureMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/VertexArrayMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/VertexArrayMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_buffer_pool.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_buffer_pool.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_command_buffer.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_command_buffer.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_common.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_common.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_map.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_utils.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_glslang_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_glslang_utils.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_render_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_render_utils.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_resources.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_resources.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_state_cache.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_state_cache.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_utils.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/serial_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/serial_utils_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DisplayVk_api.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/MemoryObjectVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/OverlayVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/OverlayVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/PersistentCommandPool.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/PersistentCommandPool.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SecondaryCommandBuffer.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SemaphoreVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/SemaphoreVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/WindowSurfaceVkFuchsia.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/fuchsia/WindowSurfaceVkFuchsia.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/DisplayVkGGP.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/DisplayVkGGP.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/WindowSurfaceVkGGP.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ggp/WindowSurfaceVkGGP.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/DisplayVkMac.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/DisplayVkMac.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/WindowSurfaceVkMac.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/WindowSurfaceVkMac.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolve.frag +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolve.frag.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolveStencilNoExport.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/BlitResolveStencilNoExport.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndex.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndex.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndexIndirectLineLoop.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndirectLineLoop.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndirectLineLoop.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayCull.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayCull.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_wrapper.h +FILE: ../../../third_party/angle/src/libANGLE/trace.h +FILE: ../../../third_party/angle/src/libANGLE/validationES32.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationES32.h +FILE: ../../../third_party/angle/src/libANGLE/validationESEXT.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationGL1.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationGL2.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationGL3.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationGL4.cpp +FILE: ../../../third_party/angle/src/libGL/entry_points_wgl.cpp +FILE: ../../../third_party/angle/src/libGL/entry_points_wgl.h +FILE: ../../../third_party/angle/src/libGL/proc_table_wgl_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/proc_table_egl_autogen.cpp +FILE: ../../../third_party/angle/util/Timer.cpp +FILE: ../../../third_party/angle/util/Timer.h +FILE: ../../../third_party/angle/util/fuchsia/ScenicWindow.cpp +FILE: ../../../third_party/angle/util/fuchsia/ScenicWindow.h +FILE: ../../../third_party/angle/util/posix/crash_handler_posix.cpp +FILE: ../../../third_party/angle/util/test_utils.cpp +FILE: ../../../third_party/angle/util/test_utils_unittest.cpp +FILE: ../../../third_party/angle/util/test_utils_unittest_helper.cpp +FILE: ../../../third_party/angle/util/test_utils_unittest_helper.h +FILE: ../../../third_party/angle/util/windows/test_utils_winuwp.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2019 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/.style.yapf +FILE: ../../../third_party/angle/.vpython +FILE: ../../../third_party/angle/.vpython3 +FILE: ../../../third_party/angle/.yapfignore +FILE: ../../../third_party/angle/CONTRIBUTORS +FILE: ../../../third_party/angle/DEPS +FILE: ../../../third_party/angle/DIR_METADATA +FILE: ../../../third_party/angle/additional_readme_paths.json +FILE: ../../../third_party/angle/doc/img/JobUnit.png +FILE: ../../../third_party/angle/doc/img/RegressionTestingArchitecture.png +FILE: ../../../third_party/angle/doc/img/StateChangeNotificationFlow.svg +FILE: ../../../third_party/angle/doc/img/StateNotificationExample.svg +FILE: ../../../third_party/angle/infra/config/generated/commit-queue.cfg +FILE: ../../../third_party/angle/infra/config/generated/cr-buildbucket.cfg +FILE: ../../../third_party/angle/infra/config/generated/luci-logdog.cfg +FILE: ../../../third_party/angle/infra/config/generated/luci-milo.cfg +FILE: ../../../third_party/angle/infra/config/generated/luci-scheduler.cfg +FILE: ../../../third_party/angle/infra/config/generated/project.cfg +FILE: ../../../third_party/angle/samples/multi_texture/basemap.tga +FILE: ../../../third_party/angle/samples/multi_texture/lightmap.tga +FILE: ../../../third_party/angle/samples/multiple_draw_buffers/multiple_draw_buffers_copy_fs.glsl +FILE: ../../../third_party/angle/samples/multiple_draw_buffers/multiple_draw_buffers_fs.glsl +FILE: ../../../third_party/angle/samples/multiple_draw_buffers/multiple_draw_buffers_vs.glsl +FILE: ../../../third_party/angle/samples/particle_system/smoke.tga +FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_format.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_load_functions_table.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_shader_preprocessor.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/ANGLE_shader_translator.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/D3D11_blit_shader_selection.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/D3D11_format.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/DXGI_format.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/DXGI_format_support.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Emulated_HLSL_functions.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_CTS_(dEQP)_build_files.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_EGL_WGL_loader.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_EGL_entry_points.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_copy_conversion_table.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/GL_format_map.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/GLenum_value_to_string_map.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Metal_default_shaders.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Metal_format_table.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/OpenGL_dispatch_table.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/SPIR-V_helpers.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Static_builtins.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Vulkan_format.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Vulkan_internal_shader_programs.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/Vulkan_mandatory_format_support_table.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/overlay_fonts.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/overlay_widgets.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/packed_enum.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/proc_table.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/restricted_traces.json +FILE: ../../../third_party/angle/scripts/code_generation_hashes/uniform_type.json +FILE: ../../../third_party/angle/src/android_system_settings/assets/a4a_rules.json +FILE: ../../../third_party/angle/src/android_system_settings/res/drawable/icon.png +FILE: ../../../third_party/angle/src/common/packed_egl_enums.json +FILE: ../../../third_party/angle/src/common/packed_gl_enums.json +FILE: ../../../third_party/angle/src/compiler/translator/emulated_builtin_function_data_hlsl.json +FILE: ../../../third_party/angle/src/feature_support_util/a4a_rules.json +FILE: ../../../third_party/angle/src/libANGLE/es3_copy_conversion_formats.json +FILE: ../../../third_party/angle/src/libANGLE/es3_format_type_combinations.json +FILE: ../../../third_party/angle/src/libANGLE/format_map_data.json +FILE: ../../../third_party/angle/src/libANGLE/overlay/DejaVuSansMono-Bold.ttf +FILE: ../../../third_party/angle/src/libANGLE/renderer/angle_format_data.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/angle_format_map.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/BufferToTexture11.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Passthrough2D11.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/ResolveColor.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/ResolveDepthStencil.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Swizzle11.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_gs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_ps_4f.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_ps_4i.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_ps_4ui.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/buffertotexture11_vs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11_fl9vs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11multiviewgs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11multiviewvs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clear11vs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/cleardepth11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11_fl9ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps1.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps2.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps3.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps4.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps5.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps6.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps7.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearfloat11ps8.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps1.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps2.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps3.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps4.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps5.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps6.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps7.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearsint11ps8.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps1.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps2.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps3.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps4.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps5.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps6.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps7.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/clearuint11ps8.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_luma_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_luma_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_luma_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_lumaalpha_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_lumaalpha_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_lumaalpha_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_565_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_565_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgb_565_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_4444_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_4444_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_4444_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_5551_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_5551_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_pm_rgba_5551_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_luma_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_luma_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_luma_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_lumaalpha_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_lumaalpha_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_lumaalpha_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_565_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_565_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgb_565_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_4444_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftof_um_rgba_5551_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pm_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_pt_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftoi_um_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgb_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgba_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pm_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgb_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgba_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_pt_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgb_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgb_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgb_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgba_2d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgba_2darray_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/multiplyalpha_ftou_um_rgba_3d_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough2d11vs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough3d11gs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrough3d11vs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthrougha2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughdepth2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlum2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlum2darray11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlum3d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlumalpha2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlumalpha2darray11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughlumalpha3d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2darray11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2darrayi11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2darrayui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr2dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr3d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr3di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughr3dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2darray11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2darrayi11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2darrayui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg2dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg3d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg3di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrg3dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2d_565_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darray11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darray_565_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darrayi11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2darrayui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb2dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3d_565_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgb3dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d_4444_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2d_5551_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darray11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darray_4444_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darray_5551_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darrayi11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2darrayui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2dms11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba2dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3d11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3d_4444_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3d_5551_11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3di11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/passthroughrgba3dui11ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvecolor2dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvedepth11_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvedepthstencil11_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvedepthstencil11_vs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/resolvestencil11_ps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlef2darrayps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlef2dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlef3dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlei2darrayps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlei2dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzlei3dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui2darrayps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui2dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/compiled/swizzleui3dps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_data.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_map.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/componentmaskpremultps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/componentmaskps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/componentmaskunmultps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/luminancepremultps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/luminanceps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/luminanceunmultps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/passthroughps.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/compiled/standardvs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/dxgi_format_data.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/dxgi_support_data.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/gl_bindings_data.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/load_functions_data.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/LineRasterComparison.gif +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/LineRasterHoles.jpg +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/LineRasterPixelExample.png +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/doc/img/VulkanShaderTranslation.svg +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mandatory_format_support_data.json +FILE: ../../../third_party/angle/src/libEGL/libEGL.rc +FILE: ../../../third_party/angle/src/libEGL/resource.h +FILE: ../../../third_party/angle/src/libGL/libGL.rc +FILE: ../../../third_party/angle/src/libGL/resource.h +FILE: ../../../third_party/angle/src/libGLESv1_CM/libGLESv1_CM.def +FILE: ../../../third_party/angle/src/libGLESv1_CM/libGLESv1_CM.rc +FILE: ../../../third_party/angle/src/libGLESv1_CM/resource.h +FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2.rc +FILE: ../../../third_party/angle/src/libGLESv2/resource.h +FILE: ../../../third_party/angle/util/ios/Info.plist +---------------------------------------------------------------------------------------------------- +Copyright 2018 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/android/AndroidManifest.xml.jinja2 + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/android/AndroidManifest.xml.jinja2 +FILE: ../../../third_party/angle/include/GLES/glext_angle.h +FILE: ../../../third_party/angle/include/platform/FeaturesVk.h +FILE: ../../../third_party/angle/samples/gles1/DrawTexture.cpp +FILE: ../../../third_party/angle/samples/gles1/FlatShading.cpp +FILE: ../../../third_party/angle/samples/gles1/HelloTriangle.cpp +FILE: ../../../third_party/angle/samples/gles1/SimpleLighting.cpp +FILE: ../../../third_party/angle/samples/gles1/SimpleTexture2D.cpp +FILE: ../../../third_party/angle/scripts/egl_angle_ext.xml +FILE: ../../../third_party/angle/scripts/gl_angle_ext.xml +FILE: ../../../third_party/angle/scripts/vk_mandatory_format_support_capture_to_json.js +FILE: ../../../third_party/angle/src/android_system_settings/res/values-v17/styles.xml +FILE: ../../../third_party/angle/src/common/FastVector.h +FILE: ../../../third_party/angle/src/common/FastVector_unittest.cpp +FILE: ../../../third_party/angle/src/common/FixedVector.h +FILE: ../../../third_party/angle/src/common/FixedVector_unittest.cpp +FILE: ../../../third_party/angle/src/common/PackedEnums.cpp +FILE: ../../../third_party/angle/src/common/android_util.cpp +FILE: ../../../third_party/angle/src/common/android_util.h +FILE: ../../../third_party/angle/src/common/hash_utils.h +FILE: ../../../third_party/angle/src/common/hash_utils_unittest.cpp +FILE: ../../../third_party/angle/src/common/matrix_utils.cpp +FILE: ../../../third_party/angle/src/common/system_utils.cpp +FILE: ../../../third_party/angle/src/common/system_utils_posix.cpp +FILE: ../../../third_party/angle/src/compiler/translator/AtomicCounterFunctionHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/AtomicCounterFunctionHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/FunctionLookup.cpp +FILE: ../../../third_party/angle/src/compiler/translator/FunctionLookup.h +FILE: ../../../third_party/angle/src/compiler/translator/ImmutableString.h +FILE: ../../../third_party/angle/src/compiler/translator/ImmutableStringBuilder.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ImmutableStringBuilder.h +FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockFunctionHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ShaderStorageBlockOutputHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/builtin_variables.json +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/FoldExpressions.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/FoldExpressions.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneEmptyCases.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneEmptyCases.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteAtomicFunctionExpressions.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteExpressionsWithShaderStorageBlock.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/SeparateArrayConstructorStatements.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/RewriteRepeatedAssignToSwizzled.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/RewriteRepeatedAssignToSwizzled.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/NameEmbeddedUniformStructs.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/NameEmbeddedUniformStructs.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteStructSamplers.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteStructSamplers.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceVariable.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceVariable.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/Visit.h +FILE: ../../../third_party/angle/src/feature_support_util/feature_support_util.cpp +FILE: ../../../third_party/angle/src/feature_support_util/feature_support_util.h +FILE: ../../../third_party/angle/src/feature_support_util/feature_support_util_unittest.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_android.cpp +FILE: ../../../third_party/angle/src/libANGLE/BlobCache.cpp +FILE: ../../../third_party/angle/src/libANGLE/BlobCache.h +FILE: ../../../third_party/angle/src/libANGLE/BlobCache_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/Context.inl.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_1_0.cpp +FILE: ../../../third_party/angle/src/libANGLE/GLES1Renderer.cpp +FILE: ../../../third_party/angle/src/libANGLE/GLES1Renderer.h +FILE: ../../../third_party/angle/src/libANGLE/GLES1Shaders.inc +FILE: ../../../third_party/angle/src/libANGLE/GLES1State.cpp +FILE: ../../../third_party/angle/src/libANGLE/GLES1State.h +FILE: ../../../third_party/angle/src/libANGLE/Observer.cpp +FILE: ../../../third_party/angle/src/libANGLE/ResourceMap_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/entry_points_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/QueryImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/RenderTargetCache.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ContextD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11Helper_autogen.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Program11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Program11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/MultiplyAlpha.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Passthrough2DArray11.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/shaders/Passthrough3D11.hlsl +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ImageGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ImageGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ContextEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ContextEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ExternalImageSiblingEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ImageEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/ImageEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/RendererEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/RendererEGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/ContextWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/ContextWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/RendererWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/RendererWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/UtilsVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/UtilsVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/WindowSurfaceVkAndroid.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/WindowSurfaceVkAndroid.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000004.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000005.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000006.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000007.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000008.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000009.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000B.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000C.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000D.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000E.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.0000000F.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000010.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000011.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000012.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000013.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000014.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000015.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000016.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolve.frag.00000017.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/BlitResolveStencilNoExport.comp.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndex.comp.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndexIndirectLineLoop.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndexIndirectLineLoop.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndexIndirectLineLoop.comp.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertIndirectLineLoop.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000004.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000005.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000006.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000008.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000004.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000005.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000006.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000007.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000008.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000009.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000004.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000005.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000006.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000007.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000008.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000009.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000B.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000C.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000D.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000E.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000000F.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000010.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000011.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000012.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000013.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000014.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000015.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000016.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000017.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000018.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000019.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000001A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000001B.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000001C.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000001D.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000001E.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000001F.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000020.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000021.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000022.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000023.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000024.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000025.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000026.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000027.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000028.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000029.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000002A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000002B.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000002C.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000002D.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000002E.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.0000002F.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000004.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000005.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000006.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000008.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000009.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000000A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000010.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000011.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000012.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000014.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000015.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000016.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000018.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000019.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000001A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000020.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000021.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000022.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000024.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000025.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000026.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000028.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.00000029.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/ImageCopy.frag.0000002A.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000002.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000003.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000004.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayCull.comp.00000005.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayDraw.comp.00000000.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/gen/OverlayDraw.comp.00000001.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/FullScreenQuad.vert +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageCopy.frag +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ImageCopy.frag.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_caps_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_map.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationES1.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationES1.h +FILE: ../../../third_party/angle/src/libANGLE/validationES2.h +FILE: ../../../third_party/angle/src/libANGLE/validationES3.h +FILE: ../../../third_party/angle/src/libANGLE/validationES31.h +FILE: ../../../third_party/angle/src/libANGLE/validationESEXT.h +FILE: ../../../third_party/angle/src/libEGL/egl_loader_autogen.cpp +FILE: ../../../third_party/angle/src/libEGL/egl_loader_autogen.h +FILE: ../../../third_party/angle/src/libGLESv1_CM/libGLESv1_CM.cpp +FILE: ../../../third_party/angle/util/EGLPlatformParameters.h +FILE: ../../../third_party/angle/util/egl_loader_autogen.cpp +FILE: ../../../third_party/angle/util/egl_loader_autogen.h +FILE: ../../../third_party/angle/util/gles_loader_autogen.cpp +FILE: ../../../third_party/angle/util/gles_loader_autogen.h +FILE: ../../../third_party/angle/util/util_export.h +FILE: ../../../third_party/angle/util/util_gl.h +FILE: ../../../third_party/angle/util/windows/WGLWindow.cpp +FILE: ../../../third_party/angle/util/windows/WGLWindow.h +FILE: ../../../third_party/angle/util/windows/wgl_loader_autogen.cpp +FILE: ../../../third_party/angle/util/windows/wgl_loader_autogen.h +---------------------------------------------------------------------------------------------------- +Copyright 2018 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/include/GLES/glext_explicit_context_autogen.inc + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/include/GLES/glext_explicit_context_autogen.inc +FILE: ../../../third_party/angle/include/GLES2/gl2ext_explicit_context_autogen.inc +FILE: ../../../third_party/angle/include/GLES3/gl31ext_explicit_context_autogen.inc +FILE: ../../../third_party/angle/include/GLES3/gl32ext_explicit_context_autogen.inc +FILE: ../../../third_party/angle/include/GLES3/gl3ext_explicit_context_autogen.inc +FILE: ../../../third_party/angle/include/platform/Platform.h +FILE: ../../../third_party/angle/infra/gn_isolate_map.pyl +FILE: ../../../third_party/angle/samples/capture_replay/angle_trace_gl.h +FILE: ../../../third_party/angle/scripts/entry_point_packed_egl_enums.json +FILE: ../../../third_party/angle/src/common/apple/SoftLinking.h +FILE: ../../../third_party/angle/src/common/entry_points_enum_autogen.cpp +FILE: ../../../third_party/angle/src/common/entry_points_enum_autogen.h +FILE: ../../../third_party/angle/src/common/gl/cgl/FunctionsCGL.cpp +FILE: ../../../third_party/angle/src/common/gl/cgl/FunctionsCGL.h +FILE: ../../../third_party/angle/src/common/system_utils_apple.cpp +FILE: ../../../third_party/angle/src/common/vulkan/vulkan_icd.cpp +FILE: ../../../third_party/angle/src/common/vulkan/vulkan_icd.h +FILE: ../../../third_party/angle/src/compiler/translator/ImmutableString_ESSL_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ImmutableString_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ParseContext_ESSL_autogen.h +FILE: ../../../third_party/angle/src/compiler/translator/ParseContext_complete_autogen.h +FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable_ESSL_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable_autogen.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateBarrierFunctionCall.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateBarrierFunctionCall.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateClipCullDistance.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateClipCullDistance.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ForcePrecisionQualifier.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ForcePrecisionQualifier.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RecordUniformBlocksWithLargeArrayMember.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/EarlyFragmentTestsOptimization.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/EarlyFragmentTestsOptimization.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/FlagSamplersWithTexelFetch.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/FlagSamplersWithTexelFetch.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RemoveAtomicCounterBuiltins.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RemoveAtomicCounterBuiltins.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/ReplaceForShaderFramebufferFetch.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/ReplaceForShaderFramebufferFetch.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteInterpolateAtOffset.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteInterpolateAtOffset.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/BuiltIn_ESSL_autogen.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/BuiltIn_complete_autogen.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/DriverUniform.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/DriverUniform.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceArrayOfMatrixVarying.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceArrayOfMatrixVarying.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/ReplaceClipCullDistanceVariable.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RewriteSampleMaskVariable.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RewriteSampleMaskVariable.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RunAtTheBeginningOfShader.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/RunAtTheBeginningOfShader.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/SpecializationConstant.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/SpecializationConstant.h +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_apple.mm +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_fuchsia.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_ios.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_vulkan.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_vulkan.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gl_1_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gl_2_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gl_3_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gl_4_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_1_0_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_2_0_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_3_0_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_3_1_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_3_2_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/Context_gles_ext_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/InfoLog.h +FILE: ../../../third_party/angle/src/libANGLE/ProgramExecutable.cpp +FILE: ../../../third_party/angle/src/libANGLE/ProgramExecutable.h +FILE: ../../../third_party/angle/src/libANGLE/angletypes_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_1_0_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_1_0_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_2_0_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_2_0_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_0_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_0_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_1_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_1_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_2_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_3_2_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_ext_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/capture_gles_ext_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/capture/frame_capture_replay_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/frame_capture_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/frame_capture_utils.h +FILE: ../../../third_party/angle/src/libANGLE/capture/frame_capture_utils_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/capture/frame_capture_utils_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/BufferImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLReusableSync.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLReusableSync.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/EGLSyncImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/FormatID_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/Format_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramPipelineImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/driver_utils_d3d.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/driver_utils_d3d.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d_format.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d_format.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/dxgi_format_map.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/dxgi_format_map_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/apple/DisplayApple_api.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/apple/DisplayApple_api.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/ContextEAGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/ContextEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DeviceEAGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DeviceEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DisplayEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/DisplayEAGL.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/FunctionsEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/FunctionsEAGL.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/RendererEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PixmapSurfaceGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PixmapSurfaceGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/glx_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/glx_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/load_functions_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/QueryMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/QueryMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SamplerMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SamplerMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SyncMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/SyncMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TransformFeedbackMtl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/TransformFeedbackMtl.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/file_hooking/shader_cache_file_hooking.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_occlusion_query_pool.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/mtl_occlusion_query_pool.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/format_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CommandProcessor.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/CommandProcessor.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DebugAnnotatorVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/DebugAnnotatorVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/vk_android_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/vk_android_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/display/DisplayVkSimple.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/display/DisplayVkSimple.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/display/WindowSurfaceVkSimple.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/display/WindowSurfaceVkSimple.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/headless/DisplayVkHeadless.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/headless/DisplayVkHeadless.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/headless/WindowSurfaceVkHeadless.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/headless/WindowSurfaceVkHeadless.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/ConvertIndexIndirectLineLoop.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/GenerateMipmap.comp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/GenerateMipmap.comp.json +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mandatory_format_support_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h +FILE: ../../../third_party/angle/src/libANGLE/validationEGL_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationES1_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationES2_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationES31_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationES32_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationES3_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationESEXT_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL11_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL12_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL13_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL14_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL15_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL1_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL21_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL2_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL31_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL32_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL33_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL3_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL41_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL42_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL43_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL44_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL45_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL46_autogen.h +FILE: ../../../third_party/angle/src/libANGLE/validationGL4_autogen.h +FILE: ../../../third_party/angle/src/libEGL/libEGL_autogen.cpp +FILE: ../../../third_party/angle/src/libEGL/libEGL_autogen.def +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_autogen.cpp +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_1_autogen.h +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_2_autogen.cpp +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_2_autogen.h +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_autogen.cpp +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_3_autogen.h +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_autogen.cpp +FILE: ../../../third_party/angle/src/libGL/entry_points_gl_4_autogen.h +FILE: ../../../third_party/angle/src/libGL/libGL_autogen.cpp +FILE: ../../../third_party/angle/src/libGL/libGL_autogen.def +FILE: ../../../third_party/angle/src/libGLESv2/egl_ext_stubs.cpp +FILE: ../../../third_party/angle/src/libGLESv2/egl_ext_stubs_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/egl_get_labeled_object_data.json +FILE: ../../../third_party/angle/src/libGLESv2/egl_stubs.cpp +FILE: ../../../third_party/angle/src/libGLESv2/egl_stubs_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl_ext_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_egl_ext_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_1_0_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_1_0_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_2_0_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_2_0_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_0_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_0_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_1_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_1_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_2_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_3_2_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_ext_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/entry_points_gles_ext_autogen.h +FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_autogen.cpp +FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_autogen.def +FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_no_capture_autogen.def +FILE: ../../../third_party/angle/src/libGLESv2/libGLESv2_with_capture_autogen.def +FILE: ../../../third_party/angle/src/libOpenCL/entry_points_cl_autogen.cpp +FILE: ../../../third_party/angle/src/libOpenCL/entry_points_cl_autogen.h +FILE: ../../../third_party/angle/util/display/DisplayPixmap.cpp +FILE: ../../../third_party/angle/util/display/DisplayWindow.cpp +FILE: ../../../third_party/angle/util/display/DisplayWindow.h +FILE: ../../../third_party/angle/util/frame_capture_test_utils.h +FILE: ../../../third_party/angle/util/ios/IOSPixmap.h +FILE: ../../../third_party/angle/util/ios/IOSPixmap.mm +FILE: ../../../third_party/angle/util/ios/IOSWindow.h +FILE: ../../../third_party/angle/util/ios/IOSWindow.mm +FILE: ../../../third_party/angle/util/ios/ios_main.mm +FILE: ../../../third_party/angle/util/png_utils.cpp +FILE: ../../../third_party/angle/util/png_utils.h +---------------------------------------------------------------------------------------------------- +Copyright 2020 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/include/GLSLANG/ShaderLang.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/include/GLSLANG/ShaderLang.h +FILE: ../../../third_party/angle/samples/shader_translator/shader_translator.cpp +FILE: ../../../third_party/angle/src/common/angleutils.h +FILE: ../../../third_party/angle/src/common/debug.cpp +FILE: ../../../third_party/angle/src/common/debug.h +FILE: ../../../third_party/angle/src/common/mathutil.h +FILE: ../../../third_party/angle/src/common/utilities.cpp +FILE: ../../../third_party/angle/src/common/utilities.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.l +FILE: ../../../third_party/angle/src/compiler/translator/ASTMetadataHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ASTMetadataHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/BaseTypes.h +FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp +FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/CallDAG.cpp +FILE: ../../../third_party/angle/src/compiler/translator/CallDAG.h +FILE: ../../../third_party/angle/src/compiler/translator/CollectVariables.cpp +FILE: ../../../third_party/angle/src/compiler/translator/CollectVariables.h +FILE: ../../../third_party/angle/src/compiler/translator/Common.h +FILE: ../../../third_party/angle/src/compiler/translator/Compiler.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Compiler.h +FILE: ../../../third_party/angle/src/compiler/translator/ConstantUnion.h +FILE: ../../../third_party/angle/src/compiler/translator/ExtensionBehavior.h +FILE: ../../../third_party/angle/src/compiler/translator/HashNames.h +FILE: ../../../third_party/angle/src/compiler/translator/InfoSink.cpp +FILE: ../../../third_party/angle/src/compiler/translator/InfoSink.h +FILE: ../../../third_party/angle/src/compiler/translator/Initialize.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Initialize.h +FILE: ../../../third_party/angle/src/compiler/translator/InitializeDll.cpp +FILE: ../../../third_party/angle/src/compiler/translator/InitializeDll.h +FILE: ../../../third_party/angle/src/compiler/translator/InitializeGlobals.h +FILE: ../../../third_party/angle/src/compiler/translator/IntermNode.cpp +FILE: ../../../third_party/angle/src/compiler/translator/IntermNode.h +FILE: ../../../third_party/angle/src/compiler/translator/Operator.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Operator.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputESSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/OutputESSL.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSLBase.cpp +FILE: ../../../third_party/angle/src/compiler/translator/OutputGLSLBase.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/OutputHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/OutputTree.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ParseContext.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ParseContext.h +FILE: ../../../third_party/angle/src/compiler/translator/PoolAlloc.cpp +FILE: ../../../third_party/angle/src/compiler/translator/PoolAlloc.h +FILE: ../../../third_party/angle/src/compiler/translator/QualifierTypes.cpp +FILE: ../../../third_party/angle/src/compiler/translator/QualifierTypes.h +FILE: ../../../third_party/angle/src/compiler/translator/ShaderLang.cpp +FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable.cpp +FILE: ../../../third_party/angle/src/compiler/translator/SymbolTable.h +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorESSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorESSL.h +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/TranslatorHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/Types.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Types.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateGlobalInitializer.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateGlobalInitializer.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateLimitations.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateSwitch.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateSwitch.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateVaryingLocations.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateVaryingLocations.h +FILE: ../../../third_party/angle/src/compiler/translator/VariablePacker.cpp +FILE: ../../../third_party/angle/src/compiler/translator/VariablePacker.h +FILE: ../../../third_party/angle/src/compiler/translator/VersionGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/VersionGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/glslang.l +FILE: ../../../third_party/angle/src/compiler/translator/glslang.y +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulatePrecision.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/EmulatePrecision.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/InitializeVariables.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/InitializeVariables.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneNoOps.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/PruneNoOps.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveDynamicIndexing.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemoveDynamicIndexing.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemovePow.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/RemovePow.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateDeclarations.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/SeparateDeclarations.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/ArrayReturnValueToOutParameter.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/SeparateArrayInitialization.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/SeparateExpressionsReturningArrays.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/UnfoldShortCircuitToIf.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/RecordConstantPrecision.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/RecordConstantPrecision.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/RegenerateStructNames.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/RegenerateStructNames.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/UnfoldShortCircuitAST.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/UnfoldShortCircuitAST.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/IntermTraverse.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/NodeSearch.h +FILE: ../../../third_party/angle/src/compiler/translator/util.h +FILE: ../../../third_party/angle/src/image_util/generatemip.h +FILE: ../../../third_party/angle/src/libANGLE/Buffer.cpp +FILE: ../../../third_party/angle/src/libANGLE/Buffer.h +FILE: ../../../third_party/angle/src/libANGLE/Config.cpp +FILE: ../../../third_party/angle/src/libANGLE/Config.h +FILE: ../../../third_party/angle/src/libANGLE/Context.cpp +FILE: ../../../third_party/angle/src/libANGLE/Context.h +FILE: ../../../third_party/angle/src/libANGLE/Display.cpp +FILE: ../../../third_party/angle/src/libANGLE/Display.h +FILE: ../../../third_party/angle/src/libANGLE/EGLSync.cpp +FILE: ../../../third_party/angle/src/libANGLE/Fence.cpp +FILE: ../../../third_party/angle/src/libANGLE/Fence.h +FILE: ../../../third_party/angle/src/libANGLE/Framebuffer.cpp +FILE: ../../../third_party/angle/src/libANGLE/Framebuffer.h +FILE: ../../../third_party/angle/src/libANGLE/HandleAllocator.cpp +FILE: ../../../third_party/angle/src/libANGLE/HandleAllocator.h +FILE: ../../../third_party/angle/src/libANGLE/Program.cpp +FILE: ../../../third_party/angle/src/libANGLE/Program.h +FILE: ../../../third_party/angle/src/libANGLE/RefCountObject.h +FILE: ../../../third_party/angle/src/libANGLE/Renderbuffer.cpp +FILE: ../../../third_party/angle/src/libANGLE/Renderbuffer.h +FILE: ../../../third_party/angle/src/libANGLE/ResourceManager.cpp +FILE: ../../../third_party/angle/src/libANGLE/ResourceManager.h +FILE: ../../../third_party/angle/src/libANGLE/Shader.cpp +FILE: ../../../third_party/angle/src/libANGLE/Shader.h +FILE: ../../../third_party/angle/src/libANGLE/Surface.cpp +FILE: ../../../third_party/angle/src/libANGLE/Surface.h +FILE: ../../../third_party/angle/src/libANGLE/Texture.cpp +FILE: ../../../third_party/angle/src/libANGLE/Texture.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/SurfaceImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ImageD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ImageD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexBuffer.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexBuffer.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexDataManager.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/IndexDataManager.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/TextureStorage.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexBuffer.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexBuffer.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexDataManager.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/VertexDataManager.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Image9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Image9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/IndexBuffer9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/IndexBuffer9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexBuffer9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexBuffer9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/vertexconversion.h +---------------------------------------------------------------------------------------------------- +Copyright 2002 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/include/GLSLANG/ShaderVars.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/include/GLSLANG/ShaderVars.h +FILE: ../../../third_party/angle/samples/sample_util/SampleApplication.cpp +FILE: ../../../third_party/angle/src/common/mathutil.cpp +FILE: ../../../third_party/angle/src/compiler/translator/CodeGen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/FlagStd140Structs.cpp +FILE: ../../../third_party/angle/src/compiler/translator/FlagStd140Structs.h +FILE: ../../../third_party/angle/src/compiler/translator/ValidateOutputs.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ValidateOutputs.h +FILE: ../../../third_party/angle/src/compiler/translator/blocklayout.cpp +FILE: ../../../third_party/angle/src/compiler/translator/blocklayout.h +FILE: ../../../third_party/angle/src/compiler/translator/blocklayoutHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/blocklayoutHLSL.h +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo.h +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_internal.h +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_libpci.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_linux.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_win.cpp +FILE: ../../../third_party/angle/src/gpu_info_util/SystemInfo_x11.cpp +FILE: ../../../third_party/angle/src/image_util/copyimage.cpp +FILE: ../../../third_party/angle/src/image_util/copyimage.h +FILE: ../../../third_party/angle/src/image_util/imageformats.h +FILE: ../../../third_party/angle/src/image_util/loadimage.cpp +FILE: ../../../third_party/angle/src/image_util/loadimage.h +FILE: ../../../third_party/angle/src/image_util/loadimage_etc.cpp +FILE: ../../../third_party/angle/src/libANGLE/Constants.h +FILE: ../../../third_party/angle/src/libANGLE/IndexRangeCache.cpp +FILE: ../../../third_party/angle/src/libANGLE/IndexRangeCache.h +FILE: ../../../third_party/angle/src/libANGLE/Sampler.cpp +FILE: ../../../third_party/angle/src/libANGLE/Sampler.h +FILE: ../../../third_party/angle/src/libANGLE/VertexArray.cpp +FILE: ../../../third_party/angle/src/libANGLE/VertexArray.h +FILE: ../../../third_party/angle/src/libANGLE/VertexAttribute.h +FILE: ../../../third_party/angle/src/libANGLE/angletypes.cpp +FILE: ../../../third_party/angle/src/libANGLE/formatutils.cpp +FILE: ../../../third_party/angle/src/libANGLE/formatutils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/QueryImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/copyvertex.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Fence11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Fence11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/PixelTransfer11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Query11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Query11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexBuffer11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/formatutils11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/formatutils11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Fence9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Fence9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Query9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Query9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/formatutils9.h +FILE: ../../../third_party/angle/src/libANGLE/validationES.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationES.h +FILE: ../../../third_party/angle/src/libANGLE/validationES2.cpp +FILE: ../../../third_party/angle/src/libANGLE/validationES3.cpp +FILE: ../../../third_party/angle/util/EGLWindow.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2013 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/include/KHR/khrplatform.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/KHR/khrplatform.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2008-2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +ORIGIN: ../../../third_party/angle/include/angle_gl.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/include/angle_gl.h +FILE: ../../../third_party/angle/include/angle_windowsstore.h +FILE: ../../../third_party/angle/include/export.h +FILE: ../../../third_party/angle/samples/hello_triangle/HelloTriangle.cpp +FILE: ../../../third_party/angle/samples/mip_map_2d/MipMap2D.cpp +FILE: ../../../third_party/angle/samples/multi_texture/MultiTexture.cpp +FILE: ../../../third_party/angle/samples/multiple_draw_buffers/MultipleDrawBuffers.cpp +FILE: ../../../third_party/angle/samples/particle_system/ParticleSystem.cpp +FILE: ../../../third_party/angle/samples/post_sub_buffer/PostSubBuffer.cpp +FILE: ../../../third_party/angle/samples/sample_util/SampleApplication.h +FILE: ../../../third_party/angle/samples/sample_util/texture_utils.cpp +FILE: ../../../third_party/angle/samples/sample_util/texture_utils.h +FILE: ../../../third_party/angle/samples/sample_util/tga_utils.cpp +FILE: ../../../third_party/angle/samples/sample_util/tga_utils.h +FILE: ../../../third_party/angle/samples/simple_instancing/SimpleInstancing.cpp +FILE: ../../../third_party/angle/samples/simple_texture_2d/SimpleTexture2D.cpp +FILE: ../../../third_party/angle/samples/simple_texture_cubemap/SimpleTextureCubemap.cpp +FILE: ../../../third_party/angle/samples/simple_vertex_shader/SimpleVertexShader.cpp +FILE: ../../../third_party/angle/samples/stencil_operations/StencilOperations.cpp +FILE: ../../../third_party/angle/samples/tex_redef_microbench/TexRedefMicroBench.cpp +FILE: ../../../third_party/angle/samples/texture_wrap/TextureWrap.cpp +FILE: ../../../third_party/angle/samples/tri_fan_microbench/TriFanMicroBench.cpp +FILE: ../../../third_party/angle/src/common/MemoryBuffer.cpp +FILE: ../../../third_party/angle/src/common/MemoryBuffer.h +FILE: ../../../third_party/angle/src/common/angle_version.h +FILE: ../../../third_party/angle/src/common/angleutils.cpp +FILE: ../../../third_party/angle/src/common/platform.h +FILE: ../../../third_party/angle/src/common/system_utils.h +FILE: ../../../third_party/angle/src/common/system_utils_win.cpp +FILE: ../../../third_party/angle/src/common/tls.cpp +FILE: ../../../third_party/angle/src/common/tls.h +FILE: ../../../third_party/angle/src/common/version.h +FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/ResourcesHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ResourcesHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/ShaderVars.cpp +FILE: ../../../third_party/angle/src/compiler/translator/StructureHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/StructureHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/UtilsHLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/UtilsHLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/d3d/RewriteElseBlocks.h +FILE: ../../../third_party/angle/src/image_util/copyimage.inc +FILE: ../../../third_party/angle/src/image_util/loadimage.inc +FILE: ../../../third_party/angle/src/libANGLE/AttributeMap.cpp +FILE: ../../../third_party/angle/src/libANGLE/AttributeMap.h +FILE: ../../../third_party/angle/src/libANGLE/Caps.cpp +FILE: ../../../third_party/angle/src/libANGLE/Caps.h +FILE: ../../../third_party/angle/src/libANGLE/Compiler.cpp +FILE: ../../../third_party/angle/src/libANGLE/Compiler.h +FILE: ../../../third_party/angle/src/libANGLE/Config_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/Error.cpp +FILE: ../../../third_party/angle/src/libANGLE/Error.h +FILE: ../../../third_party/angle/src/libANGLE/Error.inc +FILE: ../../../third_party/angle/src/libANGLE/Fence_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/FramebufferAttachment.cpp +FILE: ../../../third_party/angle/src/libANGLE/FramebufferAttachment.h +FILE: ../../../third_party/angle/src/libANGLE/ImageIndex.cpp +FILE: ../../../third_party/angle/src/libANGLE/ImageIndex.h +FILE: ../../../third_party/angle/src/libANGLE/ImageIndexIterator_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/State.cpp +FILE: ../../../third_party/angle/src/libANGLE/State.h +FILE: ../../../third_party/angle/src/libANGLE/Surface_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/TransformFeedback.cpp +FILE: ../../../third_party/angle/src/libANGLE/TransformFeedback.h +FILE: ../../../third_party/angle/src/libANGLE/TransformFeedback_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/VertexAttribute.cpp +FILE: ../../../third_party/angle/src/libANGLE/features.h +FILE: ../../../third_party/angle/src/libANGLE/queryconversions.cpp +FILE: ../../../third_party/angle/src/libANGLE/queryconversions.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/BufferImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/CompilerImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/DisplayImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/DisplayImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/FramebufferImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/RenderbufferImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/SamplerImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ShaderImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/SurfaceImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/TextureImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/TransformFeedbackImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/VertexArrayImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/copyvertex.inc.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/BufferD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/CompilerD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DisplayD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicHLSL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DynamicHLSL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ProgramD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderbufferD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderbufferD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RendererD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SamplerD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/TextureD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Trim11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Trim11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexArray11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Buffer9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexArray9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SamplerGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SamplerGL.h +FILE: ../../../third_party/angle/src/libGLESv2/global_state.cpp +FILE: ../../../third_party/angle/src/libGLESv2/global_state.h +FILE: ../../../third_party/angle/util/EGLWindow.h +FILE: ../../../third_party/angle/util/Event.h +FILE: ../../../third_party/angle/util/Matrix.cpp +FILE: ../../../third_party/angle/util/Matrix.h +FILE: ../../../third_party/angle/util/OSWindow.cpp +FILE: ../../../third_party/angle/util/OSWindow.h +FILE: ../../../third_party/angle/util/com_utils.h +FILE: ../../../third_party/angle/util/geometry_utils.cpp +FILE: ../../../third_party/angle/util/geometry_utils.h +FILE: ../../../third_party/angle/util/keyboard.h +FILE: ../../../third_party/angle/util/mouse.h +FILE: ../../../third_party/angle/util/random_utils.cpp +FILE: ../../../third_party/angle/util/random_utils.h +FILE: ../../../third_party/angle/util/shader_utils.cpp +FILE: ../../../third_party/angle/util/shader_utils.h +FILE: ../../../third_party/angle/util/test_utils.h +FILE: ../../../third_party/angle/util/windows/test_utils_win.cpp +FILE: ../../../third_party/angle/util/windows/win32/Win32Window.cpp +FILE: ../../../third_party/angle/util/windows/win32/Win32Window.h +FILE: ../../../third_party/angle/util/windows/win32/test_utils_win32.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2014 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/include/platform/FeaturesD3D.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/include/platform/FeaturesD3D.h +FILE: ../../../third_party/angle/include/platform/FeaturesGL.h +FILE: ../../../third_party/angle/include/platform/PlatformMethods.h +FILE: ../../../third_party/angle/samples/WindowTest/WindowTest.cpp +FILE: ../../../third_party/angle/samples/multi_window/MultiWindow.cpp +FILE: ../../../third_party/angle/src/common/Optional.h +FILE: ../../../third_party/angle/src/common/Optional_unittest.cpp +FILE: ../../../third_party/angle/src/common/bitset_utils.h +FILE: ../../../third_party/angle/src/common/bitset_utils_unittest.cpp +FILE: ../../../third_party/angle/src/common/mathutil_unittest.cpp +FILE: ../../../third_party/angle/src/common/matrix_utils.h +FILE: ../../../third_party/angle/src/common/matrix_utils_unittest.cpp +FILE: ../../../third_party/angle/src/common/string_utils.cpp +FILE: ../../../third_party/angle/src/common/string_utils.h +FILE: ../../../third_party/angle/src/common/string_utils_unittest.cpp +FILE: ../../../third_party/angle/src/common/system_utils_ios.cpp +FILE: ../../../third_party/angle/src/common/system_utils_linux.cpp +FILE: ../../../third_party/angle/src/common/system_utils_mac.cpp +FILE: ../../../third_party/angle/src/common/utilities_unittest.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ExtensionGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/ExtensionGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/RewriteDoWhile.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/gl/mac/RewriteDoWhile.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindSymbolNode.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_util/FindSymbolNode.h +FILE: ../../../third_party/angle/src/image_util/generatemip.inc +FILE: ../../../third_party/angle/src/libANGLE/BinaryStream_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/Debug.cpp +FILE: ../../../third_party/angle/src/libANGLE/Debug.h +FILE: ../../../third_party/angle/src/libANGLE/Device.cpp +FILE: ../../../third_party/angle/src/libANGLE/Device.h +FILE: ../../../third_party/angle/src/libANGLE/HandleAllocator_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/Image.cpp +FILE: ../../../third_party/angle/src/libANGLE/Image.h +FILE: ../../../third_party/angle/src/libANGLE/Image_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/Platform.cpp +FILE: ../../../third_party/angle/src/libANGLE/Program_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/ResourceManager_unittest.cpp +FILE: ../../../third_party/angle/src/libANGLE/VaryingPacking.cpp +FILE: ../../../third_party/angle/src/libANGLE/VaryingPacking.h +FILE: ../../../third_party/angle/src/libANGLE/Version.h +FILE: ../../../third_party/angle/src/libANGLE/Version.inc +FILE: ../../../third_party/angle/src/libANGLE/VertexAttribute.inc +FILE: ../../../third_party/angle/src/libANGLE/histogram_macros.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/BufferImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/DeviceImpl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/DeviceImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/FenceNVImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/FramebufferImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/GLImplFactory.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ImageImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ImageImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/ProgramImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/RenderbufferImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/SyncImpl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/TextureImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/TransformFeedbackImpl_mock.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/CompilerD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/DeviceD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/EGLImageD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/EGLImageD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/texture_format_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/DebugAnnotator9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/DebugAnnotator9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/StateManager9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/formatutilsD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/dxgi_support_table.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/dxgi_support_table_autogen.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BlitGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BlitGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BufferGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/BufferGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/CompilerGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/CompilerGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DisplayGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/DisplayGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FenceNVGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FenceNVGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FramebufferGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FunctionsGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/FunctionsGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ProgramGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/QueryGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/QueryGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RenderbufferGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RenderbufferGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RendererGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/RendererGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ShaderGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/ShaderGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/StateManagerGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SurfaceGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SurfaceGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SyncGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/SyncGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TextureGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TextureGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/TransformFeedbackGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/VertexArrayGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/VertexArrayGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/formatutilsgl.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/formatutilsgl.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/functionsgl_enums.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/functionsgl_typedefs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/FunctionsGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/functionsglx_typedefs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/glx/platform_glx.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/D3DTextureSurfaceWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/D3DTextureSurfaceWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DXGISwapChainWindowSurfaceWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/DisplayWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/functionswgl_typedefs.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/wgl/wgl_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/load_functions_table.h +FILE: ../../../third_party/angle/src/libANGLE/validationEGL.h +FILE: ../../../third_party/angle/util/OSPixmap.h +FILE: ../../../third_party/angle/util/osx/OSXPixmap.h +FILE: ../../../third_party/angle/util/osx/OSXPixmap.mm +FILE: ../../../third_party/angle/util/osx/OSXWindow.h +FILE: ../../../third_party/angle/util/osx/OSXWindow.mm +FILE: ../../../third_party/angle/util/posix/test_utils_posix.cpp +FILE: ../../../third_party/angle/util/windows/win32/Win32Pixmap.cpp +FILE: ../../../third_party/angle/util/windows/win32/Win32Pixmap.h +FILE: ../../../third_party/angle/util/x11/X11Pixmap.cpp +FILE: ../../../third_party/angle/util/x11/X11Pixmap.h +FILE: ../../../third_party/angle/util/x11/X11Window.cpp +FILE: ../../../third_party/angle/util/x11/X11Window.h +---------------------------------------------------------------------------------------------------- +Copyright 2015 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/infra/config/main.star + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/infra/config/main.star +---------------------------------------------------------------------------------------------------- +Copyright 2021 The Chromium 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: angle +ORIGIN: ../../../third_party/angle/src/common/Float16ToFloat32.cpp + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/Float16ToFloat32.cpp +FILE: ../../../third_party/angle/src/common/event_tracer.cpp +FILE: ../../../third_party/angle/src/common/event_tracer.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/DiagnosticsBase.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/DiagnosticsBase.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveHandlerBase.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveParser.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/ExpressionParser.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/Lexer.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/Lexer.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/Macro.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/MacroExpander.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/SourceLocation.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/Tokenizer.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/numeric_lex.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor.y +FILE: ../../../third_party/angle/src/compiler/translator/Diagnostics.cpp +FILE: ../../../third_party/angle/src/compiler/translator/Diagnostics.h +FILE: ../../../third_party/angle/src/compiler/translator/DirectiveHandler.cpp +FILE: ../../../third_party/angle/src/compiler/translator/DirectiveHandler.h +FILE: ../../../third_party/angle/src/compiler/translator/Pragma.h +FILE: ../../../third_party/angle/src/libANGLE/BinaryStream.h +FILE: ../../../third_party/angle/src/libANGLE/Query.cpp +FILE: ../../../third_party/angle/src/libANGLE/Query.h +FILE: ../../../third_party/angle/src/libANGLE/angletypes.h +FILE: ../../../third_party/angle/src/libANGLE/angletypes.inc +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderTargetD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/RenderTargetD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderExecutableD3D.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/ShaderExecutableD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/SwapChainD3D.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/IndexBuffer11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/IndexBuffer11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderTarget11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/RenderTarget11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ShaderExecutable11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/ShaderExecutable11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/VertexBuffer11.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/RenderTarget9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/RenderTarget9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/ShaderCache.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/ShaderExecutable9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/ShaderExecutable9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/SwapChain9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/TextureStorage9.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/VertexDeclarationCache.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/Blit.ps +FILE: ../../../third_party/angle/src/libANGLE/renderer/d3d/d3d9/shaders/Blit.vs +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/renderergl_utils.h +---------------------------------------------------------------------------------------------------- +Copyright 2012 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl.c + ../../../fuchsia/sdk/linux/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl.c +FILE: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl.h +FILE: ../../../third_party/angle/src/common/fuchsia_egl/fuchsia_egl_backend.h +---------------------------------------------------------------------------------------------------- +Copyright 2019 The Fuchsia 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. + +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: angle +ORIGIN: ../../../third_party/angle/src/common/spirv/angle_spirv_utils.cpp + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/spirv/angle_spirv_utils.cpp +FILE: ../../../third_party/angle/src/common/spirv/spirv_instruction_builder_autogen.cpp +FILE: ../../../third_party/angle/src/common/spirv/spirv_instruction_builder_autogen.h +FILE: ../../../third_party/angle/src/common/spirv/spirv_instruction_parser_autogen.cpp +FILE: ../../../third_party/angle/src/common/spirv/spirv_instruction_parser_autogen.h +FILE: ../../../third_party/angle/src/common/spirv/spirv_types.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/MonomorphizeUnsupportedFunctionsInVulkanGLSL.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/MonomorphizeUnsupportedFunctionsInVulkanGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteArrayOfArrayOfOpaqueUniforms.h +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteR32fImages.cpp +FILE: ../../../third_party/angle/src/compiler/translator/tree_ops/vulkan/RewriteR32fImages.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/AHBFunctions.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/android/AHBFunctions.h +FILE: ../../../third_party/angle/src/libOpenCL/entry_points_cl_utils.h +---------------------------------------------------------------------------------------------------- +Copyright 2021 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/compiler/preprocessor/DirectiveParser.cpp + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/compiler/preprocessor/DirectiveParser.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/Input.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/Input.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/Macro.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/MacroExpander.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/Preprocessor.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/Preprocessor.h +FILE: ../../../third_party/angle/src/compiler/preprocessor/Token.cpp +FILE: ../../../third_party/angle/src/compiler/preprocessor/Token.h +FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulator.h +FILE: ../../../third_party/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h +FILE: ../../../third_party/angle/src/compiler/translator/length_limits.h +---------------------------------------------------------------------------------------------------- +Copyright 2011 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_tab_autogen.cpp +TYPE: LicenseType.bison +FILE: ../../../third_party/angle/src/compiler/preprocessor/preprocessor_tab_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/glslang_tab_autogen.cpp +FILE: ../../../third_party/angle/src/compiler/translator/glslang_tab_autogen.h +---------------------------------------------------------------------------------------------------- + +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +ORIGIN: ../../../third_party/angle/src/compiler/translator/ValidateLimitations.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/compiler/translator/ValidateLimitations.h +FILE: ../../../third_party/angle/src/compiler/translator/glslang.h +FILE: ../../../third_party/angle/src/compiler/translator/util.cpp +FILE: ../../../third_party/angle/src/libANGLE/Uniform.cpp +FILE: ../../../third_party/angle/src/libANGLE/Uniform.h +---------------------------------------------------------------------------------------------------- +Copyright 2010 The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp +FILE: ../../../third_party/angle/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.h +FILE: ../../../third_party/angle/util/fuchsia/FuchsiaPixmap.cpp +FILE: ../../../third_party/angle/util/fuchsia/FuchsiaPixmap.h +---------------------------------------------------------------------------------------------------- +Copyright The ANGLE 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/blit.metal + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/blit.metal +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/clear.metal +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/common.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/gen_indices.metal +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/visibility.metal +---------------------------------------------------------------------------------------------------- +Copyright 2019 The ANGLE Project. 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/constants.h + ../../../third_party/angle/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/constants.h +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/copy_buffer.metal +FILE: ../../../third_party/angle/src/libANGLE/renderer/metal/shaders/gen_mipmap.metal +---------------------------------------------------------------------------------------------------- +Copyright 2020 The ANGLE Project. 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 TransGaming Inc., Google Inc., 3DLabs Inc. + Ltd., nor the names of their 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: angle +ORIGIN: ../../../third_party/angle/src/third_party/compiler/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/third_party/compiler/ArrayBoundsClamper.cpp +FILE: ../../../third_party/angle/src/third_party/compiler/ArrayBoundsClamper.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 2012 Apple 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: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``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 APPLE, INC. 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: angle +ORIGIN: ../../../third_party/angle/src/third_party/libXNVCtrl/LICENSE +TYPE: LicenseType.mit +FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.c +FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrlLib.h +FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/nv_control.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2008 NVIDIA, Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +ORIGIN: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.h +TYPE: LicenseType.mit +FILE: ../../../third_party/angle/src/third_party/libXNVCtrl/NVCtrl.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2010 NVIDIA, Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +ORIGIN: ../../../third_party/angle/src/third_party/systeminfo/SystemInfo.cpp +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/third_party/systeminfo/SystemInfo.cpp +FILE: ../../../third_party/angle/src/third_party/systeminfo/SystemInfo.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 2009 Apple 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: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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: angle +ORIGIN: ../../../third_party/angle/src/third_party/trace_event/trace_event.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/third_party/trace_event/trace_event.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013 The Chromium 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: base +LIBRARY: icu +LIBRARY: zlib +ORIGIN: ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_conversions.h +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_conversions_impl.h +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_math.h +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_math_impl.h +FILE: ../../../third_party/icu/icu.isolate +FILE: ../../../third_party/zlib/google/compression_utils.cc +FILE: ../../../third_party/zlib/google/compression_utils.h +FILE: ../../../third_party/zlib/google/compression_utils_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright 2014 The Chromium 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: base +LIBRARY: zlib +ORIGIN: ../../../third_party/angle/src/common/third_party/base/anglebase/containers/mru_cache.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/containers/mru_cache.h +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/sha1.cc +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/sha1.h +FILE: ../../../third_party/zlib/google/zip.h +FILE: ../../../third_party/zlib/google/zip_internal.cc +FILE: ../../../third_party/zlib/google/zip_internal.h +FILE: ../../../third_party/zlib/google/zip_reader.h +FILE: ../../../third_party/zlib/google/zip_reader_unittest.cc +FILE: ../../../third_party/zlib/google/zip_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2011 The Chromium 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: base +LIBRARY: zlib +ORIGIN: ../../../third_party/angle/src/common/third_party/base/anglebase/no_destructor.h + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/no_destructor.h +FILE: ../../../third_party/zlib/contrib/bench/zlib_bench.cc +FILE: ../../../third_party/zlib/contrib/optimizations/slide_hash_neon.h +FILE: ../../../third_party/zlib/cpu_features.c +FILE: ../../../third_party/zlib/cpu_features.h +---------------------------------------------------------------------------------------------------- +Copyright 2018 The Chromium 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: base +ORIGIN: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright 2013 The Chromium 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: boringssl +LIBRARY: dart +ORIGIN: ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.openssl +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/bsaes-armv7.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha256-armv4.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha512-armv4.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S +FILE: ../../../third_party/boringssl/src/crypto/err/asn1.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/bio.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/bn.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/cipher.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/conf.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/dh.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/digest.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/dsa.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/ec.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/ecdh.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/ecdsa.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/engine.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/evp.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/hkdf.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/obj.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/pem.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/pkcs7.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/pkcs8.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/rsa.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/ssl.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/trust_token.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/x509.errordata +FILE: ../../../third_party/boringssl/src/crypto/err/x509v3.errordata +FILE: ../../../third_party/boringssl/src/crypto/evp/scrypt.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_tables.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64-table.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/fips_shared.lds +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/intcheck1.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/intcheck2.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/intcheck3.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/[Content_Types].xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/_rels/.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/customXml/_rels/item1.xml.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/customXml/item1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/customXml/itemProps1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/docProps/app.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/docProps/core.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/docProps/thumbnail.emf +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/_rels/document.xml.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/document.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/endnotes.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/fontTable.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/footer1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/footnotes.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/media/image1.jpeg +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/media/image2.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/numbering.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/settings.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/styles.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/theme/theme1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.docx!/word/webSettings.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/[Content_Types].xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/_rels/.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/customXml/_rels/item1.xml.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/customXml/item1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/customXml/itemProps1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/docProps/app.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/docProps/core.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/docProps/thumbnail.emf +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/_rels/document.xml.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/document.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/endnotes.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/fontTable.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footer1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footer2.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footer3.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/footnotes.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/header1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/header2.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/header3.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/media/image1.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/media/image2.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/numbering.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/settings.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/styles.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/theme/theme1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20180730.docx!/word/webSettings.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/[Content_Types].xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/_rels/.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/customXml/_rels/item1.xml.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/customXml/item1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/customXml/itemProps1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/docProps/app.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/docProps/core.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/_rels/document.xml.rels +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/document.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/endnotes.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/fontTable.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/footer1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/footnotes.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/media/image1.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/media/image2.png +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/numbering.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/settings.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/styles.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/theme/theme1.xml +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20190808.docx!/word/webSettings.xml +FILE: ../../../third_party/boringssl/src/crypto/hpke/test-vectors.json +FILE: ../../../third_party/boringssl/src/crypto/obj/obj_mac.num +FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_arm_asm.S +FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c +FILE: ../../../third_party/boringssl/src/crypto/x509/charmap.h +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_time_test.cc +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ocsp.c +FILE: ../../../third_party/boringssl/src/go.mod +FILE: ../../../third_party/boringssl/src/go.sum +FILE: ../../../third_party/boringssl/src/ssl/bio_ssl.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_c_test.c +FILE: ../../../third_party/boringssl/src/util/all_tests.json +FILE: ../../../third_party/boringssl/src/util/bot/UPDATING +FILE: ../../../third_party/boringssl/src/util/bot/cmake-linux64.tar.gz.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/cmake-mac.tar.gz.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/cmake-win32.zip.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/nasm-win32.exe.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/perl-win32.zip.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/sde-linux64.tar.bz2.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/sde-win32.tar.bz2.sha1 +FILE: ../../../third_party/boringssl/src/util/bot/yasm-win32.exe.sha1 +FILE: ../../../third_party/boringssl/src/util/doc.config +FILE: ../../../third_party/boringssl/src/util/doc.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/parser.peg.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/run_cavp.go +FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate.peg.go +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart +---------------------------------------------------------------------------------------------------- +OpenSSL License + + ==================================================================== + Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. 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. + + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + + 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + + 5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + + THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + EXPRESSED 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 OpenSSL PROJECT OR + ITS 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. + ==================================================================== + + This product includes cryptographic software written by Eric Young + (eay@cryptsoft.com). This product includes software written by Tim + Hudson (tjh@cryptsoft.com). + +Original SSLeay License + +* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) +* All rights reserved. + +* This package is an SSL implementation written +* by Eric Young (eay@cryptsoft.com). +* The implementation was written so as to conform with Netscapes SSL. + +* This library is free for commercial and non-commercial use as long as +* the following conditions are aheared to. The following conditions +* apply to all code found in this distribution, be it the RC4, RSA, +* lhash, DES, etc., code; not just the SSL code. The SSL documentation +* included with this distribution is covered by the same copyright terms +* except that the holder is Tim Hudson (tjh@cryptsoft.com). + +* Copyright remains Eric Young's, and as such any Copyright notices in +* the code are not to be removed. +* If this package is used in a product, Eric Young should be given attribution +* as the author of the parts of the library used. +* This can be in the form of a textual message at program startup or +* in documentation (online or textual) provided with the package. + +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the copyright +* notice, this list of conditions and the following disclaimer. +* 2. 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. +* 3. All advertising materials mentioning features or use of this software +* must display the following acknowledgement: +* "This product includes cryptographic software written by +* Eric Young (eay@cryptsoft.com)" +* The word 'cryptographic' can be left out if the rouines from the library +* being used are not cryptographic related :-). +* 4. If you include any Windows specific code (or a derivative thereof) from +* the apps directory (application code) you must include an acknowledgement: +* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + +* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + +* The licence and distribution terms for any publically available version or +* derivative of this code cannot be changed. i.e. this code cannot simply be +* copied and put under another distribution licence +* [including the GNU Public Licence.] + +ISC license used for completely new code in BoringSSL: + +/* Copyright (c) 2015, Google Inc. + + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +The code in third_party/fiat carries the MIT license: + +Copyright (c) 2015-2016 the fiat-crypto authors (see +https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Licenses for support code + +Parts of the TLS test suite are under the Go license. This code is not included +in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so +distributing code linked against BoringSSL does not trigger this license: + +Copyright (c) 2009 The Go 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: boringssl +ORIGIN: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha256-armv8.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha512-armv8.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S +---------------------------------------------------------------------------------------------------- +Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/bsaes-armv7.S + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/bsaes-armv7.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S +---------------------------------------------------------------------------------------------------- +Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha256-armv4.S + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha256-armv4.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha512-armv4.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S +---------------------------------------------------------------------------------------------------- +Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/a_bitstr.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_bitstr.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_bool.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_d2i_fp.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_dup.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_enum.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_gentm.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_i2d_fp.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_int.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_mbstr.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_object.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_octet.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_print.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_strnid.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_time.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_type.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_utctm.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/a_utf8.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_lib.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_par.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/asn_pack.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/f_enum.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/f_int.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/f_string.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_dec.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_enc.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_fre.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_new.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_typ.c +FILE: ../../../third_party/boringssl/src/crypto/asn1/tasn_utl.c +FILE: ../../../third_party/boringssl/src/crypto/base64/base64.c +FILE: ../../../third_party/boringssl/src/crypto/bio/bio.c +FILE: ../../../third_party/boringssl/src/crypto/bio/bio_mem.c +FILE: ../../../third_party/boringssl/src/crypto/bio/connect.c +FILE: ../../../third_party/boringssl/src/crypto/bio/fd.c +FILE: ../../../third_party/boringssl/src/crypto/bio/file.c +FILE: ../../../third_party/boringssl/src/crypto/bio/hexdump.c +FILE: ../../../third_party/boringssl/src/crypto/bio/internal.h +FILE: ../../../third_party/boringssl/src/crypto/bio/printf.c +FILE: ../../../third_party/boringssl/src/crypto/bio/socket.c +FILE: ../../../third_party/boringssl/src/crypto/bn_extra/convert.c +FILE: ../../../third_party/boringssl/src/crypto/buf/buf.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/cipher_extra.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/derive_key.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_null.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_rc2.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_rc4.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/internal.h +FILE: ../../../third_party/boringssl/src/crypto/conf/conf.c +FILE: ../../../third_party/boringssl/src/crypto/conf/conf_def.h +FILE: ../../../third_party/boringssl/src/crypto/cpu-intel.c +FILE: ../../../third_party/boringssl/src/crypto/dh/check.c +FILE: ../../../third_party/boringssl/src/crypto/dh/dh.c +FILE: ../../../third_party/boringssl/src/crypto/dh/dh_test.cc +FILE: ../../../third_party/boringssl/src/crypto/digest_extra/digest_extra.c +FILE: ../../../third_party/boringssl/src/crypto/dsa/dsa.c +FILE: ../../../third_party/boringssl/src/crypto/dsa/dsa_test.cc +FILE: ../../../third_party/boringssl/src/crypto/err/err.c +FILE: ../../../third_party/boringssl/src/crypto/evp/evp.c +FILE: ../../../third_party/boringssl/src/crypto/evp/evp_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/evp/evp_ctx.c +FILE: ../../../third_party/boringssl/src/crypto/evp/internal.h +FILE: ../../../third_party/boringssl/src/crypto/evp/sign.c +FILE: ../../../third_party/boringssl/src/crypto/ex_data.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/add.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bn.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bn_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bytes.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/cmp.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/div.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/generic.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/mul.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/prime.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/random.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/shift.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/cipher.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/e_des.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/des/des.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/des/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/digest.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/digests.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/hmac/hmac.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md4/md4.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md5/md5.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/blinding.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/rsa.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha1-altivec.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha1.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha256.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha512.c +FILE: ../../../third_party/boringssl/src/crypto/hmac_extra/hmac_test.cc +FILE: ../../../third_party/boringssl/src/crypto/internal.h +FILE: ../../../third_party/boringssl/src/crypto/lhash/lhash.c +FILE: ../../../third_party/boringssl/src/crypto/mem.c +FILE: ../../../third_party/boringssl/src/crypto/obj/obj.c +FILE: ../../../third_party/boringssl/src/crypto/obj/obj_xref.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_all.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_info.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_lib.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_oth.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_pk8.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_pkey.c +FILE: ../../../third_party/boringssl/src/crypto/rc4/rc4.c +FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_test.cc +FILE: ../../../third_party/boringssl/src/crypto/stack/stack.c +FILE: ../../../third_party/boringssl/src/crypto/thread.c +FILE: ../../../third_party/boringssl/src/crypto/x509/a_digest.c +FILE: ../../../third_party/boringssl/src/crypto/x509/a_sign.c +FILE: ../../../third_party/boringssl/src/crypto/x509/a_strex.c +FILE: ../../../third_party/boringssl/src/crypto/x509/a_verify.c +FILE: ../../../third_party/boringssl/src/crypto/x509/algorithm.c +FILE: ../../../third_party/boringssl/src/crypto/x509/asn1_gen.c +FILE: ../../../third_party/boringssl/src/crypto/x509/by_dir.c +FILE: ../../../third_party/boringssl/src/crypto/x509/by_file.c +FILE: ../../../third_party/boringssl/src/crypto/x509/i2d_pr.c +FILE: ../../../third_party/boringssl/src/crypto/x509/t_crl.c +FILE: ../../../third_party/boringssl/src/crypto/x509/t_req.c +FILE: ../../../third_party/boringssl/src/crypto/x509/t_x509.c +FILE: ../../../third_party/boringssl/src/crypto/x509/t_x509a.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_att.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_cmp.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_d2.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_def.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_ext.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_lu.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_obj.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_r2x.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_req.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_set.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_txt.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_v3.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_vfy.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509name.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509rset.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_all.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_attrib.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_crl.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_exten.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_info.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_name.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_pkey.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_pubkey.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_req.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_sig.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_spki.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_val.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_x509.c +FILE: ../../../third_party/boringssl/src/decrepit/bio/base64_bio.c +FILE: ../../../third_party/boringssl/src/decrepit/blowfish/blowfish.c +FILE: ../../../third_party/boringssl/src/decrepit/cast/cast.c +FILE: ../../../third_party/boringssl/src/decrepit/cast/cast_tables.c +FILE: ../../../third_party/boringssl/src/decrepit/cast/internal.h +FILE: ../../../third_party/boringssl/src/decrepit/des/cfb64ede.c +FILE: ../../../third_party/boringssl/src/decrepit/macros.h +FILE: ../../../third_party/boringssl/src/decrepit/rc4/rc4_decrepit.c +FILE: ../../../third_party/boringssl/src/decrepit/ripemd/internal.h +FILE: ../../../third_party/boringssl/src/decrepit/ripemd/ripemd.c +FILE: ../../../third_party/boringssl/src/decrepit/rsa/rsa_decrepit.c +FILE: ../../../third_party/boringssl/src/decrepit/ssl/ssl_decrepit.c +FILE: ../../../third_party/boringssl/src/include/openssl/asn1.h +FILE: ../../../third_party/boringssl/src/include/openssl/base64.h +FILE: ../../../third_party/boringssl/src/include/openssl/bio.h +FILE: ../../../third_party/boringssl/src/include/openssl/blowfish.h +FILE: ../../../third_party/boringssl/src/include/openssl/buf.h +FILE: ../../../third_party/boringssl/src/include/openssl/cast.h +FILE: ../../../third_party/boringssl/src/include/openssl/cipher.h +FILE: ../../../third_party/boringssl/src/include/openssl/conf.h +FILE: ../../../third_party/boringssl/src/include/openssl/cpu.h +FILE: ../../../third_party/boringssl/src/include/openssl/des.h +FILE: ../../../third_party/boringssl/src/include/openssl/dh.h +FILE: ../../../third_party/boringssl/src/include/openssl/digest.h +FILE: ../../../third_party/boringssl/src/include/openssl/dsa.h +FILE: ../../../third_party/boringssl/src/include/openssl/err.h +FILE: ../../../third_party/boringssl/src/include/openssl/evp.h +FILE: ../../../third_party/boringssl/src/include/openssl/ex_data.h +FILE: ../../../third_party/boringssl/src/include/openssl/hmac.h +FILE: ../../../third_party/boringssl/src/include/openssl/lhash.h +FILE: ../../../third_party/boringssl/src/include/openssl/md4.h +FILE: ../../../third_party/boringssl/src/include/openssl/md5.h +FILE: ../../../third_party/boringssl/src/include/openssl/mem.h +FILE: ../../../third_party/boringssl/src/include/openssl/obj.h +FILE: ../../../third_party/boringssl/src/include/openssl/rc4.h +FILE: ../../../third_party/boringssl/src/include/openssl/ripemd.h +FILE: ../../../third_party/boringssl/src/include/openssl/rsa.h +FILE: ../../../third_party/boringssl/src/include/openssl/sha.h +FILE: ../../../third_party/boringssl/src/include/openssl/ssl.h +FILE: ../../../third_party/boringssl/src/include/openssl/ssl3.h +FILE: ../../../third_party/boringssl/src/include/openssl/stack.h +FILE: ../../../third_party/boringssl/src/include/openssl/thread.h +FILE: ../../../third_party/boringssl/src/include/openssl/tls1.h +FILE: ../../../third_party/boringssl/src/include/openssl/type_check.h +FILE: ../../../third_party/boringssl/src/include/openssl/x509.h +FILE: ../../../third_party/boringssl/src/include/openssl/x509_vfy.h +FILE: ../../../third_party/boringssl/src/ssl/d1_both.cc +FILE: ../../../third_party/boringssl/src/ssl/d1_pkt.cc +FILE: ../../../third_party/boringssl/src/ssl/d1_srtp.cc +FILE: ../../../third_party/boringssl/src/ssl/dtls_record.cc +FILE: ../../../third_party/boringssl/src/ssl/handshake.cc +FILE: ../../../third_party/boringssl/src/ssl/handshake_client.cc +FILE: ../../../third_party/boringssl/src/ssl/handshake_server.cc +FILE: ../../../third_party/boringssl/src/ssl/internal.h +FILE: ../../../third_party/boringssl/src/ssl/s3_both.cc +FILE: ../../../third_party/boringssl/src/ssl/s3_lib.cc +FILE: ../../../third_party/boringssl/src/ssl/s3_pkt.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_asn1.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_cert.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_cipher.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_file.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_lib.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_privkey.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_session.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_stat.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_transcript.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_x509.cc +FILE: ../../../third_party/boringssl/src/ssl/t1_enc.cc +FILE: ../../../third_party/boringssl/src/ssl/t1_lib.cc +FILE: ../../../third_party/boringssl/src/ssl/tls_method.cc +FILE: ../../../third_party/boringssl/src/ssl/tls_record.cc +---------------------------------------------------------------------------------------------------- +Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) +All rights reserved. + +This package is an SSL implementation written +by Eric Young (eay@cryptsoft.com). +The implementation was written so as to conform with Netscapes SSL. + +This library is free for commercial and non-commercial use as long as +the following conditions are aheared to. The following conditions +apply to all code found in this distribution, be it the RC4, RSA, +lhash, DES, etc., code; not just the SSL code. The SSL documentation +included with this distribution is covered by the same copyright terms +except that the holder is Tim Hudson (tjh@cryptsoft.com). + +Copyright remains Eric Young's, and as such any Copyright notices in +the code are not to be removed. +If this package is used in a product, Eric Young should be given attribution +as the author of the parts of the library used. +This can be in the form of a textual message at program startup or +in documentation (online or textual) provided with the package. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + "This product includes cryptographic software written by + Eric Young (eay@cryptsoft.com)" + The word 'cryptographic' can be left out if the rouines from the library + being used are not cryptographic related :-). +4. If you include any Windows specific code (or a derivative thereof) from + the apps directory (application code) you must include an acknowledgement: + "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + +THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + +The licence and distribution terms for any publically available version or +derivative of this code cannot be changed. i.e. this code cannot simply be +copied and put under another distribution licence +[including the GNU Public Licence.] +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/asn1_locl.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_locl.h +FILE: ../../../third_party/boringssl/src/crypto/evp/p_dsa_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_ec.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_ec_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_rsa.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_rsa_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/evp/print.c +FILE: ../../../third_party/boringssl/src/crypto/x509/rsa_pss.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2006 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/asn1_test.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/asn1/asn1_test.cc +FILE: ../../../third_party/boringssl/src/crypto/bytestring/asn1_compat.c +FILE: ../../../third_party/boringssl/src/crypto/chacha/chacha_test.cc +FILE: ../../../third_party/boringssl/src/crypto/cpu-aarch64-linux.c +FILE: ../../../third_party/boringssl/src/crypto/cpu-arm-linux.c +FILE: ../../../third_party/boringssl/src/crypto/cpu-ppc64le.c +FILE: ../../../third_party/boringssl/src/crypto/curve25519/spake25519.c +FILE: ../../../third_party/boringssl/src/crypto/curve25519/spake25519_test.cc +FILE: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/check_bn_tests.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/polyval.c +FILE: ../../../third_party/boringssl/src/crypto/obj/obj_test.cc +FILE: ../../../third_party/boringssl/src/crypto/obj/objects.go +FILE: ../../../third_party/boringssl/src/crypto/poly1305/internal.h +FILE: ../../../third_party/boringssl/src/crypto/pool/internal.h +FILE: ../../../third_party/boringssl/src/crypto/pool/pool.c +FILE: ../../../third_party/boringssl/src/crypto/pool/pool_test.cc +FILE: ../../../third_party/boringssl/src/crypto/rand_extra/deterministic.c +FILE: ../../../third_party/boringssl/src/crypto/x509/internal.h +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_test.cc +FILE: ../../../third_party/boringssl/src/decrepit/evp/dss1.c +FILE: ../../../third_party/boringssl/src/decrepit/evp/evp_do_all.c +FILE: ../../../third_party/boringssl/src/decrepit/obj/obj_decrepit.c +FILE: ../../../third_party/boringssl/src/decrepit/ripemd/ripemd_test.cc +FILE: ../../../third_party/boringssl/src/decrepit/x509/x509_decrepit.c +FILE: ../../../third_party/boringssl/src/include/openssl/asn1_mac.h +FILE: ../../../third_party/boringssl/src/include/openssl/obj_mac.h +FILE: ../../../third_party/boringssl/src/include/openssl/pool.h +FILE: ../../../third_party/boringssl/src/ssl/tls13_both.cc +FILE: ../../../third_party/boringssl/src/ssl/tls13_client.cc +FILE: ../../../third_party/boringssl/src/ssl/tls13_enc.cc +FILE: ../../../third_party/boringssl/src/ssl/tls13_server.cc +FILE: ../../../third_party/boringssl/src/util/BUILD.toplevel +FILE: ../../../third_party/boringssl/src/util/diff_asm.go +FILE: ../../../third_party/boringssl/src/util/run_android_tests.go +---------------------------------------------------------------------------------------------------- +Copyright (c) 2016, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/asn1/time_support.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/asn1/time_support.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_x509.c +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_xaux.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509cset.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2001 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/base64/base64_test.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/base64/base64_test.cc +FILE: ../../../third_party/boringssl/src/crypto/bio/bio_test.cc +FILE: ../../../third_party/boringssl/src/crypto/bio/socket_helper.c +FILE: ../../../third_party/boringssl/src/crypto/bytestring/ber.c +FILE: ../../../third_party/boringssl/src/crypto/bytestring/bytestring_test.cc +FILE: ../../../third_party/boringssl/src/crypto/bytestring/cbb.c +FILE: ../../../third_party/boringssl/src/crypto/bytestring/cbs.c +FILE: ../../../third_party/boringssl/src/crypto/bytestring/internal.h +FILE: ../../../third_party/boringssl/src/crypto/chacha/chacha.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/aead_test.cc +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_chacha20poly1305.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_tls.c +FILE: ../../../third_party/boringssl/src/crypto/cpu-arm.c +FILE: ../../../third_party/boringssl/src/crypto/crypto.c +FILE: ../../../third_party/boringssl/src/crypto/digest_extra/digest_test.cc +FILE: ../../../third_party/boringssl/src/crypto/engine/engine.c +FILE: ../../../third_party/boringssl/src/crypto/err/err_test.cc +FILE: ../../../third_party/boringssl/src/crypto/evp/evp_extra_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/aead.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/rand.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/urandom.c +FILE: ../../../third_party/boringssl/src/crypto/hkdf/hkdf.c +FILE: ../../../third_party/boringssl/src/crypto/hkdf/hkdf_test.cc +FILE: ../../../third_party/boringssl/src/crypto/lhash/lhash_test.cc +FILE: ../../../third_party/boringssl/src/crypto/pkcs7/pkcs7.c +FILE: ../../../third_party/boringssl/src/crypto/pkcs7/pkcs7_test.cc +FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs12_test.cc +FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305.c +FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_arm.c +FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_vec.c +FILE: ../../../third_party/boringssl/src/crypto/rand_extra/windows.c +FILE: ../../../third_party/boringssl/src/include/openssl/aead.h +FILE: ../../../third_party/boringssl/src/include/openssl/bytestring.h +FILE: ../../../third_party/boringssl/src/include/openssl/chacha.h +FILE: ../../../third_party/boringssl/src/include/openssl/crypto.h +FILE: ../../../third_party/boringssl/src/include/openssl/engine.h +FILE: ../../../third_party/boringssl/src/include/openssl/hkdf.h +FILE: ../../../third_party/boringssl/src/include/openssl/objects.h +FILE: ../../../third_party/boringssl/src/include/openssl/opensslconf.h +FILE: ../../../third_party/boringssl/src/include/openssl/opensslv.h +FILE: ../../../third_party/boringssl/src/include/openssl/ossl_typ.h +FILE: ../../../third_party/boringssl/src/include/openssl/pkcs12.h +FILE: ../../../third_party/boringssl/src/include/openssl/pkcs7.h +FILE: ../../../third_party/boringssl/src/include/openssl/poly1305.h +FILE: ../../../third_party/boringssl/src/include/openssl/rand.h +FILE: ../../../third_party/boringssl/src/include/openssl/safestack.h +FILE: ../../../third_party/boringssl/src/ssl/ssl_test.cc +FILE: ../../../third_party/boringssl/src/tool/args.cc +FILE: ../../../third_party/boringssl/src/tool/client.cc +FILE: ../../../third_party/boringssl/src/tool/const.cc +FILE: ../../../third_party/boringssl/src/tool/digest.cc +FILE: ../../../third_party/boringssl/src/tool/internal.h +FILE: ../../../third_party/boringssl/src/tool/pkcs12.cc +FILE: ../../../third_party/boringssl/src/tool/server.cc +FILE: ../../../third_party/boringssl/src/tool/speed.cc +FILE: ../../../third_party/boringssl/src/tool/tool.cc +FILE: ../../../third_party/boringssl/src/tool/transport_common.cc +FILE: ../../../third_party/boringssl/src/tool/transport_common.h +FILE: ../../../third_party/boringssl/src/util/make_errors.go +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/bio/pair.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/bio/pair.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2003 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/bn_extra/bn_asn1.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/err_data.c +FILE: ../../../third_party/boringssl/src/crypto/bn_extra/bn_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/cmac/cmac_test.cc +FILE: ../../../third_party/boringssl/src/crypto/conf/internal.h +FILE: ../../../third_party/boringssl/src/crypto/curve25519/asm/x25519-asm-arm.S +FILE: ../../../third_party/boringssl/src/crypto/curve25519/ed25519_test.cc +FILE: ../../../third_party/boringssl/src/crypto/curve25519/x25519_test.cc +FILE: ../../../third_party/boringssl/src/crypto/err/err_data_generate.go +FILE: ../../../third_party/boringssl/src/crypto/evp/pbkdf_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p224-64.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/util.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/internal.h +FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs8_test.cc +FILE: ../../../third_party/boringssl/src/crypto/poly1305/poly1305_test.cc +FILE: ../../../third_party/boringssl/src/crypto/refcount_c11.c +FILE: ../../../third_party/boringssl/src/crypto/refcount_lock.c +FILE: ../../../third_party/boringssl/src/crypto/refcount_test.cc +FILE: ../../../third_party/boringssl/src/crypto/thread_none.c +FILE: ../../../third_party/boringssl/src/crypto/thread_pthread.c +FILE: ../../../third_party/boringssl/src/crypto/thread_test.cc +FILE: ../../../third_party/boringssl/src/crypto/thread_win.c +FILE: ../../../third_party/boringssl/src/include/openssl/buffer.h +FILE: ../../../third_party/boringssl/src/include/openssl/cmac.h +FILE: ../../../third_party/boringssl/src/include/openssl/curve25519.h +FILE: ../../../third_party/boringssl/src/include/openssl/dtls1.h +FILE: ../../../third_party/boringssl/src/include/openssl/srtp.h +FILE: ../../../third_party/boringssl/src/ssl/ssl_aead_ctx.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_buffer.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_key_share.cc +FILE: ../../../third_party/boringssl/src/tool/ciphers.cc +FILE: ../../../third_party/boringssl/src/tool/generate_ed25519.cc +FILE: ../../../third_party/boringssl/src/tool/genrsa.cc +FILE: ../../../third_party/boringssl/src/tool/rand.cc +FILE: ../../../third_party/boringssl/src/util/all_tests.go +FILE: ../../../third_party/boringssl/src/util/bot/DEPS +---------------------------------------------------------------------------------------------------- +Copyright (c) 2015, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/buf/buf_test.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/crypto_test_data.cc +FILE: ../../../third_party/boringssl/src/crypto/buf/buf_test.cc +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesctrhmac.c +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesgcmsiv.c +FILE: ../../../third_party/boringssl/src/crypto/compiler_test.cc +FILE: ../../../third_party/boringssl/src/crypto/err/internal.h +FILE: ../../../third_party/boringssl/src/crypto/evp/p_ed25519.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_ed25519_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/evp/scrypt_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bcm.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/bn_test_to_fuzzer.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/delocate.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/is_fips.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/ctrdrbg.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/ctrdrbg_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/self_check/self_check.c +FILE: ../../../third_party/boringssl/src/crypto/pkcs7/internal.h +FILE: ../../../third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.c +FILE: ../../../third_party/boringssl/src/crypto/rand_extra/forkunsafe.c +FILE: ../../../third_party/boringssl/src/crypto/rand_extra/fuchsia.c +FILE: ../../../third_party/boringssl/src/crypto/rand_extra/rand_extra.c +FILE: ../../../third_party/boringssl/src/decrepit/cfb/cfb.c +FILE: ../../../third_party/boringssl/src/decrepit/cfb/cfb_test.cc +FILE: ../../../third_party/boringssl/src/include/openssl/is_boringssl.h +FILE: ../../../third_party/boringssl/src/include/openssl/span.h +FILE: ../../../third_party/boringssl/src/ssl/span_test.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_versions.cc +FILE: ../../../third_party/boringssl/src/tool/file.cc +FILE: ../../../third_party/boringssl/src/tool/sign.cc +FILE: ../../../third_party/boringssl/src/util/ar/ar.go +FILE: ../../../third_party/boringssl/src/util/check_imported_libraries.go +FILE: ../../../third_party/boringssl/src/util/convert_comments.go +FILE: ../../../third_party/boringssl/src/util/embed_test_data.go +FILE: ../../../third_party/boringssl/src/util/fipstools/break-hash.go +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_aes_gcm_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_aes_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ctr_drbg_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_keypair_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_pkv_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_siggen_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_ecdsa2_sigver_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_hmac_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_keywrap_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_main.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_rsa2_keygen_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_rsa2_siggen_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_rsa2_sigver_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_sha_monte_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_sha_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_tdes_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_test_util.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_test_util.h +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/test_fips.c +FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate.go +FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate.peg +FILE: ../../../third_party/boringssl/src/util/fipstools/delocate/delocate_test.go +FILE: ../../../third_party/boringssl/src/util/fipstools/fipscommon/const.go +FILE: ../../../third_party/boringssl/src/util/fipstools/inject_hash/inject_hash.go +---------------------------------------------------------------------------------------------------- +Copyright (c) 2017, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/bytestring/unicode.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/abi_self_test.cc +FILE: ../../../third_party/boringssl/src/crypto/bytestring/unicode.c +FILE: ../../../third_party/boringssl/src/crypto/chacha/internal.h +FILE: ../../../third_party/boringssl/src/crypto/cpu-aarch64-fuchsia.c +FILE: ../../../third_party/boringssl/src/crypto/cpu-arm-linux.h +FILE: ../../../third_party/boringssl/src/crypto/cpu-arm-linux_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/div_extra.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/felem.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_ec_scalar_base_mult_tests.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_p256-x86_64-tests.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/scalar.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/simple_mul.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md5/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/md5/md5_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/sha/sha_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/tls/internal.h +FILE: ../../../third_party/boringssl/src/crypto/hrss/hrss.c +FILE: ../../../third_party/boringssl/src/crypto/hrss/hrss_test.cc +FILE: ../../../third_party/boringssl/src/crypto/hrss/internal.h +FILE: ../../../third_party/boringssl/src/crypto/impl_dispatch_test.cc +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_test.cc +FILE: ../../../third_party/boringssl/src/crypto/rand_extra/rand_test.cc +FILE: ../../../third_party/boringssl/src/crypto/self_test.cc +FILE: ../../../third_party/boringssl/src/crypto/stack/stack_test.cc +FILE: ../../../third_party/boringssl/src/crypto/x509v3/internal.h +FILE: ../../../third_party/boringssl/src/include/openssl/e_os2.h +FILE: ../../../third_party/boringssl/src/include/openssl/hrss.h +FILE: ../../../third_party/boringssl/src/ssl/handoff.cc +FILE: ../../../third_party/boringssl/src/util/ar/ar_test.go +FILE: ../../../third_party/boringssl/src/util/check_filenames.go +FILE: ../../../third_party/boringssl/src/util/convert_wycheproof.go +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_kas_test.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/cavp/cavp_tlskdf_test.cc +FILE: ../../../third_party/boringssl/src/util/godeps.go +FILE: ../../../third_party/boringssl/src/util/make_prefix_headers.go +FILE: ../../../third_party/boringssl/src/util/read_symbols.go +FILE: ../../../third_party/boringssl/src/util/testresult/testresult.go +---------------------------------------------------------------------------------------------------- +Copyright (c) 2018, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/cipher_extra/cipher_test.cc +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/cipher_test.cc +FILE: ../../../third_party/boringssl/src/crypto/evp/evp_test.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2015 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesccm.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/e_aesccm.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/cbc.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/cfb.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/ctr.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/gcm.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/gcm_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/ofb.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2008 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/cipher_extra/tls_cbc.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/cipher_extra/tls_cbc.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2012 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/cmac/cmac.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/cmac/cmac.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2010 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/constant_time_test.cc +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/constant_time_test.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + "This product includes cryptographic software written by + Eric Young (eay@cryptsoft.com)" + The word 'cryptographic' can be left out if the rouines from the library + being used are not cryptographic related :-). +4. If you include any Windows specific code (or a derivative thereof) from + the apps directory (application code) you must include an acknowledgement: + "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + +THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + +The licence and distribution terms for any publically available version or +derivative of this code cannot be changed. i.e. this code cannot simply be +copied and put under another distribution licence +[including the GNU Public Licence.] +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/curve25519/curve25519.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/crypto_test.cc +FILE: ../../../third_party/boringssl/src/crypto/curve25519/curve25519.c +FILE: ../../../third_party/boringssl/src/crypto/curve25519/curve25519_tables.h +FILE: ../../../third_party/boringssl/src/crypto/curve25519/internal.h +FILE: ../../../third_party/boringssl/src/crypto/dsa/internal.h +FILE: ../../../third_party/boringssl/src/crypto/ec_extra/hash_to_curve.c +FILE: ../../../third_party/boringssl/src/crypto/ec_extra/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_tables.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256_table.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/fork_detect.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/fork_detect.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/fork_detect_test.cc +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/getrandom_fillin.h +FILE: ../../../third_party/boringssl/src/crypto/hpke/hpke.c +FILE: ../../../third_party/boringssl/src/crypto/hpke/hpke_test.cc +FILE: ../../../third_party/boringssl/src/crypto/hpke/internal.h +FILE: ../../../third_party/boringssl/src/crypto/trust_token/pmbtoken.c +FILE: ../../../third_party/boringssl/src/crypto/trust_token/trust_token_test.cc +FILE: ../../../third_party/boringssl/src/crypto/trust_token/voprf.c +FILE: ../../../third_party/boringssl/src/decrepit/xts/xts_test.cc +FILE: ../../../third_party/boringssl/src/include/openssl/trust_token.h +FILE: ../../../third_party/boringssl/src/tool/fd.cc +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/nointeractive.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/aead.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/kdf.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/keyedMac.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/rsa.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/subprocess_test.go +FILE: ../../../third_party/boringssl/src/util/testconfig/testconfig.go +---------------------------------------------------------------------------------------------------- +Copyright (c) 2020, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/dh/dh_asn1.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/dh/dh_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/dsa/dsa_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_asn1.c +FILE: ../../../third_party/boringssl/src/include/openssl/asn1t.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2000-2005 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/dh/params.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/dh/params.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2011 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/ec_extra/ec_asn1.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/ec_extra/ec_asn1.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2000-2003 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/ec_extra/ec_derive.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/ec_extra/ec_derive.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_x25519.c +FILE: ../../../third_party/boringssl/src/crypto/evp/p_x25519_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes_nohw.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/fips_shared_support.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/modes/gcm_nohw.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rand/urandom_test.cc +FILE: ../../../third_party/boringssl/src/crypto/siphash/siphash.c +FILE: ../../../third_party/boringssl/src/crypto/siphash/siphash_test.cc +FILE: ../../../third_party/boringssl/src/crypto/trust_token/internal.h +FILE: ../../../third_party/boringssl/src/crypto/trust_token/trust_token.c +FILE: ../../../third_party/boringssl/src/decrepit/blowfish/blowfish_test.cc +FILE: ../../../third_party/boringssl/src/decrepit/cast/cast_test.cc +FILE: ../../../third_party/boringssl/src/include/openssl/siphash.h +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/acvp.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/acvp/acvp.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/interactive.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/parser.peg +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/block.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/drbg.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/ecdsa.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/hash.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/hmac.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go +FILE: ../../../third_party/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 2019, Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdh/ecdh.c +FILE: ../../../third_party/boringssl/src/include/openssl/ecdh.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2000-2002 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c + ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdh/ecdh.c +FILE: ../../../third_party/boringssl/src/include/openssl/ecdh.h +---------------------------------------------------------------------------------------------------- +Copyright 2002 Sun Microsystems, 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: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/ecdsa_extra/ecdsa_asn1.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/ecdsa_extra/ecdsa_asn1.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc +FILE: ../../../third_party/boringssl/src/include/openssl/ecdsa.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/err/err.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/err/err.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/blinding.c +FILE: ../../../third_party/boringssl/src/include/openssl/bn.h +FILE: ../../../third_party/boringssl/src/include/openssl/err.h +FILE: ../../../third_party/boringssl/src/include/openssl/tls1.h +FILE: ../../../third_party/boringssl/src/ssl/d1_srtp.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_session.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/evp/digestsign.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/evp/digestsign.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2006,2007 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/evp/pbkdf.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/evp/pbkdf.c +FILE: ../../../third_party/boringssl/src/crypto/pkcs8/internal.h +FILE: ../../../third_party/boringssl/src/crypto/pkcs8/p5_pbev2.c +FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs8.c +FILE: ../../../third_party/boringssl/src/crypto/pkcs8/pkcs8_x509.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_trs.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x509spki.c +FILE: ../../../third_party/boringssl/src/crypto/x509/x_x509a.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/tab_test.cc +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_akey.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_akeya.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_bcons.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_bitst.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_enum.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_extku.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ia5.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_info.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_lib.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_prn.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_skey.c +FILE: ../../../third_party/boringssl/src/include/openssl/pkcs8.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/evp/scrypt.c + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/evp/scrypt.c +---------------------------------------------------------------------------------------------------- +Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/aes.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/mode_wrappers.c +FILE: ../../../third_party/boringssl/src/include/openssl/aes.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/key_wrap.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/aes/key_wrap.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/cipher/e_aes.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2001-2011 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/ctx.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/ctx.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_montgomery.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/oct.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/simple.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/wnaf.c +FILE: ../../../third_party/boringssl/src/include/openssl/ec.h +FILE: ../../../third_party/boringssl/src/include/openssl/ec_key.h +FILE: ../../../third_party/boringssl/src/ssl/d1_both.cc +FILE: ../../../third_party/boringssl/src/ssl/d1_pkt.cc +FILE: ../../../third_party/boringssl/src/ssl/dtls_record.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/ex_data.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/gcd.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/prime.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/random.c +FILE: ../../../third_party/boringssl/src/crypto/internal.h +FILE: ../../../third_party/boringssl/src/include/openssl/base.h +FILE: ../../../third_party/boringssl/src/include/openssl/ex_data.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/internal.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/internal.h +FILE: ../../../third_party/boringssl/src/crypto/obj/obj_dat.h +FILE: ../../../third_party/boringssl/src/include/openssl/bn.h +FILE: ../../../third_party/boringssl/src/include/openssl/nid.h +FILE: ../../../third_party/boringssl/src/include/openssl/pem.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) +All rights reserved. + +This package is an SSL implementation written +by Eric Young (eay@cryptsoft.com). +The implementation was written so as to conform with Netscapes SSL. + +This library is free for commercial and non-commercial use as long as +the following conditions are aheared to. The following conditions +apply to all code found in this distribution, be it the RC4, RSA, +lhash, DES, etc., code; not just the SSL code. The SSL documentation +included with this distribution is covered by the same copyright terms +except that the holder is Tim Hudson (tjh@cryptsoft.com). + +Copyright remains Eric Young's, and as such any Copyright notices in +the code are not to be removed. +If this package is used in a product, Eric Young should be given attribution +as the author of the parts of the library used. +This can be in the form of a textual message at program startup or +in documentation (online or textual) provided with the package. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + "This product includes cryptographic software written by + Eric Young (eay@cryptsoft.com)" + The word 'cryptographic' can be left out if the rouines from the library + being used are not cryptographic related :-). +4. If you include any Windows specific code (or a derivative thereof) from + the apps directory (application code) you must include an acknowledgement: + "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + +THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + +The licence and distribution terms for any publically available version or +derivative of this code cannot be changed. i.e. this code cannot simply be +copied and put under another distribution licence +[including the GNU Public Licence.] +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/jacobi.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/jacobi.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/sqrt.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery_inv.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/montgomery_inv.c +---------------------------------------------------------------------------------------------------- +Copyright 2016 Brian Smith. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.c + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/bn/rsaz_exp.h +---------------------------------------------------------------------------------------------------- +Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. +Copyright (c) 2012, Intel Corporation. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/md32_common.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/digest/md32_common.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec.c + ../../../third_party/boringssl/src/crypto/fipsmodule/bn/exponentiation.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_key.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/ec_montgomery.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/internal.h +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/oct.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/simple.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/wnaf.c +FILE: ../../../third_party/boringssl/src/include/openssl/ec.h +FILE: ../../../third_party/boringssl/src/include/openssl/ec_key.h +---------------------------------------------------------------------------------------------------- +Copyright 2002 Sun Microsystems, 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: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_tables.go + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_tables.go +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64-table.h +---------------------------------------------------------------------------------------------------- +Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. +Copyright (c) 2015, Intel Inc. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.c + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.c +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/p256-x86_64.h +---------------------------------------------------------------------------------------------------- +Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. +Copyright (c) 2014, Intel Corporation. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/padding.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/rsa/padding.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2005 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/fipsmodule/tls/kdf.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/tls/kdf.c +FILE: ../../../third_party/boringssl/src/decrepit/ssl/ssl_decrepit.c +FILE: ../../../third_party/boringssl/src/include/openssl/ssl.h +FILE: ../../../third_party/boringssl/src/ssl/handshake_client.cc +FILE: ../../../third_party/boringssl/src/ssl/handshake_server.cc +FILE: ../../../third_party/boringssl/src/ssl/internal.h +FILE: ../../../third_party/boringssl/src/ssl/s3_lib.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_cert.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_cipher.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_file.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_lib.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_transcript.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_x509.cc +FILE: ../../../third_party/boringssl/src/ssl/t1_enc.cc +FILE: ../../../third_party/boringssl/src/ssl/t1_lib.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/hrss/asm/poly_rq_mul.S +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/hrss/asm/poly_rq_mul.S +---------------------------------------------------------------------------------------------------- +Copyright (c) 2017, the HRSS authors. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/pem/pem_all.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_all.c +FILE: ../../../third_party/boringssl/src/decrepit/dh/dh_decrepit.c +FILE: ../../../third_party/boringssl/src/decrepit/dsa/dsa_decrepit.c +FILE: ../../../third_party/boringssl/src/include/openssl/ssl3.h +FILE: ../../../third_party/boringssl/src/ssl/handshake.cc +FILE: ../../../third_party/boringssl/src/ssl/s3_both.cc +FILE: ../../../third_party/boringssl/src/ssl/s3_pkt.cc +FILE: ../../../third_party/boringssl/src/ssl/tls_record.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c +---------------------------------------------------------------------------------------------------- +Copyright 2006-2017 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509/vpm_int.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509/vpm_int.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509/x509_time_test.cc + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_time_test.cc +---------------------------------------------------------------------------------------------------- +Copyright 2017 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509/x509_vpm.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509/x509_vpm.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_cache.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_data.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_int.h +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_lib.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_map.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_node.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/pcy_tree.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2004 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509/x_algor.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509/x_algor.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2000 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/ext_dat.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509v3/ext_dat.h +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_cpols.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_int.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_purp.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3name_test.cc +FILE: ../../../third_party/boringssl/src/include/openssl/x509v3.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_alt.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_alt.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_utl.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_conf.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_conf.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999-2002 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_crld.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_crld.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_genn.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999-2008 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_ncons.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ncons.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pcons.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pmaps.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2003 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + licensing@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_ocsp.c + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_ocsp.c +---------------------------------------------------------------------------------------------------- +Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/crypto/x509v3/v3_pci.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pci.c +FILE: ../../../third_party/boringssl/src/crypto/x509v3/v3_pcia.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2004 Kungliga Tekniska Högskolan +(Royal Institute of Technology, Stockholm, Sweden). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/decrepit/xts/xts.c +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/decrepit/xts/xts.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2011 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/include/openssl/arm_arch.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/include/openssl/arm_arch.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@openssl.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.openssl.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/ssl/bio_ssl.cc + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/ssl/bio_ssl.cc +---------------------------------------------------------------------------------------------------- +Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +https://www.openssl.org/source/license.html +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/ssl/d1_lib.cc +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/ssl/d1_lib.cc +FILE: ../../../third_party/boringssl/src/ssl/dtls_method.cc +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + +4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + endorse or promote products derived from this software without + prior written permission. For written permission, please contact + openssl-core@OpenSSL.org. + +5. Products derived from this software may not be called "OpenSSL" + nor may "OpenSSL" appear in their names without prior written + permission of the OpenSSL Project. + +6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by the OpenSSL Project + for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + +THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +EXPRESSED 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 OpenSSL PROJECT OR +ITS 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/ssl/ssl_asn1.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/ssl/ssl_asn1.cc +FILE: ../../../third_party/boringssl/src/ssl/ssl_stat.cc +---------------------------------------------------------------------------------------------------- +Copyright 2005 Nokia. All rights reserved. + +The portions of the attached software ("Contribution") is developed by +Nokia Corporation and is licensed pursuant to the OpenSSL open source +license. + +The Contribution, originally written by Mika Kousa and Pasi Eronen of +Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites +support (see RFC 4279) to OpenSSL. + +No patent licenses or other rights except those expressly stated in +the OpenSSL open source license shall be deemed granted or received +expressly, by implication, estoppel, or otherwise. + +No assurances are provided by Nokia that the Contribution does not +infringe the patent or other intellectual property rights of any third +party or that the license provides you with all the necessary rights +to make use of the Contribution. + +THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN +ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA +SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY +OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR +OTHERWISE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/fiat/LICENSE +TYPE: LicenseType.mit +FILE: ../../../third_party/boringssl/src/third_party/fiat/METADATA +FILE: ../../../third_party/boringssl/src/third_party/fiat/curve25519_32.h +FILE: ../../../third_party/boringssl/src/third_party/fiat/curve25519_64.h +FILE: ../../../third_party/boringssl/src/third_party/fiat/p256_32.h +FILE: ../../../third_party/boringssl/src/third_party/fiat/p256_64.h +---------------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015-2016 the fiat-crypto authors (see +https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/CONTRIBUTORS +FILE: ../../../third_party/boringssl/src/third_party/googletest/METADATA +FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/Config.cmake.in +FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/gtest.pc.in +FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/gtest_main.pc.in +FILE: ../../../third_party/boringssl/src/third_party/googletest/cmake/libgtest.la.in +FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest.cbproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest.groupproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_main.cbproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_unittest.cbproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-param-test.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-test-part.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-filepath.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest-md.sln +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest-md.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest-md.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest.sln +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main-md.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main-md.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_main.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test-md.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test-md.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_prod_test.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest-md.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest-md.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest.vcxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/msvc/2010/gtest_unittest.vcxproj.filters +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-all.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-filepath.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-port.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-test-part.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/DebugProject.xcconfig +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/FrameworkTarget.xcconfig +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/General.xcconfig +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/ReleaseProject.xcconfig +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/StaticLibraryTarget.xcconfig +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Config/TestTarget.xcconfig +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Resources/Info.plist +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/Info.plist +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/widget.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/widget.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/Samples/FrameworkSample/widget_test.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/xcode/gtest.xcodeproj/project.pbxproj +---------------------------------------------------------------------------------------------------- +Copyright 2008, 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_all.cc +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_all.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/codegear/gtest_link.cc +---------------------------------------------------------------------------------------------------- +Copyright 2009, 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-matchers.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-matchers.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-printers.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-spi.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-matchers.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-printers.cc +---------------------------------------------------------------------------------------------------- +Copyright 2007, 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest_pred_impl.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest_pred_impl.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest_prod.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest_main.cc +---------------------------------------------------------------------------------------------------- +Copyright 2006, 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest-port.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest-port.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest-printers.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/custom/gtest.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-port-arch.h +---------------------------------------------------------------------------------------------------- +Copyright 2015, 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-death-test-internal.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-death-test.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-message.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-death-test-internal.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-internal.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-port.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-string.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample1.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample1.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample1_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample2.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample2.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample2_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample3-inl.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample3_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample4.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample4.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample4_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample5_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-death-test.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-internal-inl.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest.cc +---------------------------------------------------------------------------------------------------- +Copyright 2005, 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-param-util.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/gtest-typed-test.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-param-util.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-type-util.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/include/gtest/internal/gtest-type-util.h.pump +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/prime_tables.h +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample6_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample7_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample8_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/src/gtest-typed-test.cc +---------------------------------------------------------------------------------------------------- +Copyright 2008 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/third_party/googletest/samples/sample10_unittest.cc +TYPE: LicenseType.bsd +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample10_unittest.cc +FILE: ../../../third_party/boringssl/src/third_party/googletest/samples/sample9_unittest.cc +---------------------------------------------------------------------------------------------------- +Copyright 2009 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: boringssl +ORIGIN: ../../../third_party/boringssl/src/util/compare_benchmarks.go +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/src/util/compare_benchmarks.go +---------------------------------------------------------------------------------------------------- +Copyright (c) 2020 Google Inc. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: boringssl +ORIGIN: null +TYPE: LicenseType.unknown +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/chacha/chacha-armv8.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/aesv8-armx64.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/armv8-mont.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/ghash-neon-armv8.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/sha1-armv8.S +FILE: ../../../third_party/boringssl/ios-aarch64/crypto/fipsmodule/vpaes-armv8.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/chacha/chacha-armv4.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/aesv8-armx32.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/armv4-mont.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/ghash-armv4.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/ghashv8-armx32.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/sha1-armv4-large.S +FILE: ../../../third_party/boringssl/ios-arm/crypto/fipsmodule/vpaes-armv7.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S +FILE: ../../../third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S +FILE: ../../../third_party/boringssl/linux-arm/crypto/fipsmodule/vpaes-armv7.S +FILE: ../../../third_party/boringssl/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S +FILE: ../../../third_party/boringssl/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S +FILE: ../../../third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S +FILE: ../../../third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/chacha/chacha-x86.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/aesni-x86.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/bn-586.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/co-586.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/ghash-ssse3-x86.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/ghash-x86.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/md5-586.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/sha1-586.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/sha256-586.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/sha512-586.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/vpaes-x86.S +FILE: ../../../third_party/boringssl/mac-x86/crypto/fipsmodule/x86-mont.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/chacha/chacha-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/aesni-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/ghash-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/md5-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/rsaz-avx2.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/sha1-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/sha256-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/sha512-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/x86_64-mont.S +FILE: ../../../third_party/boringssl/mac-x86_64/crypto/fipsmodule/x86_64-mont5.S +FILE: ../../../third_party/boringssl/win-x86/crypto/chacha/chacha-x86.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/aesni-x86.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/bn-586.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/co-586.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/ghash-ssse3-x86.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/ghash-x86.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/md5-586.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/sha1-586.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/sha256-586.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/sha512-586.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/vpaes-x86.asm +FILE: ../../../third_party/boringssl/win-x86/crypto/fipsmodule/x86-mont.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/chacha/chacha-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/md5-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont.asm +FILE: ../../../third_party/boringssl/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm +---------------------------------------------------------------------------------------------------- + +==================================================================================================== + +==================================================================================================== +LIBRARY: colorama +ORIGIN: ../../../third_party/colorama/src/LICENSE.txt +TYPE: LicenseType.bsd +FILE: ../../../third_party/colorama/src/MANIFEST.in +FILE: ../../../third_party/colorama/src/screenshots/ubuntu-demo.png +FILE: ../../../third_party/colorama/src/screenshots/windows-demo.png +---------------------------------------------------------------------------------------------------- +Copyright (c) 2010 Jonathan Hartley +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 the copyright holders, nor those 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 HOLDER 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: dart +LIBRARY: double-conversion +LIBRARY: icu +ORIGIN: ../../../third_party/dart/benchmarks/Richards/dart/Richards.dart +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/benchmarks/Richards/dart/Richards.dart +FILE: ../../../third_party/dart/benchmarks/Richards/dart2/Richards.dart +FILE: ../../../third_party/dart/benchmarks/Richards/javascript/Richards.js +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.cc +FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2006-2008 the V8 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: dart +ORIGIN: ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/.gitconfig +FILE: ../../../third_party/dart/.mailmap +FILE: ../../../third_party/dart/.style.yapf +FILE: ../../../third_party/dart/.vpython +FILE: ../../../third_party/dart/PATENT_GRANT +FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/sample.json +FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart2/sample.json +FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/helloworld.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart2/helloworld.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/helloworld.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart2/helloworld.dart +FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/iterable.dart +FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart2/iterable.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/datext_latin1_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/entext_ascii_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/netext_3_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/rutext_2_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/sktext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/zhtext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/datext_latin1_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/entext_ascii_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/netext_3_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/rutext_2_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/sktext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/zhtext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/datext_latin1_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/entext_ascii_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/netext_3_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/rutext_2_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/sktext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/zhtext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/datext_latin1_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/entext_ascii_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/netext_3_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/rutext_2_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/sktext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/zhtext_10k.dart +FILE: ../../../third_party/dart/client/idea/.idea/.name +FILE: ../../../third_party/dart/client/idea/.idea/inspectionProfiles/Project_Default.xml +FILE: ../../../third_party/dart/client/idea/.idea/vcs.xml +FILE: ../../../third_party/dart/runtime/.clang-tidy +FILE: ../../../third_party/dart/runtime/CPPLINT.cfg +FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_arm.S +FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_arm64.S +FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_x64.S +FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_x64_win.S +FILE: ../../../third_party/dart/runtime/bin/ffi_test/clobber_x86.S +FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions_helpers.S +FILE: ../../../third_party/dart/runtime/docs/compiler/images/catch-block-entry-0.png +FILE: ../../../third_party/dart/runtime/docs/compiler/images/catch-block-entry-1.png +FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-dictionary.png +FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-linear.png +FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-monomorphic.png +FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-singletarget.png +FILE: ../../../third_party/dart/runtime/docs/images/aot-ic-unlinked.png +FILE: ../../../third_party/dart/runtime/docs/images/aot.png +FILE: ../../../third_party/dart/runtime/docs/images/dart-to-kernel.png +FILE: ../../../third_party/dart/runtime/docs/images/flutter-cfe.png +FILE: ../../../third_party/dart/runtime/docs/images/images.graffle!images +FILE: ../../../third_party/dart/runtime/docs/images/inline-cache-1.png +FILE: ../../../third_party/dart/runtime/docs/images/isolates.png +FILE: ../../../third_party/dart/runtime/docs/images/kernel-loaded-1.png +FILE: ../../../third_party/dart/runtime/docs/images/kernel-loaded-2.png +FILE: ../../../third_party/dart/runtime/docs/images/kernel-service.png +FILE: ../../../third_party/dart/runtime/docs/images/optimizing-compilation.png +FILE: ../../../third_party/dart/runtime/docs/images/raw-function-lazy-compile.png +FILE: ../../../third_party/dart/runtime/docs/images/snapshot-appjit.png +FILE: ../../../third_party/dart/runtime/docs/images/snapshot-with-code.png +FILE: ../../../third_party/dart/runtime/docs/images/snapshot.png +FILE: ../../../third_party/dart/runtime/docs/images/unoptimized-compilation.png +FILE: ../../../third_party/dart/runtime/docs/infra/images/isolated-out-browser.png +FILE: ../../../third_party/dart/runtime/docs/infra/images/isolated-out-link.png +FILE: ../../../third_party/dart/runtime/observatory/lib/elements.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/img/chromium_icon.png +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/img/dart_icon.png +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/img/isolate_icon.png +FILE: ../../../third_party/dart/runtime/observatory/web/favicon.ico +FILE: ../../../third_party/dart/runtime/observatory/web/index.html +FILE: ../../../third_party/dart/runtime/observatory/web/third_party/trace_viewer_full.html +FILE: ../../../third_party/dart/runtime/observatory/web/timeline.html +FILE: ../../../third_party/dart/runtime/observatory_2/lib/elements.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/img/chromium_icon.png +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/img/dart_icon.png +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/img/isolate_icon.png +FILE: ../../../third_party/dart/runtime/observatory_2/web/favicon.ico +FILE: ../../../third_party/dart/runtime/observatory_2/web/index.html +FILE: ../../../third_party/dart/runtime/observatory_2/web/third_party/trace_viewer_full.html +FILE: ../../../third_party/dart/runtime/observatory_2/web/timeline.html +FILE: ../../../third_party/dart/runtime/tools/wiki/styles/style.scss +FILE: ../../../third_party/dart/runtime/tools/wiki/templates/includes/auto-refresh.html +FILE: ../../../third_party/dart/runtime/tools/wiki/templates/includes/favicon.html +FILE: ../../../third_party/dart/runtime/tools/wiki/templates/page.html +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/floatx10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/int8x10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/regress46127/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct128bytesx1/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct12bytesFloatx6/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesHomogenousx10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_2/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_2/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_2/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_2/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct3bytesx10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct8bytesx1/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union16bytesHomogenousx10/x64_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm64_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/arm_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/ia32_android.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/ia32_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/ia32_win.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/x64_ios.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/x64_linux.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/x64_macos.expect +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_tests/union5bytesPackedx10/x64_win.expect +FILE: ../../../third_party/dart/samples/ffi/sqlite/docs/lib/scenario-default.svg +FILE: ../../../third_party/dart/samples/ffi/sqlite/docs/lib/scenario-full.svg +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/docs/lib/scenario-default.svg +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/docs/lib/scenario-full.svg +FILE: ../../../third_party/dart/sdk/lib/_internal/allowed_experiments.json +FILE: ../../../third_party/dart/sdk/lib/html/html_common/conversions_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/html_common.dart +FILE: ../../../third_party/dart/sdk/lib/libraries.json +FILE: ../../../third_party/dart/sdk/lib/vmservice_libraries.json +FILE: ../../../third_party/dart/third_party/clang.tar.gz.sha1 +---------------------------------------------------------------------------------------------------- +Copyright 2012, the Dart project authors. + +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 LLC 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: dart +ORIGIN: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/BigIntParsePrint.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/native_version.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/native_version_dummy.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart/native_version_javascript.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/native_version.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/native_version_dummy.dart +FILE: ../../../third_party/dart/benchmarks/BigIntParsePrint/dart2/native_version_javascript.dart +FILE: ../../../third_party/dart/benchmarks/Calls/dart/Calls.dart +FILE: ../../../third_party/dart/benchmarks/Calls/dart2/Calls.dart +FILE: ../../../third_party/dart/benchmarks/Example/dart/Example.dart +FILE: ../../../third_party/dart/benchmarks/Example/dart2/Example.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/digest.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/dlopen_helper.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart/types.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/digest.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/dlopen_helper.dart +FILE: ../../../third_party/dart/benchmarks/FfiBoringssl/dart2/types.dart +FILE: ../../../third_party/dart/benchmarks/FfiCall/dart/FfiCall.dart +FILE: ../../../third_party/dart/benchmarks/FfiCall/dart/dlopen_helper.dart +FILE: ../../../third_party/dart/benchmarks/FfiCall/dart2/FfiCall.dart +FILE: ../../../third_party/dart/benchmarks/FfiCall/dart2/dlopen_helper.dart +FILE: ../../../third_party/dart/benchmarks/FfiCall/native/native_functions.c +FILE: ../../../third_party/dart/benchmarks/FfiMemory/dart/FfiMemory.dart +FILE: ../../../third_party/dart/benchmarks/FfiMemory/dart2/FfiMemory.dart +FILE: ../../../third_party/dart/benchmarks/FfiStruct/dart/FfiStruct.dart +FILE: ../../../third_party/dart/benchmarks/FfiStruct/dart2/FfiStruct.dart +FILE: ../../../third_party/dart/benchmarks/Isolate/dart/Isolate.dart +FILE: ../../../third_party/dart/benchmarks/Isolate/dart2/Isolate.dart +FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/IsolateJson.dart +FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart2/IsolateJson.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/IsolateSpawnMemory.dart +FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart +FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/SoundSplayTreeSieve.dart +FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/sound_splay_tree.dart +FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart2/SoundSplayTreeSieve.dart +FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart2/sound_splay_tree.dart +FILE: ../../../third_party/dart/runtime/bin/elf_loader.cc +FILE: ../../../third_party/dart/runtime/bin/elf_loader.h +FILE: ../../../third_party/dart/runtime/bin/entrypoints_verification_test_extension.cc +FILE: ../../../third_party/dart/runtime/bin/entrypoints_verification_test_extension_dllmain_win.cc +FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_dynamic_library.cc +FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions.cc +FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +FILE: ../../../third_party/dart/runtime/bin/ifaddrs-android.cc +FILE: ../../../third_party/dart/runtime/bin/ifaddrs-android.h +FILE: ../../../third_party/dart/runtime/bin/namespace_fuchsia.h +FILE: ../../../third_party/dart/runtime/bin/socket_base_android.cc +FILE: ../../../third_party/dart/runtime/lib/ffi.cc +FILE: ../../../third_party/dart/runtime/lib/ffi_dynamic_library.cc +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/tree_map.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/isolate_group.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/isolate_group.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/isolate_group.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline_base.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/tree_map.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/isolate_group.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/isolate_group.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/isolate_group.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/timeline_base.dart +FILE: ../../../third_party/dart/runtime/platform/elf.h +FILE: ../../../third_party/dart/runtime/platform/thread_sanitizer.h +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_api_table.dart +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_ffi_api.dart +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_type_table.dart +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/gen_api_table.dart +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/gen_type_table.dart +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/gen_util.dart +FILE: ../../../third_party/dart/runtime/tools/ffi/sdk_lib_ffi_generator.dart +FILE: ../../../third_party/dart/runtime/tools/graphexplorer/graphexplorer.html +FILE: ../../../third_party/dart/runtime/tools/graphexplorer/graphexplorer.js +FILE: ../../../third_party/dart/runtime/tools/run_clang_tidy.dart +FILE: ../../../third_party/dart/runtime/vm/bss_relocs.cc +FILE: ../../../third_party/dart/runtime/vm/bss_relocs.h +FILE: ../../../third_party/dart/runtime/vm/catch_entry_moves_test.cc +FILE: ../../../third_party/dart/runtime/vm/class_id.h +FILE: ../../../third_party/dart/runtime/vm/code_comments.cc +FILE: ../../../third_party/dart/runtime/vm/code_comments.h +FILE: ../../../third_party/dart/runtime/vm/code_entry_kind.h +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier.h +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_x64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/bce_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/block_builder.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/evaluator.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/evaluator.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_checker.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_checker.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_test_helper.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_test_helper.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/inliner_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/typed_data_aot_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/yield_position_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier.h +FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/graph_intrinsifier_x64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/offsets_extractor.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/recognized_methods_list.h +FILE: ../../../third_party/dart/runtime/vm/compiler/relocation.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_api.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_api.h +FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_offsets_extracted.h +FILE: ../../../third_party/dart/runtime/vm/compiler/runtime_offsets_list.h +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler.h +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler_x64.cc +FILE: ../../../third_party/dart/runtime/vm/constants_arm.cc +FILE: ../../../third_party/dart/runtime/vm/constants_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/constants_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/constants_x64.cc +FILE: ../../../third_party/dart/runtime/vm/elf.cc +FILE: ../../../third_party/dart/runtime/vm/elf.h +FILE: ../../../third_party/dart/runtime/vm/ffi_callback_trampolines.cc +FILE: ../../../third_party/dart/runtime/vm/ffi_callback_trampolines.h +FILE: ../../../third_party/dart/runtime/vm/frame_layout.h +FILE: ../../../third_party/dart/runtime/vm/intrusive_dlist.h +FILE: ../../../third_party/dart/runtime/vm/intrusive_dlist_test.cc +FILE: ../../../third_party/dart/runtime/vm/libfuzzer/dart_libfuzzer.cc +FILE: ../../../third_party/dart/runtime/vm/longjump.h +FILE: ../../../third_party/dart/runtime/vm/pointer_tagging.h +FILE: ../../../third_party/dart/runtime/vm/splay-tree.h +FILE: ../../../third_party/dart/runtime/vm/static_type_exactness_state.h +FILE: ../../../third_party/dart/runtime/vm/stub_code_list.h +FILE: ../../../third_party/dart/runtime/vm/thread_stack_resource.cc +FILE: ../../../third_party/dart/runtime/vm/thread_stack_resource.h +FILE: ../../../third_party/dart/runtime/vm/thread_state.cc +FILE: ../../../third_party/dart/runtime/vm/thread_state.h +FILE: ../../../third_party/dart/samples/ffi/coordinate.dart +FILE: ../../../third_party/dart/samples/ffi/dylib_utils.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/arena_isolate_shutdown_sample.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/arena_sample.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/arena_zoned_sample.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/resource_management_test.dart +FILE: ../../../third_party/dart/samples/ffi/resource_management/unmanaged_sample.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_bitfield.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_data.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_dynamic_library.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions_callbacks.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions_structs.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_structs.dart +FILE: ../../../third_party/dart/samples/ffi/samples_test.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/example/main.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/sqlite.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/bindings.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/constants.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/signatures.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/bindings/types.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/collections/closable_iterator.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/database.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/ffi/dylib_utils.dart +FILE: ../../../third_party/dart/samples_2/ffi/coordinate.dart +FILE: ../../../third_party/dart/samples_2/ffi/dylib_utils.dart +FILE: ../../../third_party/dart/samples_2/ffi/resource_management/arena_isolate_shutdown_sample.dart +FILE: ../../../third_party/dart/samples_2/ffi/resource_management/arena_sample.dart +FILE: ../../../third_party/dart/samples_2/ffi/resource_management/arena_zoned_sample.dart +FILE: ../../../third_party/dart/samples_2/ffi/resource_management/resource_management_test.dart +FILE: ../../../third_party/dart/samples_2/ffi/resource_management/unmanaged_sample.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_bitfield.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_data.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_dynamic_library.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_functions.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_functions_callbacks.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_functions_structs.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_structs.dart +FILE: ../../../third_party/dart/samples_2/ffi/samples_test.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/example/main.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/sqlite.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/bindings/bindings.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/bindings/constants.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/bindings/signatures.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/bindings/types.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/collections/closable_iterator.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/database.dart +FILE: ../../../third_party/dart/samples_2/ffi/sqlite/lib/src/ffi/dylib_utils.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/rti.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/recipe_syntax.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/annotations.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/dynamic_library.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/ffi.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/native_type.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/struct.dart +FILE: ../../../third_party/dart/sdk/lib/internal/errors.dart +---------------------------------------------------------------------------------------------------- +Copyright (c) 2019, 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 LLC 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: dart +ORIGIN: ../../../third_party/dart/benchmarks/DartCLIStartup/dart/DartCLIStartup.dart + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/benchmarks/DartCLIStartup/dart/DartCLIStartup.dart +FILE: ../../../third_party/dart/benchmarks/DartCLIStartup/dart2/DartCLIStartup.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyRegexp/dart/EventLoopLatencyRegexp.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyRegexp/dart/regexp_benchmark.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyRegexp/dart2/regexp_benchmark.dart +FILE: ../../../third_party/dart/benchmarks/IsolateFibonacci/dart/IsolateFibonacci.dart +FILE: ../../../third_party/dart/benchmarks/IsolateFibonacci/dart2/IsolateFibonacci.dart +FILE: ../../../third_party/dart/benchmarks/MapLookup/dart/MapLookup.dart +FILE: ../../../third_party/dart/benchmarks/MapLookup/dart/maps.dart +FILE: ../../../third_party/dart/benchmarks/MapLookup/dart2/MapLookup.dart +FILE: ../../../third_party/dart/benchmarks/MapLookup/dart2/maps.dart +FILE: ../../../third_party/dart/benchmarks/MapLookup/generate_maps.dart +FILE: ../../../third_party/dart/benchmarks/ObjectHash/dart/ObjectHash.dart +FILE: ../../../third_party/dart/benchmarks/ObjectHash/dart2/ObjectHash.dart +FILE: ../../../third_party/dart/benchmarks/SDKArtifactSizes/dart/SDKArtifactSizes.dart +FILE: ../../../third_party/dart/benchmarks/SDKArtifactSizes/dart2/SDKArtifactSizes.dart +FILE: ../../../third_party/dart/benchmarks/SendPort/dart/SendPort.dart +FILE: ../../../third_party/dart/benchmarks/SendPort/dart2/SendPort.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart/StringPool.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart/StringPool100.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart/version1a.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart/version1b.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart/version2.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart2/StringPool.dart +FILE: ../../../third_party/dart/benchmarks/StringPool/dart2/StringPool100.dart +FILE: ../../../third_party/dart/runtime/bin/abstract_socket_test.cc +FILE: ../../../third_party/dart/runtime/bin/utils.cc +FILE: ../../../third_party/dart/runtime/bin/virtual_memory.h +FILE: ../../../third_party/dart/runtime/bin/virtual_memory_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/virtual_memory_posix.cc +FILE: ../../../third_party/dart/runtime/bin/virtual_memory_win.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_timings.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_timings.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/range.h +FILE: ../../../third_party/dart/runtime/vm/compiler/relocation_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/safepoint_test.cc +FILE: ../../../third_party/dart/runtime/vm/message_snapshot.cc +FILE: ../../../third_party/dart/runtime/vm/message_snapshot.h +FILE: ../../../third_party/dart/runtime/vm/object_graph_copy.cc +FILE: ../../../third_party/dart/runtime/vm/object_graph_copy.h +FILE: ../../../third_party/dart/runtime/vm/pending_deopts.cc +FILE: ../../../third_party/dart/runtime/vm/pending_deopts.h +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_android_arm.S +FILE: ../../../third_party/dart/runtime/vm/virtual_memory_compressed.cc +FILE: ../../../third_party/dart/runtime/vm/virtual_memory_compressed.h +FILE: ../../../third_party/dart/samples/ffi/resource_management/utf8_helpers.dart +FILE: ../../../third_party/dart/samples_2/ffi/resource_management/utf8_helpers.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/dart2js_runtime_metrics.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/late_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart +FILE: ../../../third_party/dart/sdk/lib/core/enum.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/allocation.dart +FILE: ../../../third_party/dart/sdk/lib/ffi/union.dart +---------------------------------------------------------------------------------------------------- +Copyright (c) 2021, 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 LLC 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: dart +ORIGIN: ../../../third_party/dart/benchmarks/Dynamic/dart/Dynamic.dart + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/benchmarks/Dynamic/dart/Dynamic.dart +FILE: ../../../third_party/dart/benchmarks/Dynamic/dart2/Dynamic.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart/EventLoopLatencyJson.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart/json_benchmark.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart/latency.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart2/EventLoopLatencyJson.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart2/json_benchmark.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson/dart2/latency.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson350KB/dart/EventLoopLatencyJson350KB.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson350KB/dart/json_benchmark.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson350KB/dart/latency.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson350KB/dart2/EventLoopLatencyJson350KB.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson350KB/dart2/json_benchmark.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyJson350KB/dart2/latency.dart +FILE: ../../../third_party/dart/benchmarks/EventLoopLatencyRegexp/dart2/EventLoopLatencyRegexp.dart +FILE: ../../../third_party/dart/benchmarks/ListCopy/dart/ListCopy.dart +FILE: ../../../third_party/dart/benchmarks/ListCopy/dart2/ListCopy.dart +FILE: ../../../third_party/dart/benchmarks/MD5/dart/md5.dart +FILE: ../../../third_party/dart/benchmarks/MD5/dart2/md5.dart +FILE: ../../../third_party/dart/benchmarks/Omnibus/dart/Omnibus.dart +FILE: ../../../third_party/dart/benchmarks/Omnibus/dart2/Omnibus.dart +FILE: ../../../third_party/dart/benchmarks/OmnibusDeferred/dart/OmnibusDeferred.dart +FILE: ../../../third_party/dart/benchmarks/OmnibusDeferred/dart2/OmnibusDeferred.dart +FILE: ../../../third_party/dart/benchmarks/RuntimeType/dart/RuntimeType.dart +FILE: ../../../third_party/dart/benchmarks/RuntimeType/dart2/RuntimeType.dart +FILE: ../../../third_party/dart/benchmarks/SHA1/dart/sha1.dart +FILE: ../../../third_party/dart/benchmarks/SHA1/dart2/sha1.dart +FILE: ../../../third_party/dart/benchmarks/SHA256/dart/sha256.dart +FILE: ../../../third_party/dart/benchmarks/SHA256/dart2/sha256.dart +FILE: ../../../third_party/dart/benchmarks/SkeletalAnimation/dart/SkeletalAnimation.dart +FILE: ../../../third_party/dart/benchmarks/SkeletalAnimation/dart2/SkeletalAnimation.dart +FILE: ../../../third_party/dart/benchmarks/SkeletalAnimationSIMD/dart/SkeletalAnimationSIMD.dart +FILE: ../../../third_party/dart/benchmarks/SkeletalAnimationSIMD/dart2/SkeletalAnimationSIMD.dart +FILE: ../../../third_party/dart/benchmarks/TypedDataDuplicate/dart/TypedDataDuplicate.dart +FILE: ../../../third_party/dart/benchmarks/TypedDataDuplicate/dart2/TypedDataDuplicate.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/Utf8Decode.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart2/Utf8Decode.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart/Utf8Encode.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Encode/dart2/Utf8Encode.dart +FILE: ../../../third_party/dart/runtime/bin/dartdev_isolate.cc +FILE: ../../../third_party/dart/runtime/bin/dartdev_isolate.h +FILE: ../../../third_party/dart/runtime/bin/exe_utils.cc +FILE: ../../../third_party/dart/runtime/bin/exe_utils.h +FILE: ../../../third_party/dart/runtime/bin/ffi_test/ffi_test_functions_generated.cc +FILE: ../../../third_party/dart/runtime/bin/ffi_unit_test/run_ffi_unit_tests.cc +FILE: ../../../third_party/dart/runtime/bin/file_win.h +FILE: ../../../third_party/dart/runtime/bin/platform_macos.h +FILE: ../../../third_party/dart/runtime/bin/platform_macos_test.cc +FILE: ../../../third_party/dart/runtime/bin/priority_heap_test.cc +FILE: ../../../third_party/dart/runtime/include/dart_api_dl.c +FILE: ../../../third_party/dart/runtime/include/dart_api_dl.h +FILE: ../../../third_party/dart/runtime/include/dart_version.h +FILE: ../../../third_party/dart/runtime/include/internal/dart_api_dl_impl.h +FILE: ../../../third_party/dart/runtime/observatory/bin/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/process_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory_2/bin/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/process_snapshot.dart +FILE: ../../../third_party/dart/runtime/platform/allocation.cc +FILE: ../../../third_party/dart/runtime/platform/leak_sanitizer.h +FILE: ../../../third_party/dart/runtime/platform/priority_queue.h +FILE: ../../../third_party/dart/runtime/platform/unaligned.h +FILE: ../../../third_party/dart/runtime/platform/undefined_behavior_sanitizer.h +FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/bin/main.dart +FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/lib/cquery_driver.dart +FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/lib/xref_extractor.dart +FILE: ../../../third_party/dart/runtime/vm/canonical_tables.cc +FILE: ../../../third_party/dart/runtime/vm/closure_functions_cache.cc +FILE: ../../../third_party/dart/runtime/vm/closure_functions_cache.h +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/dispatch_table_generator.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/dispatch_table_generator.h +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler_tracer.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler_tracer.h +FILE: ../../../third_party/dart/runtime/vm/compiler/api/deopt_id.h +FILE: ../../../third_party/dart/runtime/vm/compiler/api/print_filter.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/api/print_filter.h +FILE: ../../../third_party/dart/runtime/vm/compiler/api/type_check_mode.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_base.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/constant_propagator_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/reachability_fence_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/abi.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/abi.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/call.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/call.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/callback.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/callback.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/frame_rebase.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/frame_rebase.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/marshaller.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/marshaller.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_calling_convention.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_calling_convention.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_calling_convention_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_location.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_location.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_location_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type_vm_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/recognized_method.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/recognized_method.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_test.h +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_test_custom_zone.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/unit_test_custom_zone.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_binary_flowgraph_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination.h +FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination_test.cc +FILE: ../../../third_party/dart/runtime/vm/constants_base.h +FILE: ../../../third_party/dart/runtime/vm/datastream_test.cc +FILE: ../../../third_party/dart/runtime/vm/dispatch_table.cc +FILE: ../../../third_party/dart/runtime/vm/dispatch_table.h +FILE: ../../../third_party/dart/runtime/vm/experimental_features.cc +FILE: ../../../third_party/dart/runtime/vm/experimental_features.h +FILE: ../../../third_party/dart/runtime/vm/field_table.cc +FILE: ../../../third_party/dart/runtime/vm/field_table.h +FILE: ../../../third_party/dart/runtime/vm/heap/become_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/weak_table_test.cc +FILE: ../../../third_party/dart/runtime/vm/port_set.h +FILE: ../../../third_party/dart/runtime/vm/stub_code_test.cc +FILE: ../../../third_party/dart/runtime/vm/tagged_pointer.h +FILE: ../../../third_party/dart/runtime/vm/timeline_macos.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test.cc +FILE: ../../../third_party/dart/runtime/vm/visitor.cc +FILE: ../../../third_party/dart/samples/ffi/async/async_test.dart +FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart +FILE: ../../../third_party/dart/samples/ffi/async/sample_native_port_call.dart +FILE: ../../../third_party/dart/samples/ffi/sample_ffi_functions_callbacks_closures.dart +FILE: ../../../third_party/dart/samples_2/ffi/async/async_test.dart +FILE: ../../../third_party/dart/samples_2/ffi/async/sample_async_callback.dart +FILE: ../../../third_party/dart/samples_2/ffi/async/sample_native_port_call.dart +FILE: ../../../third_party/dart/samples_2/ffi/sample_ffi_functions_callbacks_closures.dart +FILE: ../../../third_party/dart/sdk/lib/_http/embedder_config.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart +FILE: ../../../third_party/dart/sdk/lib/internal/lowering.dart +FILE: ../../../third_party/dart/sdk/lib/io/network_policy.dart +---------------------------------------------------------------------------------------------------- +Copyright (c) 2020, 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 LLC 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: dart +ORIGIN: ../../../third_party/dart/client/dart.js + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/DEPS +FILE: ../../../third_party/dart/client/dart.js +FILE: ../../../third_party/dart/runtime/bin/builtin.cc +FILE: ../../../third_party/dart/runtime/bin/builtin.h +FILE: ../../../third_party/dart/runtime/bin/builtin_gen_snapshot.cc +FILE: ../../../third_party/dart/runtime/bin/builtin_in.cc +FILE: ../../../third_party/dart/runtime/bin/builtin_natives.cc +FILE: ../../../third_party/dart/runtime/bin/crashpad.cc +FILE: ../../../third_party/dart/runtime/bin/crypto.cc +FILE: ../../../third_party/dart/runtime/bin/crypto.h +FILE: ../../../third_party/dart/runtime/bin/crypto_android.cc +FILE: ../../../third_party/dart/runtime/bin/crypto_linux.cc +FILE: ../../../third_party/dart/runtime/bin/crypto_macos.cc +FILE: ../../../third_party/dart/runtime/bin/crypto_win.cc +FILE: ../../../third_party/dart/runtime/bin/dartutils.cc +FILE: ../../../third_party/dart/runtime/bin/dartutils.h +FILE: ../../../third_party/dart/runtime/bin/directory.cc +FILE: ../../../third_party/dart/runtime/bin/directory.h +FILE: ../../../third_party/dart/runtime/bin/directory_android.cc +FILE: ../../../third_party/dart/runtime/bin/directory_linux.cc +FILE: ../../../third_party/dart/runtime/bin/directory_macos.cc +FILE: ../../../third_party/dart/runtime/bin/directory_win.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler.h +FILE: ../../../third_party/dart/runtime/bin/eventhandler_android.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler_android.h +FILE: ../../../third_party/dart/runtime/bin/eventhandler_linux.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler_linux.h +FILE: ../../../third_party/dart/runtime/bin/eventhandler_macos.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler_macos.h +FILE: ../../../third_party/dart/runtime/bin/eventhandler_test.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler_win.h +FILE: ../../../third_party/dart/runtime/bin/extensions.cc +FILE: ../../../third_party/dart/runtime/bin/extensions.h +FILE: ../../../third_party/dart/runtime/bin/extensions_android.cc +FILE: ../../../third_party/dart/runtime/bin/extensions_linux.cc +FILE: ../../../third_party/dart/runtime/bin/extensions_macos.cc +FILE: ../../../third_party/dart/runtime/bin/extensions_win.cc +FILE: ../../../third_party/dart/runtime/bin/fdutils.h +FILE: ../../../third_party/dart/runtime/bin/fdutils_android.cc +FILE: ../../../third_party/dart/runtime/bin/fdutils_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/fdutils_linux.cc +FILE: ../../../third_party/dart/runtime/bin/fdutils_macos.cc +FILE: ../../../third_party/dart/runtime/bin/file_android.cc +FILE: ../../../third_party/dart/runtime/bin/file_linux.cc +FILE: ../../../third_party/dart/runtime/bin/file_macos.cc +FILE: ../../../third_party/dart/runtime/bin/file_test.cc +FILE: ../../../third_party/dart/runtime/bin/file_win.cc +FILE: ../../../third_party/dart/runtime/bin/hashmap_test.cc +FILE: ../../../third_party/dart/runtime/bin/io_buffer.cc +FILE: ../../../third_party/dart/runtime/bin/io_buffer.h +FILE: ../../../third_party/dart/runtime/bin/io_natives.h +FILE: ../../../third_party/dart/runtime/bin/isolate_data.h +FILE: ../../../third_party/dart/runtime/bin/lockers.h +FILE: ../../../third_party/dart/runtime/bin/main.cc +FILE: ../../../third_party/dart/runtime/bin/platform.cc +FILE: ../../../third_party/dart/runtime/bin/platform.h +FILE: ../../../third_party/dart/runtime/bin/platform_android.cc +FILE: ../../../third_party/dart/runtime/bin/platform_linux.cc +FILE: ../../../third_party/dart/runtime/bin/platform_macos.cc +FILE: ../../../third_party/dart/runtime/bin/platform_win.cc +FILE: ../../../third_party/dart/runtime/bin/process.h +FILE: ../../../third_party/dart/runtime/bin/process_android.cc +FILE: ../../../third_party/dart/runtime/bin/process_linux.cc +FILE: ../../../third_party/dart/runtime/bin/process_macos.cc +FILE: ../../../third_party/dart/runtime/bin/process_win.cc +FILE: ../../../third_party/dart/runtime/bin/run_vm_tests.cc +FILE: ../../../third_party/dart/runtime/bin/secure_socket_unsupported.cc +FILE: ../../../third_party/dart/runtime/bin/socket_android.cc +FILE: ../../../third_party/dart/runtime/bin/socket_base_android.h +FILE: ../../../third_party/dart/runtime/bin/socket_base_linux.h +FILE: ../../../third_party/dart/runtime/bin/socket_base_macos.h +FILE: ../../../third_party/dart/runtime/bin/socket_base_win.h +FILE: ../../../third_party/dart/runtime/bin/test_extension.c +FILE: ../../../third_party/dart/runtime/bin/test_extension_dllmain_win.cc +FILE: ../../../third_party/dart/runtime/bin/thread.h +FILE: ../../../third_party/dart/runtime/bin/thread_android.cc +FILE: ../../../third_party/dart/runtime/bin/thread_android.h +FILE: ../../../third_party/dart/runtime/bin/thread_linux.cc +FILE: ../../../third_party/dart/runtime/bin/thread_linux.h +FILE: ../../../third_party/dart/runtime/bin/thread_macos.cc +FILE: ../../../third_party/dart/runtime/bin/thread_macos.h +FILE: ../../../third_party/dart/runtime/bin/thread_win.cc +FILE: ../../../third_party/dart/runtime/bin/thread_win.h +FILE: ../../../third_party/dart/runtime/bin/utils.h +FILE: ../../../third_party/dart/runtime/bin/utils_android.cc +FILE: ../../../third_party/dart/runtime/bin/utils_linux.cc +FILE: ../../../third_party/dart/runtime/bin/utils_macos.cc +FILE: ../../../third_party/dart/runtime/bin/utils_win.cc +FILE: ../../../third_party/dart/runtime/bin/utils_win.h +FILE: ../../../third_party/dart/runtime/include/dart_api.h +FILE: ../../../third_party/dart/runtime/lib/array.cc +FILE: ../../../third_party/dart/runtime/lib/bool.cc +FILE: ../../../third_party/dart/runtime/lib/date.cc +FILE: ../../../third_party/dart/runtime/lib/errors.cc +FILE: ../../../third_party/dart/runtime/lib/function.cc +FILE: ../../../third_party/dart/runtime/lib/growable_array.cc +FILE: ../../../third_party/dart/runtime/lib/identical.cc +FILE: ../../../third_party/dart/runtime/lib/integers.cc +FILE: ../../../third_party/dart/runtime/lib/isolate.cc +FILE: ../../../third_party/dart/runtime/lib/mirrors.cc +FILE: ../../../third_party/dart/runtime/lib/regexp.cc +FILE: ../../../third_party/dart/runtime/lib/stopwatch.cc +FILE: ../../../third_party/dart/runtime/lib/weak_property.cc +FILE: ../../../third_party/dart/runtime/platform/assert.cc +FILE: ../../../third_party/dart/runtime/platform/assert.h +FILE: ../../../third_party/dart/runtime/platform/floating_point.h +FILE: ../../../third_party/dart/runtime/platform/floating_point_win.cc +FILE: ../../../third_party/dart/runtime/platform/floating_point_win.h +FILE: ../../../third_party/dart/runtime/platform/globals.h +FILE: ../../../third_party/dart/runtime/platform/hashmap.cc +FILE: ../../../third_party/dart/runtime/platform/hashmap.h +FILE: ../../../third_party/dart/runtime/platform/syslog.h +FILE: ../../../third_party/dart/runtime/platform/syslog_android.cc +FILE: ../../../third_party/dart/runtime/platform/syslog_linux.cc +FILE: ../../../third_party/dart/runtime/platform/syslog_macos.cc +FILE: ../../../third_party/dart/runtime/platform/syslog_win.cc +FILE: ../../../third_party/dart/runtime/platform/text_buffer.cc +FILE: ../../../third_party/dart/runtime/platform/text_buffer.h +FILE: ../../../third_party/dart/runtime/platform/unicode.cc +FILE: ../../../third_party/dart/runtime/platform/unicode.h +FILE: ../../../third_party/dart/runtime/platform/utils.cc +FILE: ../../../third_party/dart/runtime/platform/utils.h +FILE: ../../../third_party/dart/runtime/platform/utils_android.cc +FILE: ../../../third_party/dart/runtime/platform/utils_android.h +FILE: ../../../third_party/dart/runtime/platform/utils_linux.cc +FILE: ../../../third_party/dart/runtime/platform/utils_linux.h +FILE: ../../../third_party/dart/runtime/platform/utils_macos.cc +FILE: ../../../third_party/dart/runtime/platform/utils_macos.h +FILE: ../../../third_party/dart/runtime/platform/utils_win.cc +FILE: ../../../third_party/dart/runtime/platform/utils_win.h +FILE: ../../../third_party/dart/runtime/vm/allocation.cc +FILE: ../../../third_party/dart/runtime/vm/allocation_test.cc +FILE: ../../../third_party/dart/runtime/vm/assert_test.cc +FILE: ../../../third_party/dart/runtime/vm/base_isolate.h +FILE: ../../../third_party/dart/runtime/vm/benchmark_test.cc +FILE: ../../../third_party/dart/runtime/vm/benchmark_test.h +FILE: ../../../third_party/dart/runtime/vm/bit_set.h +FILE: ../../../third_party/dart/runtime/vm/bit_vector.cc +FILE: ../../../third_party/dart/runtime/vm/bit_vector.h +FILE: ../../../third_party/dart/runtime/vm/bit_vector_test.cc +FILE: ../../../third_party/dart/runtime/vm/bitfield_test.cc +FILE: ../../../third_party/dart/runtime/vm/bitmap.cc +FILE: ../../../third_party/dart/runtime/vm/bitmap.h +FILE: ../../../third_party/dart/runtime/vm/bitmap_test.cc +FILE: ../../../third_party/dart/runtime/vm/boolfield.h +FILE: ../../../third_party/dart/runtime/vm/boolfield_test.cc +FILE: ../../../third_party/dart/runtime/vm/bootstrap.cc +FILE: ../../../third_party/dart/runtime/vm/bootstrap.h +FILE: ../../../third_party/dart/runtime/vm/bootstrap_natives.cc +FILE: ../../../third_party/dart/runtime/vm/bootstrap_natives.h +FILE: ../../../third_party/dart/runtime/vm/class_finalizer.h +FILE: ../../../third_party/dart/runtime/vm/class_finalizer_test.cc +FILE: ../../../third_party/dart/runtime/vm/class_table.cc +FILE: ../../../third_party/dart/runtime/vm/class_table.h +FILE: ../../../third_party/dart/runtime/vm/code_descriptors.cc +FILE: ../../../third_party/dart/runtime/vm/code_descriptors.h +FILE: ../../../third_party/dart/runtime/vm/code_descriptors_test.cc +FILE: ../../../third_party/dart/runtime/vm/code_observers.cc +FILE: ../../../third_party/dart/runtime/vm/code_observers.h +FILE: ../../../third_party/dart/runtime/vm/code_patcher.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher.h +FILE: ../../../third_party/dart/runtime/vm/code_patcher_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher_x64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/aot_call_specializer.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_base.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_x86.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/object_pool_builder.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/constant_propagator.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_printer.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_printer.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/inliner.h +FILE: ../../../third_party/dart/runtime/vm/compiler/cha.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/cha.h +FILE: ../../../third_party/dart/runtime/vm/compiler/cha_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/flow_graph_builder.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/flow_graph_builder.h +FILE: ../../../third_party/dart/runtime/vm/compiler/intrinsifier.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/intrinsifier.h +FILE: ../../../third_party/dart/runtime/vm/compiler/jit/compiler.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/jit/compiler.h +FILE: ../../../third_party/dart/runtime/vm/compiler_test.cc +FILE: ../../../third_party/dart/runtime/vm/cpu.h +FILE: ../../../third_party/dart/runtime/vm/cpu_arm.cc +FILE: ../../../third_party/dart/runtime/vm/cpu_test.cc +FILE: ../../../third_party/dart/runtime/vm/cpu_x64.cc +FILE: ../../../third_party/dart/runtime/vm/cpuinfo_linux.cc +FILE: ../../../third_party/dart/runtime/vm/cpuinfo_macos.cc +FILE: ../../../third_party/dart/runtime/vm/dart_api_impl_test.cc +FILE: ../../../third_party/dart/runtime/vm/dart_api_message.h +FILE: ../../../third_party/dart/runtime/vm/dart_api_state.h +FILE: ../../../third_party/dart/runtime/vm/datastream.h +FILE: ../../../third_party/dart/runtime/vm/debugger.cc +FILE: ../../../third_party/dart/runtime/vm/debugger.h +FILE: ../../../third_party/dart/runtime/vm/debugger_api_impl_test.cc +FILE: ../../../third_party/dart/runtime/vm/double_internals.h +FILE: ../../../third_party/dart/runtime/vm/exceptions_test.cc +FILE: ../../../third_party/dart/runtime/vm/flag_list.h +FILE: ../../../third_party/dart/runtime/vm/flags.cc +FILE: ../../../third_party/dart/runtime/vm/flags.h +FILE: ../../../third_party/dart/runtime/vm/flags_test.cc +FILE: ../../../third_party/dart/runtime/vm/globals.h +FILE: ../../../third_party/dart/runtime/vm/growable_array.h +FILE: ../../../third_party/dart/runtime/vm/growable_array_test.cc +FILE: ../../../third_party/dart/runtime/vm/handles.cc +FILE: ../../../third_party/dart/runtime/vm/handles_impl.h +FILE: ../../../third_party/dart/runtime/vm/handles_test.cc +FILE: ../../../third_party/dart/runtime/vm/hash_map.h +FILE: ../../../third_party/dart/runtime/vm/hash_map_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/freelist.h +FILE: ../../../third_party/dart/runtime/vm/heap/freelist_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/heap.cc +FILE: ../../../third_party/dart/runtime/vm/heap/heap.h +FILE: ../../../third_party/dart/runtime/vm/heap/heap_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/pages.cc +FILE: ../../../third_party/dart/runtime/vm/heap/pages_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/pointer_block.cc +FILE: ../../../third_party/dart/runtime/vm/heap/pointer_block.h +FILE: ../../../third_party/dart/runtime/vm/heap/scavenger.h +FILE: ../../../third_party/dart/runtime/vm/heap/verifier.cc +FILE: ../../../third_party/dart/runtime/vm/instructions_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/instructions_ia32.h +FILE: ../../../third_party/dart/runtime/vm/instructions_ia32_test.cc +FILE: ../../../third_party/dart/runtime/vm/instructions_x64.cc +FILE: ../../../third_party/dart/runtime/vm/instructions_x64.h +FILE: ../../../third_party/dart/runtime/vm/instructions_x64_test.cc +FILE: ../../../third_party/dart/runtime/vm/isolate_test.cc +FILE: ../../../third_party/dart/runtime/vm/json_test.cc +FILE: ../../../third_party/dart/runtime/vm/lockers.h +FILE: ../../../third_party/dart/runtime/vm/megamorphic_cache_table.cc +FILE: ../../../third_party/dart/runtime/vm/megamorphic_cache_table.h +FILE: ../../../third_party/dart/runtime/vm/memory_region.h +FILE: ../../../third_party/dart/runtime/vm/memory_region_test.cc +FILE: ../../../third_party/dart/runtime/vm/message_handler_test.cc +FILE: ../../../third_party/dart/runtime/vm/message_test.cc +FILE: ../../../third_party/dart/runtime/vm/native_arguments.h +FILE: ../../../third_party/dart/runtime/vm/native_message_handler.cc +FILE: ../../../third_party/dart/runtime/vm/native_message_handler.h +FILE: ../../../third_party/dart/runtime/vm/object.cc +FILE: ../../../third_party/dart/runtime/vm/object.h +FILE: ../../../third_party/dart/runtime/vm/object_arm_test.cc +FILE: ../../../third_party/dart/runtime/vm/object_ia32_test.cc +FILE: ../../../third_party/dart/runtime/vm/object_set.h +FILE: ../../../third_party/dart/runtime/vm/object_store.h +FILE: ../../../third_party/dart/runtime/vm/object_test.cc +FILE: ../../../third_party/dart/runtime/vm/object_x64_test.cc +FILE: ../../../third_party/dart/runtime/vm/os_android.cc +FILE: ../../../third_party/dart/runtime/vm/os_linux.cc +FILE: ../../../third_party/dart/runtime/vm/os_macos.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread.h +FILE: ../../../third_party/dart/runtime/vm/os_thread_android.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread_android.h +FILE: ../../../third_party/dart/runtime/vm/os_thread_linux.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread_linux.h +FILE: ../../../third_party/dart/runtime/vm/os_thread_macos.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread_macos.h +FILE: ../../../third_party/dart/runtime/vm/os_thread_win.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread_win.h +FILE: ../../../third_party/dart/runtime/vm/os_win.cc +FILE: ../../../third_party/dart/runtime/vm/parser.cc +FILE: ../../../third_party/dart/runtime/vm/parser.h +FILE: ../../../third_party/dart/runtime/vm/port.cc +FILE: ../../../third_party/dart/runtime/vm/port_test.cc +FILE: ../../../third_party/dart/runtime/vm/proccpuinfo.cc +FILE: ../../../third_party/dart/runtime/vm/proccpuinfo.h +FILE: ../../../third_party/dart/runtime/vm/raw_object.cc +FILE: ../../../third_party/dart/runtime/vm/raw_object.h +FILE: ../../../third_party/dart/runtime/vm/scopes.cc +FILE: ../../../third_party/dart/runtime/vm/scopes.h +FILE: ../../../third_party/dart/runtime/vm/scopes_test.cc +FILE: ../../../third_party/dart/runtime/vm/snapshot.cc +FILE: ../../../third_party/dart/runtime/vm/snapshot.h +FILE: ../../../third_party/dart/runtime/vm/snapshot_test.cc +FILE: ../../../third_party/dart/runtime/vm/stack_frame.cc +FILE: ../../../third_party/dart/runtime/vm/stack_frame_test.cc +FILE: ../../../third_party/dart/runtime/vm/stub_code.cc +FILE: ../../../third_party/dart/runtime/vm/symbols.cc +FILE: ../../../third_party/dart/runtime/vm/symbols.h +FILE: ../../../third_party/dart/runtime/vm/thread_pool.cc +FILE: ../../../third_party/dart/runtime/vm/thread_pool.h +FILE: ../../../third_party/dart/runtime/vm/thread_pool_test.cc +FILE: ../../../third_party/dart/runtime/vm/thread_test.cc +FILE: ../../../third_party/dart/runtime/vm/token.h +FILE: ../../../third_party/dart/runtime/vm/unicode.cc +FILE: ../../../third_party/dart/runtime/vm/unit_test.cc +FILE: ../../../third_party/dart/runtime/vm/utils_test.cc +FILE: ../../../third_party/dart/runtime/vm/version.h +FILE: ../../../third_party/dart/runtime/vm/version_in.cc +FILE: ../../../third_party/dart/runtime/vm/virtual_memory.cc +FILE: ../../../third_party/dart/runtime/vm/virtual_memory.h +FILE: ../../../third_party/dart/runtime/vm/virtual_memory_posix.cc +FILE: ../../../third_party/dart/runtime/vm/virtual_memory_test.cc +FILE: ../../../third_party/dart/runtime/vm/virtual_memory_win.cc +FILE: ../../../third_party/dart/runtime/vm/zone.cc +FILE: ../../../third_party/dart/runtime/vm/zone.h +FILE: ../../../third_party/dart/runtime/vm/zone_test.cc +FILE: ../../../third_party/dart/samples/sample_extension/sample_asynchronous_extension.dart +FILE: ../../../third_party/dart/samples/sample_extension/sample_extension.cc +FILE: ../../../third_party/dart/samples/sample_extension/sample_extension_dllmain_win.cc +FILE: ../../../third_party/dart/samples/sample_extension/sample_synchronous_extension.dart +FILE: ../../../third_party/dart/samples/sample_extension/test_sample_asynchronous_extension.dart +FILE: ../../../third_party/dart/samples/sample_extension/test_sample_synchronous_extension.dart +FILE: ../../../third_party/dart/samples/samples.status +FILE: ../../../third_party/dart/samples_2/sample_extension/sample_asynchronous_extension.dart +FILE: ../../../third_party/dart/samples_2/sample_extension/sample_extension.cc +FILE: ../../../third_party/dart/samples_2/sample_extension/sample_extension_dllmain_win.cc +FILE: ../../../third_party/dart/samples_2/sample_extension/sample_synchronous_extension.dart +FILE: ../../../third_party/dart/samples_2/sample_extension/test_sample_asynchronous_extension.dart +FILE: ../../../third_party/dart/samples_2/sample_extension/test_sample_synchronous_extension.dart +FILE: ../../../third_party/dart/samples_2/samples_2.status +FILE: ../../../third_party/dart/sdk/lib/_http/crypto.dart +FILE: ../../../third_party/dart/sdk/lib/_http/http_date.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/math_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/interceptors.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_array.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_number.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_string.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/native_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/regexp_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/async_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/constant_map.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/core_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/foreign_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/interceptors.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_array.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_number.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_string.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/math_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/native_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/string_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/builtin.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/common_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/directory_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/eventhandler_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/file_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/platform_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/secure_socket_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/array.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/array_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bool_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/date_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/double.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/double_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/empty_source.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/errors_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/expando_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/function_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/growable_array.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/identical_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/immutable_map.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/integers.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/integers_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/invocation_mirror_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/isolate_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/map_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/math_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/mirrors_impl.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/mirrors_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/object_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/print_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/regexp_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/stopwatch_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/string_buffer_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/string_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/timer_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/type_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/weak_property.dart +FILE: ../../../third_party/dart/sdk/lib/async/async.dart +FILE: ../../../third_party/dart/sdk/lib/async/async_error.dart +FILE: ../../../third_party/dart/sdk/lib/async/broadcast_stream_controller.dart +FILE: ../../../third_party/dart/sdk/lib/async/future.dart +FILE: ../../../third_party/dart/sdk/lib/async/future_impl.dart +FILE: ../../../third_party/dart/sdk/lib/async/stream_controller.dart +FILE: ../../../third_party/dart/sdk/lib/async/stream_impl.dart +FILE: ../../../third_party/dart/sdk/lib/async/stream_pipe.dart +FILE: ../../../third_party/dart/sdk/lib/async/timer.dart +FILE: ../../../third_party/dart/sdk/lib/collection/collection.dart +FILE: ../../../third_party/dart/sdk/lib/collection/iterable.dart +FILE: ../../../third_party/dart/sdk/lib/collection/iterator.dart +FILE: ../../../third_party/dart/sdk/lib/collection/maps.dart +FILE: ../../../third_party/dart/sdk/lib/collection/splay_tree.dart +FILE: ../../../third_party/dart/sdk/lib/core/bool.dart +FILE: ../../../third_party/dart/sdk/lib/core/core.dart +FILE: ../../../third_party/dart/sdk/lib/core/double.dart +FILE: ../../../third_party/dart/sdk/lib/core/errors.dart +FILE: ../../../third_party/dart/sdk/lib/core/exceptions.dart +FILE: ../../../third_party/dart/sdk/lib/core/expando.dart +FILE: ../../../third_party/dart/sdk/lib/core/identical.dart +FILE: ../../../third_party/dart/sdk/lib/core/int.dart +FILE: ../../../third_party/dart/sdk/lib/core/invocation.dart +FILE: ../../../third_party/dart/sdk/lib/core/iterator.dart +FILE: ../../../third_party/dart/sdk/lib/core/list.dart +FILE: ../../../third_party/dart/sdk/lib/core/num.dart +FILE: ../../../third_party/dart/sdk/lib/core/object.dart +FILE: ../../../third_party/dart/sdk/lib/core/print.dart +FILE: ../../../third_party/dart/sdk/lib/core/regexp.dart +FILE: ../../../third_party/dart/sdk/lib/core/string.dart +FILE: ../../../third_party/dart/sdk/lib/core/type.dart +FILE: ../../../third_party/dart/sdk/lib/core/uri.dart +FILE: ../../../third_party/dart/sdk/lib/html/dart2js/html_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/html/dartium/nativewrappers.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/conversions.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/css_class_set.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/html_common_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/metadata.dart +FILE: ../../../third_party/dart/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/internal/async_cast.dart +FILE: ../../../third_party/dart/sdk/lib/internal/cast.dart +FILE: ../../../third_party/dart/sdk/lib/internal/internal.dart +FILE: ../../../third_party/dart/sdk/lib/io/common.dart +FILE: ../../../third_party/dart/sdk/lib/io/directory.dart +FILE: ../../../third_party/dart/sdk/lib/io/directory_impl.dart +FILE: ../../../third_party/dart/sdk/lib/io/eventhandler.dart +FILE: ../../../third_party/dart/sdk/lib/io/io.dart +FILE: ../../../third_party/dart/sdk/lib/io/platform.dart +FILE: ../../../third_party/dart/sdk/lib/io/platform_impl.dart +FILE: ../../../third_party/dart/sdk/lib/io/secure_server_socket.dart +FILE: ../../../third_party/dart/sdk/lib/isolate/isolate.dart +FILE: ../../../third_party/dart/sdk/lib/math/math.dart +FILE: ../../../third_party/dart/sdk/lib/math/random.dart +FILE: ../../../third_party/dart/sdk/lib/svg/dart2js/svg_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/web_audio/dart2js/web_audio_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart +FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/address_sanitizer.cc + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/address_sanitizer.cc +FILE: ../../../third_party/dart/runtime/bin/dart_io_api_impl.cc +FILE: ../../../third_party/dart/runtime/bin/observatory_assets_empty.cc +FILE: ../../../third_party/dart/runtime/include/bin/dart_io_api.h +FILE: ../../../third_party/dart/runtime/lib/developer.cc +FILE: ../../../third_party/dart/runtime/lib/timeline.cc +FILE: ../../../third_party/dart/runtime/lib/vmservice.cc +FILE: ../../../third_party/dart/runtime/observatory/lib/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/cli.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/debugger.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/cli/command.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/debugger/debugger.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/debugger/debugger_location.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/logging.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/logging_list.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/megamorphiccache_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/objectpool_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/ports.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/timeline_page.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/view_footer.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/sample_profile/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/web/timeline.js +FILE: ../../../third_party/dart/runtime/observatory_2/lib/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/cli.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/debugger.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/allocation_profile/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/cli/command.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/debugger/debugger.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/debugger/debugger_location.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/logging.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/logging_list.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/megamorphiccache_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectpool_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/ports.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/timeline_page.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/view_footer.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/sample_profile/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/web/timeline.js +FILE: ../../../third_party/dart/runtime/vm/atomic_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/precompiler.h +FILE: ../../../third_party/dart/runtime/vm/log.cc +FILE: ../../../third_party/dart/runtime/vm/log.h +FILE: ../../../third_party/dart/runtime/vm/log_test.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread.cc +FILE: ../../../third_party/dart/runtime/vm/profiler_service.cc +FILE: ../../../third_party/dart/runtime/vm/profiler_service.h +FILE: ../../../third_party/dart/runtime/vm/program_visitor.cc +FILE: ../../../third_party/dart/runtime/vm/program_visitor.h +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_bytecode.cc +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_bytecode.h +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_bytecode_inl.h +FILE: ../../../third_party/dart/runtime/vm/regexp_bytecodes.h +FILE: ../../../third_party/dart/runtime/vm/regexp_interpreter.cc +FILE: ../../../third_party/dart/runtime/vm/regexp_interpreter.h +FILE: ../../../third_party/dart/runtime/vm/scope_timer.h +FILE: ../../../third_party/dart/runtime/vm/service_event.cc +FILE: ../../../third_party/dart/runtime/vm/service_event.h +FILE: ../../../third_party/dart/runtime/vm/service_isolate.cc +FILE: ../../../third_party/dart/runtime/vm/source_report.cc +FILE: ../../../third_party/dart/runtime/vm/source_report.h +FILE: ../../../third_party/dart/runtime/vm/source_report_test.cc +FILE: ../../../third_party/dart/runtime/vm/thread.cc +FILE: ../../../third_party/dart/runtime/vm/thread.h +FILE: ../../../third_party/dart/runtime/vm/thread_barrier.h +FILE: ../../../third_party/dart/runtime/vm/thread_barrier_test.cc +FILE: ../../../third_party/dart/runtime/vm/thread_registry.cc +FILE: ../../../third_party/dart/runtime/vm/thread_registry.h +FILE: ../../../third_party/dart/runtime/vm/timeline.cc +FILE: ../../../third_party/dart/runtime/vm/timeline.h +FILE: ../../../third_party/dart/runtime/vm/timeline_test.cc +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/developer_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/rtti.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/utils.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/debugger.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/developer_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/preambles/d8.js +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/async_await_error_codes.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/async_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/compact_hash.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/developer.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/timeline.dart +FILE: ../../../third_party/dart/sdk/lib/convert/base64.dart +FILE: ../../../third_party/dart/sdk/lib/developer/developer.dart +FILE: ../../../third_party/dart/sdk/lib/developer/extension.dart +FILE: ../../../third_party/dart/sdk/lib/developer/timeline.dart +FILE: ../../../third_party/dart/sdk/lib/io/io_resource_info.dart +FILE: ../../../third_party/dart/sdk/lib/io/security_context.dart +FILE: ../../../third_party/dart/sdk/lib/js/_js_annotations.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/asset.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/vmservice.dart +---------------------------------------------------------------------------------------------------- +Copyright (c) 2015, 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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/cli.cc + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/cli.cc +FILE: ../../../third_party/dart/runtime/bin/dfe.cc +FILE: ../../../third_party/dart/runtime/bin/dfe.h +FILE: ../../../third_party/dart/runtime/bin/error_exit.cc +FILE: ../../../third_party/dart/runtime/bin/error_exit.h +FILE: ../../../third_party/dart/runtime/bin/gzip.cc +FILE: ../../../third_party/dart/runtime/bin/gzip.h +FILE: ../../../third_party/dart/runtime/bin/isolate_data.cc +FILE: ../../../third_party/dart/runtime/bin/main_options.cc +FILE: ../../../third_party/dart/runtime/bin/main_options.h +FILE: ../../../third_party/dart/runtime/bin/namespace.cc +FILE: ../../../third_party/dart/runtime/bin/namespace.h +FILE: ../../../third_party/dart/runtime/bin/namespace_android.cc +FILE: ../../../third_party/dart/runtime/bin/namespace_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/namespace_linux.cc +FILE: ../../../third_party/dart/runtime/bin/namespace_macos.cc +FILE: ../../../third_party/dart/runtime/bin/namespace_win.cc +FILE: ../../../third_party/dart/runtime/bin/options.cc +FILE: ../../../third_party/dart/runtime/bin/options.h +FILE: ../../../third_party/dart/runtime/bin/secure_socket_filter.cc +FILE: ../../../third_party/dart/runtime/bin/secure_socket_filter.h +FILE: ../../../third_party/dart/runtime/bin/secure_socket_utils.cc +FILE: ../../../third_party/dart/runtime/bin/secure_socket_utils.h +FILE: ../../../third_party/dart/runtime/bin/security_context.cc +FILE: ../../../third_party/dart/runtime/bin/security_context.h +FILE: ../../../third_party/dart/runtime/bin/security_context_android.cc +FILE: ../../../third_party/dart/runtime/bin/security_context_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/security_context_linux.cc +FILE: ../../../third_party/dart/runtime/bin/security_context_macos.cc +FILE: ../../../third_party/dart/runtime/bin/security_context_win.cc +FILE: ../../../third_party/dart/runtime/bin/snapshot_utils.cc +FILE: ../../../third_party/dart/runtime/bin/snapshot_utils.h +FILE: ../../../third_party/dart/runtime/bin/socket_base.cc +FILE: ../../../third_party/dart/runtime/bin/socket_base.h +FILE: ../../../third_party/dart/runtime/bin/sync_socket.cc +FILE: ../../../third_party/dart/runtime/bin/sync_socket.h +FILE: ../../../third_party/dart/runtime/bin/sync_socket_android.cc +FILE: ../../../third_party/dart/runtime/bin/sync_socket_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/sync_socket_linux.cc +FILE: ../../../third_party/dart/runtime/bin/sync_socket_macos.cc +FILE: ../../../third_party/dart/runtime/bin/sync_socket_win.cc +FILE: ../../../third_party/dart/runtime/lib/async.cc +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/containers/search_bar.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/reload.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/singletargetcache_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/singletargetcache_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/subtypetestcache_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/subtypetestcache_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/timeline/dashboard.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unlinkedcall_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unlinkedcall_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/service.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/single_target_cache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/subtype_test_cache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/timeline.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/unlinked_call.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/single_target_cache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/subtype_test_cache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/timeline.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/unlinked_call.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/vm.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/single_target_cache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/subtype_test_cache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/timeline.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/unlinked_call.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/vm.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/search_bar.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/reload.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/singletargetcache_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/singletargetcache_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/subtypetestcache_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/subtypetestcache_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/timeline/dashboard.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unlinkedcall_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unlinkedcall_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/service.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/single_target_cache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/subtype_test_cache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/timeline.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/unlinked_call.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/single_target_cache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/subtype_test_cache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/timeline.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/unlinked_call.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/vm.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/single_target_cache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/subtype_test_cache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/timeline.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/unlinked_call.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/vm.dart +FILE: ../../../third_party/dart/runtime/platform/allocation.h +FILE: ../../../third_party/dart/runtime/platform/growable_array.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations_helpers.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations_helpers_arm.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations_helpers_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/call_specializer.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/call_specializer.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/prologue_builder.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/prologue_builder.h +FILE: ../../../third_party/dart/runtime/vm/compiler/jit/jit_call_specializer.h +FILE: ../../../third_party/dart/runtime/vm/constants_x86.h +FILE: ../../../third_party/dart/runtime/vm/debugger_api_impl_test.h +FILE: ../../../third_party/dart/runtime/vm/dwarf.cc +FILE: ../../../third_party/dart/runtime/vm/dwarf.h +FILE: ../../../third_party/dart/runtime/vm/fixed_cache.h +FILE: ../../../third_party/dart/runtime/vm/fixed_cache_test.cc +FILE: ../../../third_party/dart/runtime/vm/gdb_helpers.cc +FILE: ../../../third_party/dart/runtime/vm/heap/compactor.cc +FILE: ../../../third_party/dart/runtime/vm/heap/compactor.h +FILE: ../../../third_party/dart/runtime/vm/image_snapshot.cc +FILE: ../../../third_party/dart/runtime/vm/image_snapshot.h +FILE: ../../../third_party/dart/runtime/vm/json_writer.cc +FILE: ../../../third_party/dart/runtime/vm/json_writer.h +FILE: ../../../third_party/dart/runtime/vm/kernel_binary.h +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks.h +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_arm.cc +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_tcmalloc.cc +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_test.cc +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_unsupported.cc +FILE: ../../../third_party/dart/runtime/vm/malloc_hooks_x64.cc +FILE: ../../../third_party/dart/runtime/vm/mixin_test.cc +FILE: ../../../third_party/dart/runtime/vm/stack_trace.cc +FILE: ../../../third_party/dart/runtime/vm/stack_trace.h +FILE: ../../../third_party/dart/runtime/vm/timeline_android.cc +FILE: ../../../third_party/dart/runtime/vm/timeline_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/timeline_linux.cc +FILE: ../../../third_party/dart/runtime/vm/zone_text_buffer.cc +FILE: ../../../third_party/dart/runtime/vm/zone_text_buffer.h +FILE: ../../../third_party/dart/sdk/lib/_http/overrides.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/custom_hash_map.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/identity_hash_map.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/profile.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/cli_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/namespace_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/sync_socket_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/class_id_fasta.dart +FILE: ../../../third_party/dart/sdk/lib/cli/cli.dart +FILE: ../../../third_party/dart/sdk/lib/cli/wait_for.dart +FILE: ../../../third_party/dart/sdk/lib/core/bigint.dart +FILE: ../../../third_party/dart/sdk/lib/internal/linked_list.dart +FILE: ../../../third_party/dart/sdk/lib/internal/patch.dart +FILE: ../../../third_party/dart/sdk/lib/io/embedder_config.dart +FILE: ../../../third_party/dart/sdk/lib/io/namespace_impl.dart +FILE: ../../../third_party/dart/sdk/lib/io/overrides.dart +FILE: ../../../third_party/dart/sdk/lib/io/sync_socket.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/named_lookup.dart +FILE: ../../../third_party/dart/utils/bazel/kernel_worker.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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/console.h + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/console.h +FILE: ../../../third_party/dart/runtime/bin/console_posix.cc +FILE: ../../../third_party/dart/runtime/bin/console_win.cc +---------------------------------------------------------------------------------------------------- +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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/crashpad.h + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/crashpad.h +FILE: ../../../third_party/dart/runtime/bin/dart_embedder_api_impl.cc +FILE: ../../../third_party/dart/runtime/bin/typed_data_utils.cc +FILE: ../../../third_party/dart/runtime/bin/typed_data_utils.h +FILE: ../../../third_party/dart/runtime/include/dart_embedder_api.h +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz.dart +FILE: ../../../third_party/dart/runtime/tools/dartfuzz/dartfuzz_test.dart +FILE: ../../../third_party/dart/runtime/vm/base64.cc +FILE: ../../../third_party/dart/runtime/vm/base64.h +FILE: ../../../third_party/dart/runtime/vm/base64_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/code_statistics.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/code_statistics.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/compile_type.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_elimination_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot.h +FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_pass.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_pass.h +FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_state.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/compiler_state.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/base_flow_graph_builder.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/base_flow_graph_builder.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_reader.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_reader.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_fingerprints.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_fingerprints.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_translation_helper.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_translation_helper.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/scope_builder.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/scope_builder.h +FILE: ../../../third_party/dart/runtime/vm/compiler/relocation.h +FILE: ../../../third_party/dart/runtime/vm/constants.h +FILE: ../../../third_party/dart/runtime/vm/datastream.cc +FILE: ../../../third_party/dart/runtime/vm/finalizable_data.h +FILE: ../../../third_party/dart/runtime/vm/hash.h +FILE: ../../../third_party/dart/runtime/vm/raw_object_fields.cc +FILE: ../../../third_party/dart/runtime/vm/raw_object_fields.h +FILE: ../../../third_party/dart/runtime/vm/reverse_pc_lookup_cache.cc +FILE: ../../../third_party/dart/runtime/vm/reverse_pc_lookup_cache.h +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs.h +FILE: ../../../third_party/dart/runtime/vm/v8_snapshot_writer.cc +FILE: ../../../third_party/dart/runtime/vm/v8_snapshot_writer.h +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/instantiation.dart +FILE: ../../../third_party/dart/sdk/lib/js/_js.dart +FILE: ../../../third_party/dart/sdk/lib/js/_js_client.dart +FILE: ../../../third_party/dart/sdk/lib/js/_js_server.dart +FILE: ../../../third_party/dart/sdk/lib/typed_data/unmodifiable_typed_data.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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/crypto_fuchsia.cc + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/crypto_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/crypto_test.cc +FILE: ../../../third_party/dart/runtime/bin/directory_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/directory_test.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/eventhandler_fuchsia.h +FILE: ../../../third_party/dart/runtime/bin/extensions_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/file_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/file_support.cc +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/loader.cc +FILE: ../../../third_party/dart/runtime/bin/loader.h +FILE: ../../../third_party/dart/runtime/bin/platform_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/process_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/reference_counting.h +FILE: ../../../third_party/dart/runtime/bin/root_certificates_unsupported.cc +FILE: ../../../third_party/dart/runtime/bin/socket_base_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/socket_base_fuchsia.h +FILE: ../../../third_party/dart/runtime/bin/socket_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/stdio_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/thread_fuchsia.cc +FILE: ../../../third_party/dart/runtime/bin/thread_fuchsia.h +FILE: ../../../third_party/dart/runtime/bin/utils_fuchsia.cc +FILE: ../../../third_party/dart/runtime/lib/stacktrace.h +FILE: ../../../third_party/dart/runtime/observatory/lib/event.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/models.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/repositories.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/notification.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/containers/virtual_collection.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/containers/virtual_tree.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/error_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/general_error.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/custom_element.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/nav_bar.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/nav_menu.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/rendering_queue.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/rendering_scheduler.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/uris.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/counter_chart.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/location.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/run_state.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/shared_summary.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate/summary.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/metric/details.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/metric/graph.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/class_menu.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/isolate_menu.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/library_menu.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/menu_item.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/notify.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/notify_event.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/notify_exception.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/refresh.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/top_menu.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/nav/vm_menu.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/source_link.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/strongly_reachable_instances.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_connect_target.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/exceptions.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/breakpoint.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/class.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/code.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/context.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/error.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/event.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/extension_data.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/field.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/flag.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/frame.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/function.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/guarded.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/heap_space.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/icdata.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/instance.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/isolate.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/library.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/local_var_descriptors.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/map_association.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/megamorphiccache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/metric.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/notification.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/object.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/objectpool.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/objectstore.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/pc_descriptors.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/ports.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/script.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/sentinel.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/source_location.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/target.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/timeline_event.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/type_arguments.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/unknown.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/objects/vm.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/breakpoint.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/class.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/context.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/editor.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/eval.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/event.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/field.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/flag.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/function.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/icdata.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/instance.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/isolate.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/library.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/megamorphiccache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/metric.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/notification.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/object.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/objectpool.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/objectstore.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/ports.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/reachable_size.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/retained_size.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/script.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/strongly_reachable_instances.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/target.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/models/repositories/type_arguments.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/breakpoint.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/class.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/context.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/editor.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/eval.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/event.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/field.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/flag.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/function.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/icdata.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/instance.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/isolate.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/library.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/megamorphiccache.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/metric.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/notification.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/object.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/objectpool.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/objectstore.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/ports.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/reachable_size.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/retained_size.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/script.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/settings.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/strongly_reachable_instances.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/target.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/repositories/type_arguments.dart +FILE: ../../../third_party/dart/runtime/observatory/web/timeline_message_handler.js +FILE: ../../../third_party/dart/runtime/observatory_2/lib/event.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/models.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/repositories.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/notification.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/virtual_collection.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/containers/virtual_tree.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/error_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/general_error.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/custom_element.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/nav_bar.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/nav_menu.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/rendering_queue.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/rendering_scheduler.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/tag.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/uris.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/counter_chart.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/location.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/run_state.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/shared_summary.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate/summary.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metric/details.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metric/graph.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/class_menu.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/isolate_menu.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/library_menu.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/menu_item.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify_event.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/notify_exception.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/refresh.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/top_menu.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/nav/vm_menu.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/source_link.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/strongly_reachable_instances.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_connect_target.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/exceptions.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/breakpoint.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/class.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/code.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/context.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/error.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/event.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/extension_data.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/field.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/flag.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/frame.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/function.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/guarded.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/heap_space.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/icdata.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/instance.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/isolate.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/library.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/local_var_descriptors.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/map_association.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/megamorphiccache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/metric.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/notification.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/object.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/objectpool.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/objectstore.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/pc_descriptors.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/ports.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/script.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/sentinel.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/source_location.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/target.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/timeline_event.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/type_arguments.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/unknown.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/objects/vm.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/breakpoint.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/class.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/context.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/editor.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/eval.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/event.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/field.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/flag.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/function.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/icdata.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/instance.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/isolate.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/library.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/megamorphiccache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/metric.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/notification.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/object.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/objectpool.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/objectstore.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/ports.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/reachable_size.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/retained_size.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/script.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/strongly_reachable_instances.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/target.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/models/repositories/type_arguments.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/breakpoint.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/class.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/context.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/editor.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/eval.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/event.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/field.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/flag.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/function.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/icdata.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/inbound_references.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/instance.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/isolate.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/library.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/megamorphiccache.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/metric.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/notification.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/object.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/objectpool.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/objectstore.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/persistent_handles.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/ports.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/reachable_size.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/retained_size.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/retaining_path.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/sample_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/script.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/settings.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/strongly_reachable_instances.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/target.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/repositories/type_arguments.dart +FILE: ../../../third_party/dart/runtime/observatory_2/web/timeline_message_handler.js +FILE: ../../../third_party/dart/runtime/platform/syslog_fuchsia.cc +FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.cc +FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.h +FILE: ../../../third_party/dart/runtime/vm/canonical_tables.h +FILE: ../../../third_party/dart/runtime/vm/clustered_snapshot.cc +FILE: ../../../third_party/dart/runtime/vm/clustered_snapshot.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/branch_optimizer.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/branch_optimizer.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_elimination.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_elimination.h +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_to_il.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/kernel_to_il.h +FILE: ../../../third_party/dart/runtime/vm/cpuinfo_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/dart_api_state.cc +FILE: ../../../third_party/dart/runtime/vm/heap/become.cc +FILE: ../../../third_party/dart/runtime/vm/heap/become.h +FILE: ../../../third_party/dart/runtime/vm/heap/safepoint.cc +FILE: ../../../third_party/dart/runtime/vm/heap/safepoint.h +FILE: ../../../third_party/dart/runtime/vm/isolate_reload.cc +FILE: ../../../third_party/dart/runtime/vm/isolate_reload.h +FILE: ../../../third_party/dart/runtime/vm/isolate_reload_test.cc +FILE: ../../../third_party/dart/runtime/vm/kernel.cc +FILE: ../../../third_party/dart/runtime/vm/kernel.h +FILE: ../../../third_party/dart/runtime/vm/kernel_binary.cc +FILE: ../../../third_party/dart/runtime/vm/kernel_isolate.cc +FILE: ../../../third_party/dart/runtime/vm/kernel_isolate.h +FILE: ../../../third_party/dart/runtime/vm/kernel_loader.cc +FILE: ../../../third_party/dart/runtime/vm/kernel_loader.h +FILE: ../../../third_party/dart/runtime/vm/lockers.cc +FILE: ../../../third_party/dart/runtime/vm/native_symbol_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/object_reload.cc +FILE: ../../../third_party/dart/runtime/vm/object_service.cc +FILE: ../../../third_party/dart/runtime/vm/os_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/os_thread_fuchsia.h +FILE: ../../../third_party/dart/runtime/vm/signal_handler_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_fuchsia.cc +FILE: ../../../third_party/dart/runtime/vm/token_position.cc +FILE: ../../../third_party/dart/runtime/vm/token_position.h +FILE: ../../../third_party/dart/runtime/vm/uri.cc +FILE: ../../../third_party/dart/runtime/vm/uri.h +FILE: ../../../third_party/dart/runtime/vm/uri_test.cc +FILE: ../../../third_party/dart/runtime/vm/virtual_memory_fuchsia.cc +FILE: ../../../third_party/dart/sdk/lib/developer/service.dart +FILE: ../../../third_party/dart/sdk/lib/js_util/js_util.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/devfs.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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/eventhandler_win.cc + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/WATCHLISTS +FILE: ../../../third_party/dart/runtime/bin/eventhandler_win.cc +FILE: ../../../third_party/dart/runtime/bin/file.cc +FILE: ../../../third_party/dart/runtime/bin/file.h +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher.cc +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher.h +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_android.cc +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_linux.cc +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_macos.cc +FILE: ../../../third_party/dart/runtime/bin/file_system_watcher_win.cc +FILE: ../../../third_party/dart/runtime/bin/filter.cc +FILE: ../../../third_party/dart/runtime/bin/filter.h +FILE: ../../../third_party/dart/runtime/bin/gen_snapshot.cc +FILE: ../../../third_party/dart/runtime/bin/io_natives.cc +FILE: ../../../third_party/dart/runtime/bin/io_service.cc +FILE: ../../../third_party/dart/runtime/bin/io_service.h +FILE: ../../../third_party/dart/runtime/bin/io_service_no_ssl.cc +FILE: ../../../third_party/dart/runtime/bin/io_service_no_ssl.h +FILE: ../../../third_party/dart/runtime/bin/process.cc +FILE: ../../../third_party/dart/runtime/bin/socket.cc +FILE: ../../../third_party/dart/runtime/bin/socket.h +FILE: ../../../third_party/dart/runtime/bin/socket_base_linux.cc +FILE: ../../../third_party/dart/runtime/bin/socket_base_macos.cc +FILE: ../../../third_party/dart/runtime/bin/socket_base_win.cc +FILE: ../../../third_party/dart/runtime/bin/socket_linux.cc +FILE: ../../../third_party/dart/runtime/bin/socket_macos.cc +FILE: ../../../third_party/dart/runtime/bin/socket_win.cc +FILE: ../../../third_party/dart/runtime/bin/stdio.cc +FILE: ../../../third_party/dart/runtime/bin/stdio.h +FILE: ../../../third_party/dart/runtime/bin/stdio_android.cc +FILE: ../../../third_party/dart/runtime/bin/stdio_linux.cc +FILE: ../../../third_party/dart/runtime/bin/stdio_macos.cc +FILE: ../../../third_party/dart/runtime/bin/stdio_win.cc +FILE: ../../../third_party/dart/runtime/bin/vmservice_impl.cc +FILE: ../../../third_party/dart/runtime/bin/vmservice_impl.h +FILE: ../../../third_party/dart/runtime/include/dart_native_api.h +FILE: ../../../third_party/dart/runtime/lib/invocation_mirror.h +FILE: ../../../third_party/dart/runtime/lib/libgen_in.cc +FILE: ../../../third_party/dart/runtime/lib/simd128.cc +FILE: ../../../third_party/dart/runtime/lib/stacktrace.cc +FILE: ../../../third_party/dart/runtime/lib/typed_data.cc +FILE: ../../../third_party/dart/runtime/lib/uri.cc +FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/application.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/location_manager.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_instances.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/code_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/code_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/context_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/context_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/cpu_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/cpu_profile/virtual_tree.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/cpu_profile_table.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/curly_block.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/error_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/eval_box.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/field_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/field_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/flag_list.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/function_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/function_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/helpers/any_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/icdata_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/icdata_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/instance_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/instance_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/json_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/library_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/library_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/local_var_descriptors_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/megamorphiccache_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/native_memory_profiler.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/object_common.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/object_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/objectpool_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/objectstore_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/observatory_application.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/pc_descriptors_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/sample_buffer_control.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/script_inset.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/script_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/script_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/sentinel_value.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/sentinel_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/source_inset.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/stack_trace_tree_config.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/type_arguments_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/unknown_ref.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_view.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/tracer.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/application.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/location_manager.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_instances.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/code_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/code_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/context_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/context_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile/virtual_tree.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/cpu_profile_table.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/curly_block.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/error_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/eval_box.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/field_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/field_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/flag_list.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/function_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/function_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_snapshot.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/icdata_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/icdata_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/instance_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/instance_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/json_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/library_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/library_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/local_var_descriptors_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/megamorphiccache_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/native_memory_profiler.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/object_common.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/object_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectpool_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/objectstore_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/observatory_application.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/pc_descriptors_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sample_buffer_control.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_inset.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/script_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sentinel_value.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/sentinel_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/source_inset.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/stack_trace_tree_config.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/type_arguments_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/unknown_ref.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_view.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/tracer.dart +FILE: ../../../third_party/dart/runtime/platform/atomic.h +FILE: ../../../third_party/dart/runtime/platform/signal_blocker.h +FILE: ../../../third_party/dart/runtime/vm/allocation.h +FILE: ../../../third_party/dart/runtime/vm/class_finalizer.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/aot/aot_call_specializer.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm64_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_ia32.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_x64.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/block_scheduler.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/block_scheduler.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/constant_propagator.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_arm.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_x64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/inliner.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/linearscan.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/linearscan.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/locations.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/type_propagator.h +FILE: ../../../third_party/dart/runtime/vm/compiler/jit/jit_call_specializer.cc +FILE: ../../../third_party/dart/runtime/vm/constants_arm.h +FILE: ../../../third_party/dart/runtime/vm/constants_ia32.h +FILE: ../../../third_party/dart/runtime/vm/constants_x64.h +FILE: ../../../third_party/dart/runtime/vm/dart.cc +FILE: ../../../third_party/dart/runtime/vm/dart_api_impl.cc +FILE: ../../../third_party/dart/runtime/vm/deferred_objects.cc +FILE: ../../../third_party/dart/runtime/vm/deferred_objects.h +FILE: ../../../third_party/dart/runtime/vm/deopt_instructions.cc +FILE: ../../../third_party/dart/runtime/vm/deopt_instructions.h +FILE: ../../../third_party/dart/runtime/vm/guard_field_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/weak_table.cc +FILE: ../../../third_party/dart/runtime/vm/heap/weak_table.h +FILE: ../../../third_party/dart/runtime/vm/instructions.h +FILE: ../../../third_party/dart/runtime/vm/instructions_arm.cc +FILE: ../../../third_party/dart/runtime/vm/instructions_arm.h +FILE: ../../../third_party/dart/runtime/vm/instructions_arm_test.cc +FILE: ../../../third_party/dart/runtime/vm/isolate.cc +FILE: ../../../third_party/dart/runtime/vm/isolate.h +FILE: ../../../third_party/dart/runtime/vm/json_stream.cc +FILE: ../../../third_party/dart/runtime/vm/json_stream.h +FILE: ../../../third_party/dart/runtime/vm/native_api_impl.cc +FILE: ../../../third_party/dart/runtime/vm/native_symbol.h +FILE: ../../../third_party/dart/runtime/vm/native_symbol_android.cc +FILE: ../../../third_party/dart/runtime/vm/native_symbol_linux.cc +FILE: ../../../third_party/dart/runtime/vm/native_symbol_macos.cc +FILE: ../../../third_party/dart/runtime/vm/native_symbol_win.cc +FILE: ../../../third_party/dart/runtime/vm/object_id_ring.cc +FILE: ../../../third_party/dart/runtime/vm/object_id_ring.h +FILE: ../../../third_party/dart/runtime/vm/object_id_ring_test.cc +FILE: ../../../third_party/dart/runtime/vm/object_store.cc +FILE: ../../../third_party/dart/runtime/vm/os_test.cc +FILE: ../../../third_party/dart/runtime/vm/profiler.cc +FILE: ../../../third_party/dart/runtime/vm/profiler.h +FILE: ../../../third_party/dart/runtime/vm/profiler_test.cc +FILE: ../../../third_party/dart/runtime/vm/random.cc +FILE: ../../../third_party/dart/runtime/vm/random.h +FILE: ../../../third_party/dart/runtime/vm/reusable_handles.h +FILE: ../../../third_party/dart/runtime/vm/service.cc +FILE: ../../../third_party/dart/runtime/vm/service.h +FILE: ../../../third_party/dart/runtime/vm/service_isolate.h +FILE: ../../../third_party/dart/runtime/vm/service_test.cc +FILE: ../../../third_party/dart/runtime/vm/signal_handler.h +FILE: ../../../third_party/dart/runtime/vm/signal_handler_android.cc +FILE: ../../../third_party/dart/runtime/vm/signal_handler_linux.cc +FILE: ../../../third_party/dart/runtime/vm/signal_handler_macos.cc +FILE: ../../../third_party/dart/runtime/vm/signal_handler_win.cc +FILE: ../../../third_party/dart/runtime/vm/simulator.h +FILE: ../../../third_party/dart/runtime/vm/simulator_arm.cc +FILE: ../../../third_party/dart/runtime/vm/simulator_arm.h +FILE: ../../../third_party/dart/runtime/vm/stack_frame_arm.h +FILE: ../../../third_party/dart/runtime/vm/stack_frame_ia32.h +FILE: ../../../third_party/dart/runtime/vm/stack_frame_x64.h +FILE: ../../../third_party/dart/runtime/vm/stub_code_arm_test.cc +FILE: ../../../third_party/dart/runtime/vm/tags.h +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter.cc +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter.h +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_android.cc +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_linux.cc +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_macos.cc +FILE: ../../../third_party/dart/runtime/vm/thread_interrupter_win.cc +FILE: ../../../third_party/dart/sdk/lib/_http/http.dart +FILE: ../../../third_party/dart/sdk/lib/_http/http_headers.dart +FILE: ../../../third_party/dart/sdk/lib/_http/http_impl.dart +FILE: ../../../third_party/dart/sdk/lib/_http/http_parser.dart +FILE: ../../../third_party/dart/sdk/lib/_http/http_session.dart +FILE: ../../../third_party/dart/sdk/lib/_http/websocket.dart +FILE: ../../../third_party/dart/sdk/lib/_http/websocket_impl.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/collection_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/convert_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/internal_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/typed_data_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/annotations.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_primitives.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/js_rti.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/mirror_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/native_typed_data.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/annotations.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/collection_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/convert_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/internal_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/io_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_helper.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_names.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_primitives.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/native_typed_data.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/typed_data_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/file_system_entity_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/filter_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/io_service_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/process_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/socket_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/stdio_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/vmservice_io.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/bin/vmservice_server.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/collection_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/core_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/deferred_load_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/function.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/internal_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/mirror_reference.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/null_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/stacktrace.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/symbol_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/timer_impl.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/typed_data_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/uri_patch.dart +FILE: ../../../third_party/dart/sdk/lib/async/deferred_load.dart +FILE: ../../../third_party/dart/sdk/lib/async/schedule_microtask.dart +FILE: ../../../third_party/dart/sdk/lib/async/stream.dart +FILE: ../../../third_party/dart/sdk/lib/async/stream_transformers.dart +FILE: ../../../third_party/dart/sdk/lib/async/zone.dart +FILE: ../../../third_party/dart/sdk/lib/collection/collections.dart +FILE: ../../../third_party/dart/sdk/lib/collection/hash_map.dart +FILE: ../../../third_party/dart/sdk/lib/collection/hash_set.dart +FILE: ../../../third_party/dart/sdk/lib/collection/linked_hash_map.dart +FILE: ../../../third_party/dart/sdk/lib/collection/linked_hash_set.dart +FILE: ../../../third_party/dart/sdk/lib/collection/linked_list.dart +FILE: ../../../third_party/dart/sdk/lib/collection/list.dart +FILE: ../../../third_party/dart/sdk/lib/convert/ascii.dart +FILE: ../../../third_party/dart/sdk/lib/convert/byte_conversion.dart +FILE: ../../../third_party/dart/sdk/lib/convert/chunked_conversion.dart +FILE: ../../../third_party/dart/sdk/lib/convert/codec.dart +FILE: ../../../third_party/dart/sdk/lib/convert/convert.dart +FILE: ../../../third_party/dart/sdk/lib/convert/converter.dart +FILE: ../../../third_party/dart/sdk/lib/convert/encoding.dart +FILE: ../../../third_party/dart/sdk/lib/convert/html_escape.dart +FILE: ../../../third_party/dart/sdk/lib/convert/json.dart +FILE: ../../../third_party/dart/sdk/lib/convert/latin1.dart +FILE: ../../../third_party/dart/sdk/lib/convert/line_splitter.dart +FILE: ../../../third_party/dart/sdk/lib/convert/string_conversion.dart +FILE: ../../../third_party/dart/sdk/lib/convert/utf.dart +FILE: ../../../third_party/dart/sdk/lib/core/annotations.dart +FILE: ../../../third_party/dart/sdk/lib/core/null.dart +FILE: ../../../third_party/dart/sdk/lib/core/stacktrace.dart +FILE: ../../../third_party/dart/sdk/lib/core/string_sink.dart +FILE: ../../../third_party/dart/sdk/lib/core/symbol.dart +FILE: ../../../third_party/dart/sdk/lib/internal/bytes_builder.dart +FILE: ../../../third_party/dart/sdk/lib/internal/list.dart +FILE: ../../../third_party/dart/sdk/lib/internal/print.dart +FILE: ../../../third_party/dart/sdk/lib/internal/symbol.dart +FILE: ../../../third_party/dart/sdk/lib/io/data_transformer.dart +FILE: ../../../third_party/dart/sdk/lib/io/file.dart +FILE: ../../../third_party/dart/sdk/lib/io/file_impl.dart +FILE: ../../../third_party/dart/sdk/lib/io/file_system_entity.dart +FILE: ../../../third_party/dart/sdk/lib/io/io_service.dart +FILE: ../../../third_party/dart/sdk/lib/io/io_sink.dart +FILE: ../../../third_party/dart/sdk/lib/io/link.dart +FILE: ../../../third_party/dart/sdk/lib/io/secure_socket.dart +FILE: ../../../third_party/dart/sdk/lib/io/socket.dart +FILE: ../../../third_party/dart/sdk/lib/io/stdio.dart +FILE: ../../../third_party/dart/sdk/lib/io/string_transformer.dart +FILE: ../../../third_party/dart/sdk/lib/js/js.dart +FILE: ../../../third_party/dart/sdk/lib/math/point.dart +FILE: ../../../third_party/dart/sdk/lib/math/rectangle.dart +FILE: ../../../third_party/dart/sdk/lib/mirrors/mirrors.dart +FILE: ../../../third_party/dart/sdk/lib/typed_data/typed_data.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/client.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/constants.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/message.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/message_router.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/running_isolate.dart +FILE: ../../../third_party/dart/sdk/lib/vmservice/running_isolates.dart +FILE: ../../../third_party/dart/sdk/lib/web_gl/dart2js/web_gl_dart2js.dart +FILE: ../../../third_party/dart/utils/compiler/create_snapshot_entry.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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/bin/process_test.cc + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/process_test.cc +FILE: ../../../third_party/dart/runtime/bin/snapshot_empty.cc +FILE: ../../../third_party/dart/runtime/bin/snapshot_in.cc +FILE: ../../../third_party/dart/runtime/include/dart_tools_api.h +FILE: ../../../third_party/dart/runtime/lib/double.cc +FILE: ../../../third_party/dart/runtime/lib/math.cc +FILE: ../../../third_party/dart/runtime/lib/mirrors.h +FILE: ../../../third_party/dart/runtime/lib/object.cc +FILE: ../../../third_party/dart/runtime/lib/string.cc +FILE: ../../../third_party/dart/runtime/vm/bitfield.h +FILE: ../../../third_party/dart/runtime/vm/code_patcher_ia32_test.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher_x64_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_test.cc +FILE: ../../../third_party/dart/runtime/vm/cpu_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/custom_isolate_test.cc +FILE: ../../../third_party/dart/runtime/vm/dart.h +FILE: ../../../third_party/dart/runtime/vm/dart_api_impl.h +FILE: ../../../third_party/dart/runtime/vm/dart_entry.cc +FILE: ../../../third_party/dart/runtime/vm/dart_entry.h +FILE: ../../../third_party/dart/runtime/vm/debugger_arm.cc +FILE: ../../../third_party/dart/runtime/vm/debugger_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/debugger_x64.cc +FILE: ../../../third_party/dart/runtime/vm/double_conversion.cc +FILE: ../../../third_party/dart/runtime/vm/double_conversion.h +FILE: ../../../third_party/dart/runtime/vm/exceptions.cc +FILE: ../../../third_party/dart/runtime/vm/exceptions.h +FILE: ../../../third_party/dart/runtime/vm/handle_visitor.h +FILE: ../../../third_party/dart/runtime/vm/handles.h +FILE: ../../../third_party/dart/runtime/vm/heap/freelist.cc +FILE: ../../../third_party/dart/runtime/vm/heap/marker.cc +FILE: ../../../third_party/dart/runtime/vm/heap/marker.h +FILE: ../../../third_party/dart/runtime/vm/heap/pages.h +FILE: ../../../third_party/dart/runtime/vm/heap/scavenger.cc +FILE: ../../../third_party/dart/runtime/vm/heap/sweeper.cc +FILE: ../../../third_party/dart/runtime/vm/heap/sweeper.h +FILE: ../../../third_party/dart/runtime/vm/heap/verifier.h +FILE: ../../../third_party/dart/runtime/vm/longjump.cc +FILE: ../../../third_party/dart/runtime/vm/longjump_test.cc +FILE: ../../../third_party/dart/runtime/vm/memory_region.cc +FILE: ../../../third_party/dart/runtime/vm/message.cc +FILE: ../../../third_party/dart/runtime/vm/message.h +FILE: ../../../third_party/dart/runtime/vm/message_handler.cc +FILE: ../../../third_party/dart/runtime/vm/message_handler.h +FILE: ../../../third_party/dart/runtime/vm/native_entry.cc +FILE: ../../../third_party/dart/runtime/vm/native_entry.h +FILE: ../../../third_party/dart/runtime/vm/native_entry_test.cc +FILE: ../../../third_party/dart/runtime/vm/native_entry_test.h +FILE: ../../../third_party/dart/runtime/vm/native_function.h +FILE: ../../../third_party/dart/runtime/vm/object_store_test.cc +FILE: ../../../third_party/dart/runtime/vm/os.h +FILE: ../../../third_party/dart/runtime/vm/port.h +FILE: ../../../third_party/dart/runtime/vm/resolver.cc +FILE: ../../../third_party/dart/runtime/vm/resolver.h +FILE: ../../../third_party/dart/runtime/vm/runtime_entry.cc +FILE: ../../../third_party/dart/runtime/vm/runtime_entry.h +FILE: ../../../third_party/dart/runtime/vm/runtime_entry_arm.cc +FILE: ../../../third_party/dart/runtime/vm/runtime_entry_ia32.cc +FILE: ../../../third_party/dart/runtime/vm/runtime_entry_list.h +FILE: ../../../third_party/dart/runtime/vm/runtime_entry_x64.cc +FILE: ../../../third_party/dart/runtime/vm/stack_frame.h +FILE: ../../../third_party/dart/runtime/vm/stub_code.h +FILE: ../../../third_party/dart/runtime/vm/stub_code_ia32_test.cc +FILE: ../../../third_party/dart/runtime/vm/stub_code_x64_test.cc +FILE: ../../../third_party/dart/runtime/vm/timer.cc +FILE: ../../../third_party/dart/runtime/vm/timer.h +FILE: ../../../third_party/dart/runtime/vm/token.cc +FILE: ../../../third_party/dart/runtime/vm/unicode_data.cc +FILE: ../../../third_party/dart/runtime/vm/unicode_test.cc +FILE: ../../../third_party/dart/runtime/vm/unit_test.h +FILE: ../../../third_party/dart/runtime/vm/visitor.h +FILE: ../../../third_party/dart/sdk/lib/collection/queue.dart +FILE: ../../../third_party/dart/sdk/lib/core/comparable.dart +FILE: ../../../third_party/dart/sdk/lib/core/date_time.dart +FILE: ../../../third_party/dart/sdk/lib/core/duration.dart +FILE: ../../../third_party/dart/sdk/lib/core/function.dart +FILE: ../../../third_party/dart/sdk/lib/core/iterable.dart +FILE: ../../../third_party/dart/sdk/lib/core/map.dart +FILE: ../../../third_party/dart/sdk/lib/core/pattern.dart +FILE: ../../../third_party/dart/sdk/lib/core/set.dart +FILE: ../../../third_party/dart/sdk/lib/core/stopwatch.dart +FILE: ../../../third_party/dart/sdk/lib/core/string_buffer.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/device.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/filtered_element_list.dart +FILE: ../../../third_party/dart/sdk/lib/html/html_common/lists.dart +FILE: ../../../third_party/dart/sdk/lib/internal/iterable.dart +FILE: ../../../third_party/dart/sdk/lib/internal/sort.dart +FILE: ../../../third_party/dart/utils/peg/pegparser.dart +---------------------------------------------------------------------------------------------------- +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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/lib/class_id.cc + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/lib/class_id.cc +FILE: ../../../third_party/dart/runtime/lib/profiler.cc +FILE: ../../../third_party/dart/runtime/observatory/bin/shell.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/app.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/object_graph.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/service.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/service_common.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/service_html.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/service_io.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/page.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/settings.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/app/view_model.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/class_tree.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/debugger.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/heap_map.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/isolate_reconnect.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/metrics.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/elements/vm_connect.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/src/service/object.dart +FILE: ../../../third_party/dart/runtime/observatory/lib/utils.dart +FILE: ../../../third_party/dart/runtime/observatory/web/main.dart +FILE: ../../../third_party/dart/runtime/observatory_2/bin/shell.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/app.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/object_graph.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/service.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/service_common.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/service_html.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/service_io.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/page.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/settings.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/app/view_model.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/allocation_profile.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/class_tree.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/debugger.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/heap_map.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/isolate_reconnect.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/metrics.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/elements/vm_connect.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/src/service/object.dart +FILE: ../../../third_party/dart/runtime/observatory_2/lib/utils.dart +FILE: ../../../third_party/dart/runtime/observatory_2/web/main.dart +FILE: ../../../third_party/dart/runtime/platform/address_sanitizer.h +FILE: ../../../third_party/dart/runtime/platform/memory_sanitizer.h +FILE: ../../../third_party/dart/runtime/platform/safe_stack.h +FILE: ../../../third_party/dart/runtime/tools/verbose_gc_to_bmu.dart +FILE: ../../../third_party/dart/runtime/vm/bit_set_test.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/code_patcher_arm64_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_arm64.h +FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/disassembler_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/il_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/range_analysis.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/range_analysis.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/range_analysis_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/method_recognizer.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/method_recognizer.h +FILE: ../../../third_party/dart/runtime/vm/constants_arm64.h +FILE: ../../../third_party/dart/runtime/vm/cpu_arm.h +FILE: ../../../third_party/dart/runtime/vm/cpu_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/cpu_arm64.h +FILE: ../../../third_party/dart/runtime/vm/cpu_ia32.h +FILE: ../../../third_party/dart/runtime/vm/cpu_x64.h +FILE: ../../../third_party/dart/runtime/vm/cpuid.cc +FILE: ../../../third_party/dart/runtime/vm/cpuid.h +FILE: ../../../third_party/dart/runtime/vm/cpuinfo.h +FILE: ../../../third_party/dart/runtime/vm/cpuinfo_android.cc +FILE: ../../../third_party/dart/runtime/vm/cpuinfo_test.cc +FILE: ../../../third_party/dart/runtime/vm/cpuinfo_win.cc +FILE: ../../../third_party/dart/runtime/vm/debugger_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/hash_table.h +FILE: ../../../third_party/dart/runtime/vm/hash_table_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/scavenger_test.cc +FILE: ../../../third_party/dart/runtime/vm/heap/spaces.h +FILE: ../../../third_party/dart/runtime/vm/heap/weak_code.cc +FILE: ../../../third_party/dart/runtime/vm/heap/weak_code.h +FILE: ../../../third_party/dart/runtime/vm/instructions_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/instructions_arm64.h +FILE: ../../../third_party/dart/runtime/vm/instructions_arm64_test.cc +FILE: ../../../third_party/dart/runtime/vm/metrics.cc +FILE: ../../../third_party/dart/runtime/vm/metrics.h +FILE: ../../../third_party/dart/runtime/vm/metrics_test.cc +FILE: ../../../third_party/dart/runtime/vm/object_arm64_test.cc +FILE: ../../../third_party/dart/runtime/vm/object_graph.cc +FILE: ../../../third_party/dart/runtime/vm/object_graph.h +FILE: ../../../third_party/dart/runtime/vm/object_graph_test.cc +FILE: ../../../third_party/dart/runtime/vm/regexp.cc +FILE: ../../../third_party/dart/runtime/vm/regexp.h +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler.cc +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler.h +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_ir.cc +FILE: ../../../third_party/dart/runtime/vm/regexp_assembler_ir.h +FILE: ../../../third_party/dart/runtime/vm/regexp_ast.cc +FILE: ../../../third_party/dart/runtime/vm/regexp_ast.h +FILE: ../../../third_party/dart/runtime/vm/regexp_parser.cc +FILE: ../../../third_party/dart/runtime/vm/regexp_parser.h +FILE: ../../../third_party/dart/runtime/vm/regexp_test.cc +FILE: ../../../third_party/dart/runtime/vm/report.cc +FILE: ../../../third_party/dart/runtime/vm/report.h +FILE: ../../../third_party/dart/runtime/vm/ring_buffer.h +FILE: ../../../third_party/dart/runtime/vm/ring_buffer_test.cc +FILE: ../../../third_party/dart/runtime/vm/runtime_entry_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/simulator_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/simulator_arm64.h +FILE: ../../../third_party/dart/runtime/vm/stack_frame_arm64.h +FILE: ../../../third_party/dart/runtime/vm/stub_code_arm64_test.cc +FILE: ../../../third_party/dart/runtime/vm/tags.cc +FILE: ../../../third_party/dart/runtime/vm/unibrow-inl.h +FILE: ../../../third_party/dart/runtime/vm/unibrow.cc +FILE: ../../../third_party/dart/runtime/vm/unibrow.h +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/d8.js +FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/private/preambles/jsshell.js +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/linked_hash_map.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/class_id.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/convert_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/lib_prefix.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/profiler.dart +FILE: ../../../third_party/dart/sdk/lib/collection/set.dart +FILE: ../../../third_party/dart/sdk/lib/core/sink.dart +FILE: ../../../third_party/dart/sdk/lib/developer/profiler.dart +FILE: ../../../third_party/dart/sdk/lib/io/process.dart +FILE: ../../../third_party/dart/sdk/lib/io/service_object.dart +FILE: ../../../third_party/dart/sdk/lib/isolate/capability.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 LLC 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: dart +ORIGIN: ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/observatory/web/third_party/webcomponents.min.js +FILE: ../../../third_party/dart/runtime/observatory_2/web/third_party/webcomponents.min.js +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014 The Polymer 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: dart +ORIGIN: ../../../third_party/dart/runtime/platform/splay-tree-inl.h + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/platform/splay-tree-inl.h +FILE: ../../../third_party/dart/runtime/platform/splay-tree.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2010, 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 LLC 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: dart +ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart +TYPE: LicenseType.mit +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart +---------------------------------------------------------------------------------------------------- +Copyright (c) 2003-2005 Tom Wu +Copyright (c) 2012 Adam Singer (adam@solvr.io) +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF +THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +In addition, the following condition applies: + +All redistributions must retain an intact copy of this copyright notice +and disclaimer. +==================================================================================================== + +==================================================================================================== +LIBRARY: dart +ORIGIN: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart + ../../../third_party/boringssl/src/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/bigint_patch.dart +---------------------------------------------------------------------------------------------------- +Copyright 2009 The Go Authors. All rights reserved. +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file +==================================================================================================== + +==================================================================================================== +LIBRARY: dart +ORIGIN: ../../../third_party/dart/sdk/lib/io/network_profiling.dart + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/sdk/lib/io/network_profiling.dart +---------------------------------------------------------------------------------------------------- +Copyright (c) 2019, 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 LLC 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: double-conversion +LIBRARY: icu +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum-dtoa.cc +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum-dtoa.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum-dtoa.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/bignum.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/cached-powers.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/diy-fp.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fixed-dtoa.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/strtod.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/utils.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-diy-fp.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-utils.h +---------------------------------------------------------------------------------------------------- +Copyright 2010 the V8 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: double-conversion +LIBRARY: icu +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/fast-dtoa.cc +FILE: ../../../third_party/dart/runtime/third_party/double-conversion/src/ieee.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-ieee.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.h +FILE: ../../../third_party/icu/source/i18n/double-conversion.h +---------------------------------------------------------------------------------------------------- +Copyright 2012 the V8 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: ffx_spd +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/ffx_a.h +TYPE: LicenseType.mit +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/ffx_a.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2017-2019 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) <2014> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: ffx_spd +ORIGIN: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/ffx_spd.h +TYPE: LicenseType.mit +FILE: ../../../third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/ffx_spd.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: files +ORIGIN: ../../../third_party/expat/files/COPYING +TYPE: LicenseType.mit +FILE: ../../../third_party/expat/files/Changes +FILE: ../../../third_party/expat/files/MANIFEST +FILE: ../../../third_party/expat/files/lib/amigaconfig.h +FILE: ../../../third_party/expat/files/lib/expat_config.h +FILE: ../../../third_party/expat/files/lib/internal.h +FILE: ../../../third_party/expat/files/lib/libexpat.def +FILE: ../../../third_party/expat/files/lib/libexpatw.def +FILE: ../../../third_party/expat/files/lib/nametab.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + and Clark Cooper +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: files +ORIGIN: ../../../third_party/expat/files/lib/ascii.h + ../../../third_party/expat/files/COPYING +TYPE: LicenseType.mit +FILE: ../../../third_party/expat/files/lib/ascii.h +FILE: ../../../third_party/expat/files/lib/asciitab.h +FILE: ../../../third_party/expat/files/lib/iasciitab.h +FILE: ../../../third_party/expat/files/lib/latin1tab.h +FILE: ../../../third_party/expat/files/lib/utf8tab.h +FILE: ../../../third_party/expat/files/lib/xmlrole.c +FILE: ../../../third_party/expat/files/lib/xmlrole.h +FILE: ../../../third_party/expat/files/lib/xmltok.c +FILE: ../../../third_party/expat/files/lib/xmltok.h +FILE: ../../../third_party/expat/files/lib/xmltok_impl.c +FILE: ../../../third_party/expat/files/lib/xmltok_impl.c.original +FILE: ../../../third_party/expat/files/lib/xmltok_impl.h +FILE: ../../../third_party/expat/files/lib/xmltok_ns.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: files +ORIGIN: ../../../third_party/expat/files/lib/expat.h + ../../../third_party/expat/files/COPYING +TYPE: LicenseType.mit +FILE: ../../../third_party/expat/files/lib/expat.h +FILE: ../../../third_party/expat/files/lib/expat_external.h +FILE: ../../../third_party/expat/files/lib/xmlparse.c +FILE: ../../../third_party/expat/files/lib/xmlparse.c.original +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: files +ORIGIN: ../../../third_party/expat/files/lib/macconfig.h + ../../../third_party/expat/files/COPYING +TYPE: LicenseType.mit +FILE: ../../../third_party/expat/files/lib/macconfig.h +FILE: ../../../third_party/expat/files/lib/winconfig.h +FILE: ../../../third_party/expat/files/lib/winconfig.h.original +---------------------------------------------------------------------------------------------------- +Copyright 2000, Clark Cooper +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/docs/FTL.TXT +TYPE: LicenseType.freetype +FILE: ../../../third_party/freetype2/.mailmap +FILE: ../../../third_party/freetype2/devel/ft2build.h +FILE: ../../../third_party/freetype2/devel/ftoption.h +FILE: ../../../third_party/freetype2/include/freetype-flutter-config/ftmodule.h +FILE: ../../../third_party/freetype2/include/freetype-flutter-config/ftoption.h +FILE: ../../../third_party/freetype2/include/freetype/config/ftconfig.h +FILE: ../../../third_party/freetype2/include/freetype/config/ftheader.h +FILE: ../../../third_party/freetype2/include/freetype/config/ftmodule.h +FILE: ../../../third_party/freetype2/include/freetype/config/ftoption.h +FILE: ../../../third_party/freetype2/include/freetype/config/ftstdlib.h +FILE: ../../../third_party/freetype2/include/freetype/config/integer-types.h +FILE: ../../../third_party/freetype2/include/freetype/config/mac-support.h +FILE: ../../../third_party/freetype2/include/freetype/config/public-macros.h +FILE: ../../../third_party/freetype2/include/freetype/freetype.h +FILE: ../../../third_party/freetype2/include/freetype/ftadvanc.h +FILE: ../../../third_party/freetype2/include/freetype/ftbbox.h +FILE: ../../../third_party/freetype2/include/freetype/ftbdf.h +FILE: ../../../third_party/freetype2/include/freetype/ftbitmap.h +FILE: ../../../third_party/freetype2/include/freetype/ftbzip2.h +FILE: ../../../third_party/freetype2/include/freetype/ftcache.h +FILE: ../../../third_party/freetype2/include/freetype/ftchapters.h +FILE: ../../../third_party/freetype2/include/freetype/ftcid.h +FILE: ../../../third_party/freetype2/include/freetype/ftcolor.h +FILE: ../../../third_party/freetype2/include/freetype/ftdriver.h +FILE: ../../../third_party/freetype2/include/freetype/fterrdef.h +FILE: ../../../third_party/freetype2/include/freetype/fterrors.h +FILE: ../../../third_party/freetype2/include/freetype/ftfntfmt.h +FILE: ../../../third_party/freetype2/include/freetype/ftgasp.h +FILE: ../../../third_party/freetype2/include/freetype/ftglyph.h +FILE: ../../../third_party/freetype2/include/freetype/ftgxval.h +FILE: ../../../third_party/freetype2/include/freetype/ftgzip.h +FILE: ../../../third_party/freetype2/include/freetype/ftimage.h +FILE: ../../../third_party/freetype2/include/freetype/ftincrem.h +FILE: ../../../third_party/freetype2/include/freetype/ftlcdfil.h +FILE: ../../../third_party/freetype2/include/freetype/ftlist.h +FILE: ../../../third_party/freetype2/include/freetype/ftlzw.h +FILE: ../../../third_party/freetype2/include/freetype/ftmac.h +FILE: ../../../third_party/freetype2/include/freetype/ftmm.h +FILE: ../../../third_party/freetype2/include/freetype/ftmodapi.h +FILE: ../../../third_party/freetype2/include/freetype/ftmoderr.h +FILE: ../../../third_party/freetype2/include/freetype/ftotval.h +FILE: ../../../third_party/freetype2/include/freetype/ftoutln.h +FILE: ../../../third_party/freetype2/include/freetype/ftparams.h +FILE: ../../../third_party/freetype2/include/freetype/ftpfr.h +FILE: ../../../third_party/freetype2/include/freetype/ftrender.h +FILE: ../../../third_party/freetype2/include/freetype/ftsizes.h +FILE: ../../../third_party/freetype2/include/freetype/ftsnames.h +FILE: ../../../third_party/freetype2/include/freetype/ftstroke.h +FILE: ../../../third_party/freetype2/include/freetype/ftsynth.h +FILE: ../../../third_party/freetype2/include/freetype/ftsystem.h +FILE: ../../../third_party/freetype2/include/freetype/fttrigon.h +FILE: ../../../third_party/freetype2/include/freetype/fttypes.h +FILE: ../../../third_party/freetype2/include/freetype/ftwinfnt.h +FILE: ../../../third_party/freetype2/include/freetype/internal/autohint.h +FILE: ../../../third_party/freetype2/include/freetype/internal/cffotypes.h +FILE: ../../../third_party/freetype2/include/freetype/internal/cfftypes.h +FILE: ../../../third_party/freetype2/include/freetype/internal/compiler-macros.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftcalc.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftdebug.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftdrv.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftgloadr.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftmemory.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftobjs.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftpsprop.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftrfork.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftserv.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftstream.h +FILE: ../../../third_party/freetype2/include/freetype/internal/fttrace.h +FILE: ../../../third_party/freetype2/include/freetype/internal/ftvalid.h +FILE: ../../../third_party/freetype2/include/freetype/internal/psaux.h +FILE: ../../../third_party/freetype2/include/freetype/internal/pshints.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svbdf.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svcfftl.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svcid.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svfntfmt.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svgldict.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svgxval.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svkern.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svmetric.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svmm.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svotval.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpfr.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpostnm.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svprop.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpscmap.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svpsinfo.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svsfnt.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svttcmap.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svtteng.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svttglyf.h +FILE: ../../../third_party/freetype2/include/freetype/internal/services/svwinfnt.h +FILE: ../../../third_party/freetype2/include/freetype/internal/sfnt.h +FILE: ../../../third_party/freetype2/include/freetype/internal/t1types.h +FILE: ../../../third_party/freetype2/include/freetype/internal/tttypes.h +FILE: ../../../third_party/freetype2/include/freetype/internal/wofftypes.h +FILE: ../../../third_party/freetype2/include/freetype/t1tables.h +FILE: ../../../third_party/freetype2/include/freetype/ttnameid.h +FILE: ../../../third_party/freetype2/include/freetype/tttables.h +FILE: ../../../third_party/freetype2/include/freetype/tttags.h +FILE: ../../../third_party/freetype2/include/ft2build.h +FILE: ../../../third_party/freetype2/meson.build +FILE: ../../../third_party/freetype2/modules.cfg +FILE: ../../../third_party/freetype2/src/autofit/afangles.c +FILE: ../../../third_party/freetype2/src/autofit/afangles.h +FILE: ../../../third_party/freetype2/src/autofit/afblue.c +FILE: ../../../third_party/freetype2/src/autofit/afblue.cin +FILE: ../../../third_party/freetype2/src/autofit/afblue.dat +FILE: ../../../third_party/freetype2/src/autofit/afblue.h +FILE: ../../../third_party/freetype2/src/autofit/afblue.hin +FILE: ../../../third_party/freetype2/src/autofit/afcjk.c +FILE: ../../../third_party/freetype2/src/autofit/afcjk.h +FILE: ../../../third_party/freetype2/src/autofit/afcover.h +FILE: ../../../third_party/freetype2/src/autofit/afdummy.c +FILE: ../../../third_party/freetype2/src/autofit/afdummy.h +FILE: ../../../third_party/freetype2/src/autofit/aferrors.h +FILE: ../../../third_party/freetype2/src/autofit/afglobal.c +FILE: ../../../third_party/freetype2/src/autofit/afglobal.h +FILE: ../../../third_party/freetype2/src/autofit/afhints.c +FILE: ../../../third_party/freetype2/src/autofit/afhints.h +FILE: ../../../third_party/freetype2/src/autofit/afindic.c +FILE: ../../../third_party/freetype2/src/autofit/afindic.h +FILE: ../../../third_party/freetype2/src/autofit/aflatin.c +FILE: ../../../third_party/freetype2/src/autofit/aflatin.h +FILE: ../../../third_party/freetype2/src/autofit/aflatin2.c +FILE: ../../../third_party/freetype2/src/autofit/aflatin2.h +FILE: ../../../third_party/freetype2/src/autofit/afloader.c +FILE: ../../../third_party/freetype2/src/autofit/afloader.h +FILE: ../../../third_party/freetype2/src/autofit/afmodule.c +FILE: ../../../third_party/freetype2/src/autofit/afmodule.h +FILE: ../../../third_party/freetype2/src/autofit/afranges.c +FILE: ../../../third_party/freetype2/src/autofit/afranges.h +FILE: ../../../third_party/freetype2/src/autofit/afscript.h +FILE: ../../../third_party/freetype2/src/autofit/afshaper.c +FILE: ../../../third_party/freetype2/src/autofit/afshaper.h +FILE: ../../../third_party/freetype2/src/autofit/afstyles.h +FILE: ../../../third_party/freetype2/src/autofit/aftypes.h +FILE: ../../../third_party/freetype2/src/autofit/afwarp.c +FILE: ../../../third_party/freetype2/src/autofit/afwarp.h +FILE: ../../../third_party/freetype2/src/autofit/afwrtsys.h +FILE: ../../../third_party/freetype2/src/autofit/autofit.c +FILE: ../../../third_party/freetype2/src/base/ftadvanc.c +FILE: ../../../third_party/freetype2/src/base/ftbase.c +FILE: ../../../third_party/freetype2/src/base/ftbase.h +FILE: ../../../third_party/freetype2/src/base/ftbbox.c +FILE: ../../../third_party/freetype2/src/base/ftbdf.c +FILE: ../../../third_party/freetype2/src/base/ftbitmap.c +FILE: ../../../third_party/freetype2/src/base/ftcalc.c +FILE: ../../../third_party/freetype2/src/base/ftcid.c +FILE: ../../../third_party/freetype2/src/base/ftcolor.c +FILE: ../../../third_party/freetype2/src/base/ftdbgmem.c +FILE: ../../../third_party/freetype2/src/base/ftdebug.c +FILE: ../../../third_party/freetype2/src/base/fterrors.c +FILE: ../../../third_party/freetype2/src/base/ftfntfmt.c +FILE: ../../../third_party/freetype2/src/base/ftfstype.c +FILE: ../../../third_party/freetype2/src/base/ftgasp.c +FILE: ../../../third_party/freetype2/src/base/ftgloadr.c +FILE: ../../../third_party/freetype2/src/base/ftglyph.c +FILE: ../../../third_party/freetype2/src/base/ftgxval.c +FILE: ../../../third_party/freetype2/src/base/ftinit.c +FILE: ../../../third_party/freetype2/src/base/ftlcdfil.c +FILE: ../../../third_party/freetype2/src/base/ftmac.c +FILE: ../../../third_party/freetype2/src/base/ftmm.c +FILE: ../../../third_party/freetype2/src/base/ftobjs.c +FILE: ../../../third_party/freetype2/src/base/ftotval.c +FILE: ../../../third_party/freetype2/src/base/ftoutln.c +FILE: ../../../third_party/freetype2/src/base/ftpatent.c +FILE: ../../../third_party/freetype2/src/base/ftpfr.c +FILE: ../../../third_party/freetype2/src/base/ftpsprop.c +FILE: ../../../third_party/freetype2/src/base/ftrfork.c +FILE: ../../../third_party/freetype2/src/base/ftsnames.c +FILE: ../../../third_party/freetype2/src/base/ftstream.c +FILE: ../../../third_party/freetype2/src/base/ftstroke.c +FILE: ../../../third_party/freetype2/src/base/ftsynth.c +FILE: ../../../third_party/freetype2/src/base/ftsystem.c +FILE: ../../../third_party/freetype2/src/base/fttrigon.c +FILE: ../../../third_party/freetype2/src/base/fttype1.c +FILE: ../../../third_party/freetype2/src/base/ftutil.c +FILE: ../../../third_party/freetype2/src/base/ftver.rc +FILE: ../../../third_party/freetype2/src/base/ftwinfnt.c +FILE: ../../../third_party/freetype2/src/base/md5.c +FILE: ../../../third_party/freetype2/src/base/md5.h +FILE: ../../../third_party/freetype2/src/bzip2/ftbzip2.c +FILE: ../../../third_party/freetype2/src/cache/ftcache.c +FILE: ../../../third_party/freetype2/src/cache/ftcbasic.c +FILE: ../../../third_party/freetype2/src/cache/ftccache.c +FILE: ../../../third_party/freetype2/src/cache/ftccache.h +FILE: ../../../third_party/freetype2/src/cache/ftccback.h +FILE: ../../../third_party/freetype2/src/cache/ftccmap.c +FILE: ../../../third_party/freetype2/src/cache/ftcerror.h +FILE: ../../../third_party/freetype2/src/cache/ftcglyph.c +FILE: ../../../third_party/freetype2/src/cache/ftcglyph.h +FILE: ../../../third_party/freetype2/src/cache/ftcimage.c +FILE: ../../../third_party/freetype2/src/cache/ftcimage.h +FILE: ../../../third_party/freetype2/src/cache/ftcmanag.c +FILE: ../../../third_party/freetype2/src/cache/ftcmanag.h +FILE: ../../../third_party/freetype2/src/cache/ftcmru.c +FILE: ../../../third_party/freetype2/src/cache/ftcmru.h +FILE: ../../../third_party/freetype2/src/cache/ftcsbits.c +FILE: ../../../third_party/freetype2/src/cache/ftcsbits.h +FILE: ../../../third_party/freetype2/src/cff/cff.c +FILE: ../../../third_party/freetype2/src/cff/cffcmap.c +FILE: ../../../third_party/freetype2/src/cff/cffcmap.h +FILE: ../../../third_party/freetype2/src/cff/cffdrivr.c +FILE: ../../../third_party/freetype2/src/cff/cffdrivr.h +FILE: ../../../third_party/freetype2/src/cff/cfferrs.h +FILE: ../../../third_party/freetype2/src/cff/cffgload.c +FILE: ../../../third_party/freetype2/src/cff/cffgload.h +FILE: ../../../third_party/freetype2/src/cff/cffload.c +FILE: ../../../third_party/freetype2/src/cff/cffload.h +FILE: ../../../third_party/freetype2/src/cff/cffobjs.c +FILE: ../../../third_party/freetype2/src/cff/cffobjs.h +FILE: ../../../third_party/freetype2/src/cff/cffparse.c +FILE: ../../../third_party/freetype2/src/cff/cffparse.h +FILE: ../../../third_party/freetype2/src/cff/cfftoken.h +FILE: ../../../third_party/freetype2/src/cid/ciderrs.h +FILE: ../../../third_party/freetype2/src/cid/cidgload.c +FILE: ../../../third_party/freetype2/src/cid/cidgload.h +FILE: ../../../third_party/freetype2/src/cid/cidload.c +FILE: ../../../third_party/freetype2/src/cid/cidload.h +FILE: ../../../third_party/freetype2/src/cid/cidobjs.c +FILE: ../../../third_party/freetype2/src/cid/cidobjs.h +FILE: ../../../third_party/freetype2/src/cid/cidparse.c +FILE: ../../../third_party/freetype2/src/cid/cidparse.h +FILE: ../../../third_party/freetype2/src/cid/cidriver.c +FILE: ../../../third_party/freetype2/src/cid/cidriver.h +FILE: ../../../third_party/freetype2/src/cid/cidtoken.h +FILE: ../../../third_party/freetype2/src/cid/type1cid.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvalid.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvalid.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxvbsln.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvcommn.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvcommn.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxverror.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxvfeat.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvfeat.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxvfgen.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvjust.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvkern.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvlcar.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmod.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmod.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort0.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort1.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort2.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort4.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmort5.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx.h +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx0.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx1.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx2.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx4.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvmorx5.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvopbd.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvprop.c +FILE: ../../../third_party/freetype2/src/gxvalid/gxvtrak.c +FILE: ../../../third_party/freetype2/src/gzip/ftgzip.c +FILE: ../../../third_party/freetype2/src/lzw/ftlzw.c +FILE: ../../../third_party/freetype2/src/lzw/ftzopen.c +FILE: ../../../third_party/freetype2/src/lzw/ftzopen.h +FILE: ../../../third_party/freetype2/src/otvalid/otvalid.c +FILE: ../../../third_party/freetype2/src/otvalid/otvalid.h +FILE: ../../../third_party/freetype2/src/otvalid/otvbase.c +FILE: ../../../third_party/freetype2/src/otvalid/otvcommn.c +FILE: ../../../third_party/freetype2/src/otvalid/otvcommn.h +FILE: ../../../third_party/freetype2/src/otvalid/otverror.h +FILE: ../../../third_party/freetype2/src/otvalid/otvgdef.c +FILE: ../../../third_party/freetype2/src/otvalid/otvgpos.c +FILE: ../../../third_party/freetype2/src/otvalid/otvgpos.h +FILE: ../../../third_party/freetype2/src/otvalid/otvgsub.c +FILE: ../../../third_party/freetype2/src/otvalid/otvjstf.c +FILE: ../../../third_party/freetype2/src/otvalid/otvmath.c +FILE: ../../../third_party/freetype2/src/otvalid/otvmod.c +FILE: ../../../third_party/freetype2/src/otvalid/otvmod.h +FILE: ../../../third_party/freetype2/src/pcf/pcferror.h +FILE: ../../../third_party/freetype2/src/pfr/pfr.c +FILE: ../../../third_party/freetype2/src/pfr/pfrcmap.c +FILE: ../../../third_party/freetype2/src/pfr/pfrcmap.h +FILE: ../../../third_party/freetype2/src/pfr/pfrdrivr.c +FILE: ../../../third_party/freetype2/src/pfr/pfrdrivr.h +FILE: ../../../third_party/freetype2/src/pfr/pfrerror.h +FILE: ../../../third_party/freetype2/src/pfr/pfrgload.c +FILE: ../../../third_party/freetype2/src/pfr/pfrgload.h +FILE: ../../../third_party/freetype2/src/pfr/pfrload.c +FILE: ../../../third_party/freetype2/src/pfr/pfrload.h +FILE: ../../../third_party/freetype2/src/pfr/pfrobjs.c +FILE: ../../../third_party/freetype2/src/pfr/pfrobjs.h +FILE: ../../../third_party/freetype2/src/pfr/pfrsbit.c +FILE: ../../../third_party/freetype2/src/pfr/pfrsbit.h +FILE: ../../../third_party/freetype2/src/pfr/pfrtypes.h +FILE: ../../../third_party/freetype2/src/psaux/afmparse.c +FILE: ../../../third_party/freetype2/src/psaux/afmparse.h +FILE: ../../../third_party/freetype2/src/psaux/cffdecode.c +FILE: ../../../third_party/freetype2/src/psaux/cffdecode.h +FILE: ../../../third_party/freetype2/src/psaux/psarrst.c +FILE: ../../../third_party/freetype2/src/psaux/psarrst.h +FILE: ../../../third_party/freetype2/src/psaux/psaux.c +FILE: ../../../third_party/freetype2/src/psaux/psauxerr.h +FILE: ../../../third_party/freetype2/src/psaux/psauxmod.c +FILE: ../../../third_party/freetype2/src/psaux/psauxmod.h +FILE: ../../../third_party/freetype2/src/psaux/psblues.c +FILE: ../../../third_party/freetype2/src/psaux/psblues.h +FILE: ../../../third_party/freetype2/src/psaux/psconv.c +FILE: ../../../third_party/freetype2/src/psaux/psconv.h +FILE: ../../../third_party/freetype2/src/psaux/pserror.c +FILE: ../../../third_party/freetype2/src/psaux/pserror.h +FILE: ../../../third_party/freetype2/src/psaux/psfixed.h +FILE: ../../../third_party/freetype2/src/psaux/psfont.c +FILE: ../../../third_party/freetype2/src/psaux/psfont.h +FILE: ../../../third_party/freetype2/src/psaux/psft.c +FILE: ../../../third_party/freetype2/src/psaux/psft.h +FILE: ../../../third_party/freetype2/src/psaux/psglue.h +FILE: ../../../third_party/freetype2/src/psaux/pshints.c +FILE: ../../../third_party/freetype2/src/psaux/pshints.h +FILE: ../../../third_party/freetype2/src/psaux/psintrp.c +FILE: ../../../third_party/freetype2/src/psaux/psintrp.h +FILE: ../../../third_party/freetype2/src/psaux/psobjs.c +FILE: ../../../third_party/freetype2/src/psaux/psobjs.h +FILE: ../../../third_party/freetype2/src/psaux/psread.c +FILE: ../../../third_party/freetype2/src/psaux/psread.h +FILE: ../../../third_party/freetype2/src/psaux/psstack.c +FILE: ../../../third_party/freetype2/src/psaux/psstack.h +FILE: ../../../third_party/freetype2/src/psaux/pstypes.h +FILE: ../../../third_party/freetype2/src/psaux/t1cmap.c +FILE: ../../../third_party/freetype2/src/psaux/t1cmap.h +FILE: ../../../third_party/freetype2/src/psaux/t1decode.c +FILE: ../../../third_party/freetype2/src/psaux/t1decode.h +FILE: ../../../third_party/freetype2/src/pshinter/pshalgo.c +FILE: ../../../third_party/freetype2/src/pshinter/pshalgo.h +FILE: ../../../third_party/freetype2/src/pshinter/pshglob.c +FILE: ../../../third_party/freetype2/src/pshinter/pshglob.h +FILE: ../../../third_party/freetype2/src/pshinter/pshinter.c +FILE: ../../../third_party/freetype2/src/pshinter/pshmod.c +FILE: ../../../third_party/freetype2/src/pshinter/pshmod.h +FILE: ../../../third_party/freetype2/src/pshinter/pshnterr.h +FILE: ../../../third_party/freetype2/src/pshinter/pshrec.c +FILE: ../../../third_party/freetype2/src/pshinter/pshrec.h +FILE: ../../../third_party/freetype2/src/psnames/psmodule.c +FILE: ../../../third_party/freetype2/src/psnames/psmodule.h +FILE: ../../../third_party/freetype2/src/psnames/psnamerr.h +FILE: ../../../third_party/freetype2/src/psnames/psnames.c +FILE: ../../../third_party/freetype2/src/psnames/pstables.h +FILE: ../../../third_party/freetype2/src/raster/ftmisc.h +FILE: ../../../third_party/freetype2/src/raster/ftraster.c +FILE: ../../../third_party/freetype2/src/raster/ftraster.h +FILE: ../../../third_party/freetype2/src/raster/ftrend1.c +FILE: ../../../third_party/freetype2/src/raster/ftrend1.h +FILE: ../../../third_party/freetype2/src/raster/raster.c +FILE: ../../../third_party/freetype2/src/raster/rasterrs.h +FILE: ../../../third_party/freetype2/src/sfnt/pngshim.c +FILE: ../../../third_party/freetype2/src/sfnt/pngshim.h +FILE: ../../../third_party/freetype2/src/sfnt/sfdriver.c +FILE: ../../../third_party/freetype2/src/sfnt/sfdriver.h +FILE: ../../../third_party/freetype2/src/sfnt/sferrors.h +FILE: ../../../third_party/freetype2/src/sfnt/sfnt.c +FILE: ../../../third_party/freetype2/src/sfnt/sfobjs.c +FILE: ../../../third_party/freetype2/src/sfnt/sfobjs.h +FILE: ../../../third_party/freetype2/src/sfnt/sfwoff.c +FILE: ../../../third_party/freetype2/src/sfnt/sfwoff.h +FILE: ../../../third_party/freetype2/src/sfnt/sfwoff2.c +FILE: ../../../third_party/freetype2/src/sfnt/sfwoff2.h +FILE: ../../../third_party/freetype2/src/sfnt/ttbdf.c +FILE: ../../../third_party/freetype2/src/sfnt/ttbdf.h +FILE: ../../../third_party/freetype2/src/sfnt/ttcmap.c +FILE: ../../../third_party/freetype2/src/sfnt/ttcmap.h +FILE: ../../../third_party/freetype2/src/sfnt/ttcmapc.h +FILE: ../../../third_party/freetype2/src/sfnt/ttcolr.c +FILE: ../../../third_party/freetype2/src/sfnt/ttcolr.h +FILE: ../../../third_party/freetype2/src/sfnt/ttcpal.c +FILE: ../../../third_party/freetype2/src/sfnt/ttcpal.h +FILE: ../../../third_party/freetype2/src/sfnt/ttkern.c +FILE: ../../../third_party/freetype2/src/sfnt/ttkern.h +FILE: ../../../third_party/freetype2/src/sfnt/ttload.c +FILE: ../../../third_party/freetype2/src/sfnt/ttload.h +FILE: ../../../third_party/freetype2/src/sfnt/ttmtx.c +FILE: ../../../third_party/freetype2/src/sfnt/ttmtx.h +FILE: ../../../third_party/freetype2/src/sfnt/ttpost.c +FILE: ../../../third_party/freetype2/src/sfnt/ttpost.h +FILE: ../../../third_party/freetype2/src/sfnt/ttsbit.c +FILE: ../../../third_party/freetype2/src/sfnt/ttsbit.h +FILE: ../../../third_party/freetype2/src/sfnt/woff2tags.c +FILE: ../../../third_party/freetype2/src/sfnt/woff2tags.h +FILE: ../../../third_party/freetype2/src/smooth/ftgrays.c +FILE: ../../../third_party/freetype2/src/smooth/ftgrays.h +FILE: ../../../third_party/freetype2/src/smooth/ftsmerrs.h +FILE: ../../../third_party/freetype2/src/smooth/ftsmooth.c +FILE: ../../../third_party/freetype2/src/smooth/ftsmooth.h +FILE: ../../../third_party/freetype2/src/smooth/smooth.c +FILE: ../../../third_party/freetype2/src/truetype/truetype.c +FILE: ../../../third_party/freetype2/src/truetype/ttdriver.c +FILE: ../../../third_party/freetype2/src/truetype/ttdriver.h +FILE: ../../../third_party/freetype2/src/truetype/tterrors.h +FILE: ../../../third_party/freetype2/src/truetype/ttgload.c +FILE: ../../../third_party/freetype2/src/truetype/ttgload.h +FILE: ../../../third_party/freetype2/src/truetype/ttgxvar.c +FILE: ../../../third_party/freetype2/src/truetype/ttgxvar.h +FILE: ../../../third_party/freetype2/src/truetype/ttinterp.c +FILE: ../../../third_party/freetype2/src/truetype/ttinterp.h +FILE: ../../../third_party/freetype2/src/truetype/ttobjs.c +FILE: ../../../third_party/freetype2/src/truetype/ttobjs.h +FILE: ../../../third_party/freetype2/src/truetype/ttpload.c +FILE: ../../../third_party/freetype2/src/truetype/ttpload.h +FILE: ../../../third_party/freetype2/src/truetype/ttsubpix.c +FILE: ../../../third_party/freetype2/src/truetype/ttsubpix.h +FILE: ../../../third_party/freetype2/src/type1/t1afm.c +FILE: ../../../third_party/freetype2/src/type1/t1afm.h +FILE: ../../../third_party/freetype2/src/type1/t1driver.c +FILE: ../../../third_party/freetype2/src/type1/t1driver.h +FILE: ../../../third_party/freetype2/src/type1/t1errors.h +FILE: ../../../third_party/freetype2/src/type1/t1gload.c +FILE: ../../../third_party/freetype2/src/type1/t1gload.h +FILE: ../../../third_party/freetype2/src/type1/t1load.c +FILE: ../../../third_party/freetype2/src/type1/t1load.h +FILE: ../../../third_party/freetype2/src/type1/t1objs.c +FILE: ../../../third_party/freetype2/src/type1/t1objs.h +FILE: ../../../third_party/freetype2/src/type1/t1parse.c +FILE: ../../../third_party/freetype2/src/type1/t1parse.h +FILE: ../../../third_party/freetype2/src/type1/t1tokens.h +FILE: ../../../third_party/freetype2/src/type1/type1.c +FILE: ../../../third_party/freetype2/src/type42/t42drivr.c +FILE: ../../../third_party/freetype2/src/type42/t42drivr.h +FILE: ../../../third_party/freetype2/src/type42/t42error.h +FILE: ../../../third_party/freetype2/src/type42/t42objs.c +FILE: ../../../third_party/freetype2/src/type42/t42objs.h +FILE: ../../../third_party/freetype2/src/type42/t42parse.c +FILE: ../../../third_party/freetype2/src/type42/t42parse.h +FILE: ../../../third_party/freetype2/src/type42/t42types.h +FILE: ../../../third_party/freetype2/src/type42/type42.c +FILE: ../../../third_party/freetype2/src/winfonts/fnterrs.h +FILE: ../../../third_party/freetype2/src/winfonts/winfnt.c +FILE: ../../../third_party/freetype2/src/winfonts/winfnt.h +FILE: ../../../third_party/freetype2/version.sed +---------------------------------------------------------------------------------------------------- +The FreeType Project LICENSE + + 2006-Jan-27 + +Copyright 1996-2002, 2006 by +David Turner, Robert Wilhelm, and Werner Lemberg + +Introduction +============ + + The FreeType Project is distributed in several archive packages; + some of them may contain, in addition to the FreeType font engine, + various tools and contributions which rely on, or relate to, the + FreeType Project. + + This license applies to all files found in such packages, and + which do not fall under their own explicit license. The license + affects thus the FreeType font engine, the test programs, + documentation and makefiles, at the very least. + + This license was inspired by the BSD, Artistic, and IJG + (Independent JPEG Group) licenses, which all encourage inclusion + and use of free software in commercial and freeware products + alike. As a consequence, its main points are that: + + o We don't promise that this software works. However, we will be + interested in any kind of bug reports. (`as is' distribution) + + o You can use this software for whatever you want, in parts or + full form, without having to pay us. (`royalty-free' usage) + + o You may not pretend that you wrote this software. If you use + it, or only parts of it, in a program, you must acknowledge + somewhere in your documentation that you have used the + FreeType code. (`credits') + + We specifically permit and encourage the inclusion of this + software, with or without modifications, in commercial products. + We disclaim all warranties covering The FreeType Project and + assume no liability related to The FreeType Project. + + Finally, many people asked us for a preferred form for a + credit/disclaimer to use in compliance with this license. We thus + encourage you to use the following text: + + Portions of this software are copyright © The FreeType + Project (www.freetype.org). All rights reserved. + + Please replace with the value from the FreeType version you + actually use. + +Legal Terms +=========== + +0. Definitions + + Throughout this license, the terms `package', `FreeType Project', + and `FreeType archive' refer to the set of files originally + distributed by the authors (David Turner, Robert Wilhelm, and + Werner Lemberg) as the `FreeType Project', be they named as alpha, + beta or final release. + + `You' refers to the licensee, or person using the project, where + `using' is a generic term including compiling the project's source + code as well as linking it to form a `program' or `executable'. + This program is referred to as `a program using the FreeType + engine'. + + This license applies to all files distributed in the original + FreeType Project, including all source code, binaries and + documentation, unless otherwise stated in the file in its + original, unmodified form as distributed in the original archive. + If you are unsure whether or not a particular file is covered by + this license, you must contact us to verify this. + + The FreeType Project is copyright (C) 1996-2000 by David Turner, + Robert Wilhelm, and Werner Lemberg. All rights reserved except as + specified below. + +1. No Warranty + + THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO + USE, OF THE FREETYPE PROJECT. + +2. Redistribution + + This license grants a worldwide, royalty-free, perpetual and + irrevocable right and license to use, execute, perform, compile, + display, copy, create derivative works of, distribute and + sublicense the FreeType Project (in both source and object code + forms) and derivative works thereof for any purpose; and to + authorize others to exercise some or all of the rights granted + herein, subject to the following conditions: + + o Redistribution of source code must retain this license file + (`FTL.TXT') unaltered; any additions, deletions or changes to + the original files must be clearly indicated in accompanying + documentation. The copyright notices of the unaltered, + original files must be preserved in all copies of source + files. + + o Redistribution in binary form must provide a disclaimer that + states that the software is based in part of the work of the + FreeType Team, in the distribution documentation. We also + encourage you to put an URL to the FreeType web page in your + documentation, though this isn't mandatory. + + These conditions apply to any software derived from or based on + the FreeType Project, not just the unmodified files. If you use + our work, you must acknowledge us. However, no fee need be paid + to us. + +3. Advertising + + Neither the FreeType authors and contributors nor you shall use + the name of the other for commercial, advertising, or promotional + purposes without specific prior written permission. + + We suggest, but do not require, that you use one or more of the + following phrases to refer to this software in your documentation + or advertising materials: `FreeType Project', `FreeType Engine', + `FreeType library', or `FreeType Distribution'. + + As you have not signed this license, you are not required to + accept it. However, as the FreeType Project is copyrighted + material, only this license, or another one contracted with the + authors, grants you the right to use, distribute, and modify it. + Therefore, by using, distributing, or modifying the FreeType + Project, you indicate that you understand and accept all the terms + of this license. + +4. Contacts + + There are two mailing lists related to FreeType: + + o freetype@nongnu.org + + Discusses general use and applications of FreeType, as well as + future and wanted additions to the library and distribution. + If you are looking for support, start in this list if you + haven't found anything to help you in the documentation. + + o freetype-devel@nongnu.org + + Discusses bugs, as well as engine internals, design issues, + specific licenses, porting, etc. + + Our home page can be found at + + https://www.freetype.org + +--- end of FTL.TXT --- +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/include/freetype/internal/fthash.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/include/freetype/internal/fthash.h +FILE: ../../../third_party/freetype2/src/base/fthash.c +---------------------------------------------------------------------------------------------------- +Copyright 2000 Computing Research Labs, New Mexico State University +Copyright 2001-2015 + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/bdf/bdf.c +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/bdf/bdf.c +---------------------------------------------------------------------------------------------------- +Copyright (C) 2001, 2002 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/bdf/bdf.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/bdf/bdf.h +---------------------------------------------------------------------------------------------------- +Copyright 2000 Computing Research Labs, New Mexico State University +Copyright 2001-2004, 2011 Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/bdf/bdfdrivr.c +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/bdf/bdfdrivr.c +---------------------------------------------------------------------------------------------------- +Copyright (C) 2001-2008, 2011, 2013, 2014 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/bdf/bdfdrivr.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/bdf/bdfdrivr.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 2001, 2002, 2003, 2004 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/bdf/bdferror.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/bdf/bdferror.h +---------------------------------------------------------------------------------------------------- +Copyright 2001, 2002, 2012 Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/bdf/bdflib.c +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/bdf/bdflib.c +---------------------------------------------------------------------------------------------------- +Copyright 2000 Computing Research Labs, New Mexico State University +Copyright 2001-2014 + Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/gzip/adler32.c + ../../../third_party/freetype2/src/gzip/zlib.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/freetype2/src/gzip/adler32.c +FILE: ../../../third_party/freetype2/src/gzip/infblock.c +FILE: ../../../third_party/freetype2/src/gzip/infblock.h +FILE: ../../../third_party/freetype2/src/gzip/infcodes.c +FILE: ../../../third_party/freetype2/src/gzip/infcodes.h +FILE: ../../../third_party/freetype2/src/gzip/inflate.c +FILE: ../../../third_party/freetype2/src/gzip/inftrees.c +FILE: ../../../third_party/freetype2/src/gzip/inftrees.h +FILE: ../../../third_party/freetype2/src/gzip/infutil.c +FILE: ../../../third_party/freetype2/src/gzip/infutil.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 1995-2002 Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/gzip/ftzconf.h + ../../../third_party/freetype2/src/gzip/zlib.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/freetype2/src/gzip/ftzconf.h +FILE: ../../../third_party/freetype2/src/gzip/zutil.c +FILE: ../../../third_party/freetype2/src/gzip/zutil.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 1995-2002 Jean-loup Gailly. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/gzip/zlib.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/freetype2/src/gzip/inffixed.h +FILE: ../../../third_party/freetype2/src/gzip/zlib.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcf.c +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcf.c +---------------------------------------------------------------------------------------------------- +Copyright 2000-2001, 2003 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcf.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcf.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 2000, 2001, 2002, 2003, 2006, 2010 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcfdrivr.c +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcfdrivr.c +---------------------------------------------------------------------------------------------------- +Copyright (C) 2000-2004, 2006-2011, 2013, 2014 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcfdrivr.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcfdrivr.h +---------------------------------------------------------------------------------------------------- +Copyright 2000-2001, 2002 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcfread.c +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcfread.c +---------------------------------------------------------------------------------------------------- +Copyright 2000-2010, 2012-2014 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcfread.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcfread.h +---------------------------------------------------------------------------------------------------- +Copyright 2003 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcfutil.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/freetype2/src/pcf/pcfutil.c +---------------------------------------------------------------------------------------------------- +Copyright 1990, 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. +==================================================================================================== + +==================================================================================================== +LIBRARY: freetype2 +ORIGIN: ../../../third_party/freetype2/src/pcf/pcfutil.h +TYPE: LicenseType.mit +FILE: ../../../third_party/freetype2/src/pcf/pcfutil.h +---------------------------------------------------------------------------------------------------- +Copyright 2000, 2001, 2004 by +Francesco Zappa Nardelli + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/COPYING.txt +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/.appveyor.yml +FILE: ../../../third_party/glfw/CMake/MacOSXBundleInfo.plist.in +FILE: ../../../third_party/glfw/cmake_uninstall.cmake.in +FILE: ../../../third_party/glfw/docs/Doxyfile.in +FILE: ../../../third_party/glfw/docs/DoxygenLayout.xml +FILE: ../../../third_party/glfw/docs/build.dox +FILE: ../../../third_party/glfw/docs/compat.dox +FILE: ../../../third_party/glfw/docs/compile.dox +FILE: ../../../third_party/glfw/docs/context.dox +FILE: ../../../third_party/glfw/docs/extra.less +FILE: ../../../third_party/glfw/docs/footer.html +FILE: ../../../third_party/glfw/docs/header.html +FILE: ../../../third_party/glfw/docs/input.dox +FILE: ../../../third_party/glfw/docs/internal.dox +FILE: ../../../third_party/glfw/docs/intro.dox +FILE: ../../../third_party/glfw/docs/main.dox +FILE: ../../../third_party/glfw/docs/monitor.dox +FILE: ../../../third_party/glfw/docs/moving.dox +FILE: ../../../third_party/glfw/docs/news.dox +FILE: ../../../third_party/glfw/docs/quick.dox +FILE: ../../../third_party/glfw/docs/spaces.svg +FILE: ../../../third_party/glfw/docs/vulkan.dox +FILE: ../../../third_party/glfw/docs/window.dox +FILE: ../../../third_party/glfw/include/GLFW/glfw3.h +FILE: ../../../third_party/glfw/include/GLFW/glfw3native.h +FILE: ../../../third_party/glfw/src/cocoa_monitor.m +FILE: ../../../third_party/glfw/src/context.c +FILE: ../../../third_party/glfw/src/egl_context.c +FILE: ../../../third_party/glfw/src/egl_context.h +FILE: ../../../third_party/glfw/src/glfw3.pc.in +FILE: ../../../third_party/glfw/src/glfw3Config.cmake.in +FILE: ../../../third_party/glfw/src/glx_context.c +FILE: ../../../third_party/glfw/src/glx_context.h +FILE: ../../../third_party/glfw/src/init.c +FILE: ../../../third_party/glfw/src/input.c +FILE: ../../../third_party/glfw/src/internal.h +FILE: ../../../third_party/glfw/src/linux_joystick.c +FILE: ../../../third_party/glfw/src/monitor.c +FILE: ../../../third_party/glfw/src/posix_time.c +FILE: ../../../third_party/glfw/src/posix_time.h +FILE: ../../../third_party/glfw/src/posix_tls.c +FILE: ../../../third_party/glfw/src/posix_tls.h +FILE: ../../../third_party/glfw/src/vulkan.c +FILE: ../../../third_party/glfw/src/wgl_context.c +FILE: ../../../third_party/glfw/src/wgl_context.h +FILE: ../../../third_party/glfw/src/win32_init.c +FILE: ../../../third_party/glfw/src/win32_joystick.c +FILE: ../../../third_party/glfw/src/win32_monitor.c +FILE: ../../../third_party/glfw/src/win32_platform.h +FILE: ../../../third_party/glfw/src/win32_time.c +FILE: ../../../third_party/glfw/src/win32_tls.c +FILE: ../../../third_party/glfw/src/win32_window.c +FILE: ../../../third_party/glfw/src/x11_init.c +FILE: ../../../third_party/glfw/src/x11_monitor.c +FILE: ../../../third_party/glfw/src/x11_platform.h +FILE: ../../../third_party/glfw/src/x11_window.c +FILE: ../../../third_party/glfw/src/xkb_unicode.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2016 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/cocoa_init.m +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/cocoa_init.m +FILE: ../../../third_party/glfw/src/cocoa_platform.h +FILE: ../../../third_party/glfw/src/cocoa_time.c +FILE: ../../../third_party/glfw/src/cocoa_window.m +FILE: ../../../third_party/glfw/src/nsgl_context.h +FILE: ../../../third_party/glfw/src/nsgl_context.m +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2016 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/cocoa_joystick.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/cocoa_joystick.h +FILE: ../../../third_party/glfw/src/win32_joystick.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2006-2016 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/cocoa_joystick.m +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/cocoa_joystick.m +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2016 Camilla Berglund +Copyright (c) 2012 Torsten Walluhn + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/glfw_config.h.in +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/glfw_config.h.in +---------------------------------------------------------------------------------------------------- +Copyright (c) 2010-2016 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/linux_joystick.h +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/linux_joystick.h +FILE: ../../../third_party/glfw/src/wl_init.c +FILE: ../../../third_party/glfw/src/wl_monitor.c +FILE: ../../../third_party/glfw/src/wl_platform.h +FILE: ../../../third_party/glfw/src/wl_window.c +FILE: ../../../third_party/glfw/src/xkb_unicode.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014 Jonas Ådahl + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/mir_init.c +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/mir_init.c +FILE: ../../../third_party/glfw/src/mir_monitor.c +FILE: ../../../third_party/glfw/src/mir_platform.h +FILE: ../../../third_party/glfw/src/mir_window.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014-2015 Brandon Schaefer + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: glfw +ORIGIN: ../../../third_party/glfw/src/window.c +TYPE: LicenseType.zlib +FILE: ../../../third_party/glfw/src/window.c +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2016 Camilla Berglund +Copyright (c) 2012 Torsten Walluhn + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/COPYING +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/.circleci/config.yml +FILE: ../../../third_party/harfbuzz/.codecov.yml +FILE: ../../../third_party/harfbuzz/.editorconfig +FILE: ../../../third_party/harfbuzz/THANKS +FILE: ../../../third_party/harfbuzz/TODO +FILE: ../../../third_party/harfbuzz/docs/HarfBuzz.png +FILE: ../../../third_party/harfbuzz/docs/HarfBuzz.svg +FILE: ../../../third_party/harfbuzz/docs/harfbuzz-docs.xml +FILE: ../../../third_party/harfbuzz/docs/meson.build +FILE: ../../../third_party/harfbuzz/docs/usermanual-buffers-language-script-and-direction.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-clusters.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-fonts-and-faces.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-getting-started.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-glyph-information.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-install-harfbuzz.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-integration.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-object-model.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-opentype-features.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-shaping-concepts.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-utilities.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-what-is-harfbuzz.xml +FILE: ../../../third_party/harfbuzz/docs/version.xml.in +FILE: ../../../third_party/harfbuzz/harfbuzz.doap +FILE: ../../../third_party/harfbuzz/meson-cc-tests/intel-atomic-primitives-test.c +FILE: ../../../third_party/harfbuzz/meson-cc-tests/solaris-atomic-operations.c +FILE: ../../../third_party/harfbuzz/meson.build +FILE: ../../../third_party/harfbuzz/perf/fonts/Amiri-Regular.ttf +FILE: ../../../third_party/harfbuzz/perf/fonts/NotoNastaliqUrdu-Regular.ttf +FILE: ../../../third_party/harfbuzz/perf/fonts/NotoSansDevanagari-Regular.ttf +FILE: ../../../third_party/harfbuzz/perf/fonts/Roboto-Regular.ttf +FILE: ../../../third_party/harfbuzz/perf/meson.build +FILE: ../../../third_party/harfbuzz/perf/perf-draw.hh +FILE: ../../../third_party/harfbuzz/perf/perf-extents.hh +FILE: ../../../third_party/harfbuzz/perf/perf-shaping.hh +FILE: ../../../third_party/harfbuzz/perf/perf.cc +FILE: ../../../third_party/harfbuzz/src/Makefile.sources +FILE: ../../../third_party/harfbuzz/src/harfbuzz-config.cmake.in +FILE: ../../../third_party/harfbuzz/src/harfbuzz-gobject.pc.in +FILE: ../../../third_party/harfbuzz/src/harfbuzz-icu.pc.in +FILE: ../../../third_party/harfbuzz/src/harfbuzz-subset.pc.in +FILE: ../../../third_party/harfbuzz/src/harfbuzz.cc +FILE: ../../../third_party/harfbuzz/src/harfbuzz.pc.in +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-joining-list.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-table.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use-table.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-tag-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ucd-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-unicode-emoji-table.hh +FILE: ../../../third_party/harfbuzz/src/meson.build +FILE: ../../../third_party/harfbuzz/src/update-unicode-tables.make +FILE: ../../../third_party/harfbuzz/subprojects/cairo.wrap +FILE: ../../../third_party/harfbuzz/subprojects/expat.wrap +FILE: ../../../third_party/harfbuzz/subprojects/fontconfig.wrap +FILE: ../../../third_party/harfbuzz/subprojects/freetype2.wrap +FILE: ../../../third_party/harfbuzz/subprojects/glib.wrap +FILE: ../../../third_party/harfbuzz/subprojects/google-benchmark.wrap +FILE: ../../../third_party/harfbuzz/subprojects/libffi.wrap +FILE: ../../../third_party/harfbuzz/subprojects/libpng.wrap +FILE: ../../../third_party/harfbuzz/subprojects/pixman.wrap +FILE: ../../../third_party/harfbuzz/subprojects/proxy-libintl.wrap +FILE: ../../../third_party/harfbuzz/subprojects/ttf-parser.wrap +FILE: ../../../third_party/harfbuzz/subprojects/zlib.wrap +---------------------------------------------------------------------------------------------------- +HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. +For parts of HarfBuzz that are licensed under different licenses see individual +files names COPYING in subdirectories where applicable. + +Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. +Copyright © 2018,2019,2020 Ebrahim Byagowi +Copyright © 2019,2020 Facebook, Inc. +Copyright © 2012 Mozilla Foundation +Copyright © 2011 Codethink Limited +Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) +Copyright © 2009 Keith Stribley +Copyright © 2009 Martin Hosken and SIL International +Copyright © 2007 Chris Wilson +Copyright © 2006 Behdad Esfahbod +Copyright © 2005 David Turner +Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. +Copyright © 1998-2004 David Turner and Werner Lemberg + +For full copyright notices consult the individual files in the package. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/dump-indic-data.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/dump-indic-data.cc +FILE: ../../../third_party/harfbuzz/src/dump-khmer-data.cc +FILE: ../../../third_party/harfbuzz/src/dump-myanmar-data.cc +FILE: ../../../third_party/harfbuzz/src/dump-use-data.cc +FILE: ../../../third_party/harfbuzz/src/hb-aat-map.hh +FILE: ../../../third_party/harfbuzz/src/hb-array.hh +FILE: ../../../third_party/harfbuzz/src/hb-map.cc +FILE: ../../../third_party/harfbuzz/src/hb-map.h +FILE: ../../../third_party/harfbuzz/src/hb-map.hh +FILE: ../../../third_party/harfbuzz/src/hb-meta.hh +FILE: ../../../third_party/harfbuzz/src/hb-null.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-deprecated.h +FILE: ../../../third_party/harfbuzz/src/hb-ot-face.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-hdmx-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-name-language-static.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-name-language.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-name.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-os2-unicode-ranges.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.hh +FILE: ../../../third_party/harfbuzz/src/hb-static.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset-input.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset-input.hh +FILE: ../../../third_party/harfbuzz/src/hb-subset-plan.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset-plan.hh +FILE: ../../../third_party/harfbuzz/src/hb-subset.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset.h +FILE: ../../../third_party/harfbuzz/src/hb-subset.hh +FILE: ../../../third_party/harfbuzz/src/test-iter.cc +FILE: ../../../third_party/harfbuzz/src/test-ot-name.cc +FILE: ../../../third_party/harfbuzz/src/test-unicode-ranges.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/failing-alloc.c +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/failing-alloc.c +FILE: ../../../third_party/harfbuzz/src/hb-draw.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2020 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-ankr-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-ankr-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-bsln-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-feat-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-just-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.h +FILE: ../../../third_party/harfbuzz/src/hb-aat-ltag-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat.h +FILE: ../../../third_party/harfbuzz/src/hb-ot-color-svg-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-gasp-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-metrics.h +FILE: ../../../third_party/harfbuzz/src/hb-ot-metrics.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-stat-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-common.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-common.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-morx-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.hh +FILE: ../../../third_party/harfbuzz/src/hb-debug.hh +FILE: ../../../third_party/harfbuzz/src/hb-kern.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-kern-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-post-macroman.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-var-avar-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-var-fvar-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-var-hvar-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-var-mvar-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-var.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-var.h +FILE: ../../../third_party/harfbuzz/src/hb-string-array.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2017 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-kerx-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-kerx-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-trak-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Ebrahim Byagowi +Copyright © 2018 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-opbd-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-opbd-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-gdi.cc +FILE: ../../../third_party/harfbuzz/src/hb-gdi.h +FILE: ../../../third_party/harfbuzz/src/hb-number-parser.hh +FILE: ../../../third_party/harfbuzz/src/hb-number-parser.rl +FILE: ../../../third_party/harfbuzz/src/hb-number.cc +FILE: ../../../third_party/harfbuzz/src/hb-number.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-meta-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-meta.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-meta.h +FILE: ../../../third_party/harfbuzz/src/hb-style.cc +FILE: ../../../third_party/harfbuzz/src/hb-style.h +FILE: ../../../third_party/harfbuzz/src/test-number.cc +FILE: ../../../third_party/harfbuzz/src/test-ot-meta.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2019 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2017 Google, Inc. +Copyright © 2018 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-map.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-aat-map.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-map.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009,2010 Red Hat, Inc. +Copyright © 2010,2011,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-algs.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-algs.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2017 Google, Inc. +Copyright © 2019 Facebook, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-atomic.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-atomic.hh +FILE: ../../../third_party/harfbuzz/src/hb-mutex.hh +FILE: ../../../third_party/harfbuzz/src/hb-object.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007 Chris Wilson +Copyright © 2009,2010 Red Hat, Inc. +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-bimap.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-bimap.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2019 Adobe Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-blob.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-blob.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2018 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-blob.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-blob.h +FILE: ../../../third_party/harfbuzz/src/hb-face.h +FILE: ../../../third_party/harfbuzz/src/hb-font.h +FILE: ../../../third_party/harfbuzz/src/hb-ot.h +FILE: ../../../third_party/harfbuzz/src/hb.h +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-blob.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-blob.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2018 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-json.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-json.hh +FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-json.rl +FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-text.hh +FILE: ../../../third_party/harfbuzz/src/hb-buffer-deserialize-text.rl +FILE: ../../../third_party/harfbuzz/src/hb-deprecated.h +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-jstf-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-hangul.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer-serialize.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-buffer-serialize.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-buffer.cc +FILE: ../../../third_party/harfbuzz/src/hb-buffer.hh +---------------------------------------------------------------------------------------------------- +Copyright © 1998-2004 David Turner and Werner Lemberg +Copyright © 2004,2007,2009,2010 Red Hat, Inc. +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-buffer.h +---------------------------------------------------------------------------------------------------- +Copyright © 1998-2004 David Turner and Werner Lemberg +Copyright © 2004,2007,2009 Red Hat, Inc. +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-cache.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-cache.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize.hh +FILE: ../../../third_party/harfbuzz/src/hb-set-digest.hh +FILE: ../../../third_party/harfbuzz/src/hb-set.cc +FILE: ../../../third_party/harfbuzz/src/hb-set.h +FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.cc +FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.h +FILE: ../../../third_party/harfbuzz/src/hb-shaper-impl.hh +FILE: ../../../third_party/harfbuzz/src/hb-shaper-list.hh +FILE: ../../../third_party/harfbuzz/src/hb-shaper.cc +FILE: ../../../third_party/harfbuzz/src/hb-shaper.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-cff-interp-common.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-cff-interp-common.hh +FILE: ../../../third_party/harfbuzz/src/hb-cff-interp-cs-common.hh +FILE: ../../../third_party/harfbuzz/src/hb-cff-interp-dict-common.hh +FILE: ../../../third_party/harfbuzz/src/hb-cff1-interp-cs.hh +FILE: ../../../third_party/harfbuzz/src/hb-cff2-interp-cs.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-cff-common.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-cff1-table.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-cff1-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-cff2-table.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-cff2-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-vorg-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-subset-cff-common.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset-cff-common.hh +FILE: ../../../third_party/harfbuzz/src/hb-subset-cff1.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset-cff1.hh +FILE: ../../../third_party/harfbuzz/src/hb-subset-cff2.cc +FILE: ../../../third_party/harfbuzz/src/hb-subset-cff2.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Adobe Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-common.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-common.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009,2010 Red Hat, Inc. +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-common.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-common.h +FILE: ../../../third_party/harfbuzz/src/hb.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-config.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-config.hh +FILE: ../../../third_party/harfbuzz/src/hb-pool.hh +FILE: ../../../third_party/harfbuzz/src/test-algs.cc +FILE: ../../../third_party/harfbuzz/src/test-meta.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2019 Facebook, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-coretext.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-coretext.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2012,2013 Mozilla Foundation. +Copyright © 2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-coretext.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-coretext.h +---------------------------------------------------------------------------------------------------- +Copyright © 2012 Mozilla Foundation. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-directwrite.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-directwrite.cc +FILE: ../../../third_party/harfbuzz/src/hb-directwrite.h +---------------------------------------------------------------------------------------------------- +Copyright © 2015-2019 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-dispatch.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-dispatch.hh +FILE: ../../../third_party/harfbuzz/src/hb-machinery.hh +FILE: ../../../third_party/harfbuzz/src/hb-sanitize.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009,2010 Red Hat, Inc. +Copyright © 2012,2018 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-draw.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-draw.cc +FILE: ../../../third_party/harfbuzz/src/hb-draw.h +---------------------------------------------------------------------------------------------------- +Copyright © 2019-2020 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-face.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-face.cc +FILE: ../../../third_party/harfbuzz/src/hb-font.cc +FILE: ../../../third_party/harfbuzz/src/hb-shape.cc +FILE: ../../../third_party/harfbuzz/src/hb-shape.h +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-face.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-face.hh +FILE: ../../../third_party/harfbuzz/src/hb-font.hh +FILE: ../../../third_party/harfbuzz/src/hb-glib.cc +FILE: ../../../third_party/harfbuzz/src/hb-glib.h +FILE: ../../../third_party/harfbuzz/src/hb-icu.h +FILE: ../../../third_party/harfbuzz/src/hb-ot-tag.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2011 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-fallback-shape.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-fallback-shape.cc +FILE: ../../../third_party/harfbuzz/src/hb-gobject-structs.cc +FILE: ../../../third_party/harfbuzz/src/hb-uniscribe.h +FILE: ../../../third_party/harfbuzz/src/hb-version.h +FILE: ../../../third_party/harfbuzz/src/hb-version.h.in +---------------------------------------------------------------------------------------------------- +Copyright © 2011 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ft.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ft.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2009 Keith Stribley +Copyright © 2015 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ft.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ft.h +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2015 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-gobject-enums.cc.tmpl +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-gobject-enums.cc.tmpl +FILE: ../../../third_party/harfbuzz/src/hb-gobject-structs.h +FILE: ../../../third_party/harfbuzz/src/hb-gobject.h +---------------------------------------------------------------------------------------------------- +Copyright (C) 2011 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-gobject-enums.h.tmpl +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-gobject-enums.h.tmpl +---------------------------------------------------------------------------------------------------- +Copyright (C) 2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-graphite2.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-graphite2.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2011 Martin Hosken +Copyright © 2011 SIL International +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-graphite2.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-graphite2.h +---------------------------------------------------------------------------------------------------- +Copyright © 2011 Martin Hosken +Copyright © 2011 SIL International + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-icu.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-icu.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2009 Keith Stribley +Copyright © 2011 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-iter.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-iter.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Google, Inc. +Copyright © 2019 Facebook, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-open-file.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-open-file.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-open-type.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-open-type.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009,2010 Red Hat, Inc. +Copyright © 2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-cff1-std-str.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-cff1-std-str.hh +FILE: ../../../third_party/harfbuzz/src/test-bimap.cc +FILE: ../../../third_party/harfbuzz/src/test-ot-glyphname.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2019 Adobe, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-cmap-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-cmap-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-font.h +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2014 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color-cbdt-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-color-cbdt-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-post-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2016 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color-colr-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-color-colr-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-color-sbix-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Ebrahim Byagowi +Copyright © 2020 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color-cpal-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-color-cpal-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-color.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2016 Google, Inc. +Copyright © 2018 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-color.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-color.h +---------------------------------------------------------------------------------------------------- +Copyright © 2016 Google, Inc. +Copyright © 2018 Khaled Hosny +Copyright © 2018 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-face-table-list.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-face-table-list.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2012,2013 Google, Inc. +Copyright © 2019, Facebook Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-face.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-face.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-font.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-font.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2011,2014 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-glyf-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-glyf-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2015 Google, Inc. +Copyright © 2019 Adobe Inc. +Copyright © 2019 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-head-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-head-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2010 Red Hat, Inc. +Copyright © 2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-hhea-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-hhea-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-hmtx-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-maxp-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-name-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer-machine.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer-machine.rl +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-base-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-base-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2016 Elie Roux +Copyright © 2018 Google, Inc. +Copyright © 2018-2019 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-common.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-common.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2010,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gdef-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gdef-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2010,2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gpos-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gpos-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gsub-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009,2010 Red Hat, Inc. +Copyright © 2010,2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009,2010 Red Hat, Inc. +Copyright © 2010,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout.cc +---------------------------------------------------------------------------------------------------- +Copyright © 1998-2004 David Turner and Werner Lemberg +Copyright © 2006 Behdad Esfahbod +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout.h +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-map.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-map.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2009,2010 Red Hat, Inc. +Copyright © 2010,2011,2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-math-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-math-table.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-math.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-math.h +---------------------------------------------------------------------------------------------------- +Copyright © 2016 Igalia S.L. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-metrics.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-metrics.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2018-2019 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-name.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-name.h +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Ebrahim Byagowi. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-os2-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-os2-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2011,2012 Google, Inc. +Copyright © 2018 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-default.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-hebrew.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-thai.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2010,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use-machine.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use-machine.rl +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-use.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2015 Mozilla Foundation. +Copyright © 2015 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar.cc +FILE: ../../../third_party/harfbuzz/src/hb-uniscribe.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2011,2012,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2010,2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2009,2010 Red Hat, Inc. +Copyright © 2010,2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.h +---------------------------------------------------------------------------------------------------- +Copyright © 2013 Red Hat, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2010 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-var-gvar-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-var-gvar-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2019 Adobe Inc. +Copyright © 2019 Ebrahim Byagowi + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-serialize.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-serialize.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009,2010 Red Hat, Inc. +Copyright © 2012,2018 Google, Inc. +Copyright © 2019 Facebook, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-set.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-set.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2012,2017 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-shape-plan.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2012,2018 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-ucd.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ucd.cc +---------------------------------------------------------------------------------------------------- +Copyright (C) 2012 Grigori Goronzy + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-unicode.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-unicode.cc +FILE: ../../../third_party/harfbuzz/src/hb-unicode.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2011 Codethink Limited +Copyright © 2010,2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-unicode.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-unicode.h +---------------------------------------------------------------------------------------------------- +Copyright © 2009 Red Hat, Inc. +Copyright © 2011 Codethink Limited +Copyright © 2011,2012 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-utf.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-utf.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2011,2012,2014 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/hb-vector.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-vector.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2017,2018 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/main.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/main.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2007,2008,2009 Red Hat, Inc. +Copyright © 2018,2019,2020 Ebrahim Byagowi +Copyright © 2018 Khaled Hosny + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/test-array.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/test-array.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2020 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2010,2011,2013 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: harfbuzz +ORIGIN: ../../../third_party/harfbuzz/src/test-gpos-size-params.cc +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/test-gpos-size-params.cc +FILE: ../../../third_party/harfbuzz/src/test-gsub-would-substitute.cc +FILE: ../../../third_party/harfbuzz/src/test.cc +---------------------------------------------------------------------------------------------------- +Copyright © 2010,2011 Google, Inc. + + This is part of HarfBuzz, a text shaping library. + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/dart/runtime/third_party/double-conversion/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright 2006-2011, the V8 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: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999 Computer Systems and Communication Lab, + Institute of Information Science, Academia + * Sinica. 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 the Computer Systems and Communication Lab + 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 +REGENTS 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: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999 TaBE Project. +Copyright (c) 1999 Pai-Hsiang Hsiao. +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 the TaBE Project 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 +REGENTS 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: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013 International Business Machines Corporation +and others. All Rights Reserved. + +Project: https://github.com/veer66/lao-dictionary +Dictionary: https://github.com/veer66/lao-dictionary/blob/master/Lao-Dictionary.txt +License: https://github.com/veer66/lao-dictionary/blob/master/Lao-Dictionary-LICENSE.txt + (copied below) + + This file is derived from the above dictionary, with slight + modifications. + + Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + 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. + +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 HOLDER 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: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright (c) 2014 International Business Machines Corporation +and others. All Rights Reserved. + +This list is part of a project hosted at: + github.com/kanyawtech/myanmar-karen-word-lists + +Copyright (c) 2013, LeRoy Benjamin Sharon +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 Myanmar Karen Word Lists, 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 HOLDER 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: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +The BSD License +http://opensource.org/licenses/bsd-license.php +Copyright (C) 2006-2008, 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: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.icu +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright (c) 1995-2016 International Business Machines Corporation and others +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + University of Illinois +c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright 2000, 2001, 2002, 2003 Nara Institute of Science +and Technology. All Rights Reserved. + +Use, reproduction, and distribution of this software is permitted. +Any copy of this software, whether in its original form or modified, +must include both the above copyright notice and the following +paragraphs. + +Nara Institute of Science and Technology (NAIST), +the copyright holders, disclaims all warranties with regard to this +software, including all implied warranties of merchantability and +fitness, in no event shall NAIST be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether in an +action of contract, negligence or other tortuous action, arising out +of or in connection with the use or performance of this software. + +A large portion of the dictionary entries +originate from ICOT Free Software. The following conditions for ICOT +Free Software applies to the current dictionary as well. + +Each User may also freely distribute the Program, whether in its +original form or modified, to any third party or parties, PROVIDED +that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear +on, or be attached to, the Program, which is distributed substantially +in the same form as set out herein and that such intended +distribution, if actually made, will neither violate or otherwise +contravene any of the laws and regulations of the countries having +jurisdiction over the User or the intended distribution itself. + +NO WARRANTY + +The program was produced on an experimental basis in the course of the +research and development conducted during the project and is provided +to users as so produced on an experimental basis. Accordingly, the +program is provided without any warranty whatsoever, whether express, +implied, statutory or otherwise. The term "warranty" used herein +includes, but is not limited to, any warranty of the quality, +performance, merchantability and fitness for a particular purpose of +the program and the nonexistence of any infringement or violation of +any right of any third party. + +Each user of the program will agree and understand, and be deemed to +have agreed and understood, that there is no warranty whatsoever for +the program and, accordingly, the entire risk arising from or +otherwise connected with the program is assumed by the user. + +Therefore, neither ICOT, the copyright holder, or any other +organization that participated in or was otherwise related to the +development of the program and their respective officials, directors, +officers and other employees shall be held liable for any and all +damages, including, without limitation, general, special, incidental +and consequential damages, arising out of or otherwise in connection +with the use or inability to use the program or any product, material +or result produced or otherwise obtained by using the program, +regardless of whether they have been advised of, or otherwise had +knowledge of, the possibility of such damages at any time during the +project or thereafter. Each user will be deemed to have agreed to the +foregoing by his or her commencement of use of the program. The term +"use" as used herein includes, but is not limited to, the use, +modification, copying and distribution of the program and the +production of secondary products from the program. + +In the case where the program, whether in its original form or +modified, was distributed or delivered to or received by a user from +any person, organization or entity other than ICOT, unless it makes or +grants independently of ICOT any specific warranty to the user in +writing, such person, organization or entity, will also be exempted +from and not be held liable to the user for any such damages as noted +above as far as the program is concerned. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/LICENSE +TYPE: LicenseType.unknown +FILE: ../../../third_party/icu/DIR_METADATA +FILE: ../../../third_party/icu/android/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl.dat +FILE: ../../../third_party/icu/android_small/icudtl_extra.dat +FILE: ../../../third_party/icu/cast/brkitr.patch +FILE: ../../../third_party/icu/cast/icudtl.dat +FILE: ../../../third_party/icu/chromeos/icudtl.dat +FILE: ../../../third_party/icu/common/icudtb.dat +FILE: ../../../third_party/icu/common/icudtl.dat +FILE: ../../../third_party/icu/flutter/brkitr.patch +FILE: ../../../third_party/icu/flutter/icudtl.dat +FILE: ../../../third_party/icu/fuzzers/fuzzer_utils.h +FILE: ../../../third_party/icu/fuzzers/icu_appendable_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_break_iterator_utf32_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_number_format_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_to_case_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_ucasemap_fuzzer.cc +FILE: ../../../third_party/icu/ios/icudtl.dat +FILE: ../../../third_party/icu/patches/Collator_getKeywordValues.patch +FILE: ../../../third_party/icu/patches/ICUService_getKey.patch +FILE: ../../../third_party/icu/patches/ardatepattern.patch +FILE: ../../../third_party/icu/patches/atomic_template_instantiation.patch +FILE: ../../../third_party/icu/patches/cjdict.patch +FILE: ../../../third_party/icu/patches/configure.patch +FILE: ../../../third_party/icu/patches/data_symb.patch +FILE: ../../../third_party/icu/patches/fuchsia.patch +FILE: ../../../third_party/icu/patches/gb_table.patch +FILE: ../../../third_party/icu/patches/iso2022jp.patch +FILE: ../../../third_party/icu/patches/isvalidenum.patch +FILE: ../../../third_party/icu/patches/khmer-dictbe.patch +FILE: ../../../third_party/icu/patches/locale1.patch +FILE: ../../../third_party/icu/patches/locale_google.patch +FILE: ../../../third_party/icu/patches/locid_operators.patch +FILE: ../../../third_party/icu/patches/restrace.patch +FILE: ../../../third_party/icu/patches/ultag_parse_Null.patch +FILE: ../../../third_party/icu/patches/wordbrk.patch +FILE: ../../../third_party/icu/patches/wpo.patch +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-implicithan.icu +FILE: ../../../third_party/icu/source/data/in/coll/ucadata-unihan.icu +FILE: ../../../third_party/icu/source/data/in/nfc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc.nrm +FILE: ../../../third_party/icu/source/data/in/nfkc_cf.nrm +FILE: ../../../third_party/icu/source/data/in/pnames.icu +FILE: ../../../third_party/icu/source/data/in/ubidi.icu +FILE: ../../../third_party/icu/source/data/in/ucase.icu +FILE: ../../../third_party/icu/source/data/in/ulayout.icu +FILE: ../../../third_party/icu/source/data/in/unames.icu +FILE: ../../../third_party/icu/source/data/in/uprops.icu +FILE: ../../../third_party/icu/source/data/in/uts46.nrm +FILE: ../../../third_party/icu/source/data/mappings/big5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-kr-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/gb18030.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm866-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-10-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-13-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-14-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-15-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-16-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-2-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-3-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-4-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-5-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-6-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-7-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859-8-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-r-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/koi8-u-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/macintosh-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/shift_jis-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1250-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1251-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1252-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1253-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1254-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1255-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1256-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1257-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-1258-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-html.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-936-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/x-mac-cyrillic-html.ucm +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsp +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.dsw +FILE: ../../../third_party/icu/source/tools/tzcode/asctime.c +FILE: ../../../third_party/icu/source/tools/tzcode/ialloc.c +FILE: ../../../third_party/icu/source/tools/tzcode/localtime.c +FILE: ../../../third_party/icu/source/tools/tzcode/private.h +FILE: ../../../third_party/icu/source/tools/tzcode/scheck.c +FILE: ../../../third_party/icu/source/tools/tzcode/tzfile.h +FILE: ../../../third_party/icu/source/tools/tzcode/tzselect.ksh +FILE: ../../../third_party/icu/source/tools/tzcode/zdump.c +FILE: ../../../third_party/icu/source/tools/tzcode/zic.c +FILE: ../../../third_party/icu/tzres/metaZones.res +FILE: ../../../third_party/icu/tzres/timezoneTypes.res +FILE: ../../../third_party/icu/tzres/zoneinfo64.res +---------------------------------------------------------------------------------------------------- +Copyright © 1991-2020 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/filters/android.json + ../../../third_party/dart/runtime/third_party/double-conversion/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/filters/android.json +FILE: ../../../third_party/icu/filters/android_extra.json +FILE: ../../../third_party/icu/filters/android_small.json +FILE: ../../../third_party/icu/filters/cast.json +FILE: ../../../third_party/icu/filters/chromeos.json +FILE: ../../../third_party/icu/filters/common.json +FILE: ../../../third_party/icu/filters/flutter.json +FILE: ../../../third_party/icu/filters/ios.json +---------------------------------------------------------------------------------------------------- +Copyright 2019 the V8 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 LLC 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: icu +ORIGIN: ../../../third_party/icu/fuzzers/icu_converter_fuzzer.cc + ../../../LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/fuzzers/icu_converter_fuzzer.cc +FILE: ../../../third_party/icu/fuzzers/icu_regex.dict +FILE: ../../../third_party/icu/fuzzers/icu_uregex_open_fuzzer.cc +---------------------------------------------------------------------------------------------------- +Copyright 2016 The Chromium 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: icu +ORIGIN: ../../../third_party/icu/scripts/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/fuzzers/icu_unicode_string_codepage_create_fuzzer.cc +FILE: ../../../third_party/icu/scripts/accept_lang.list +FILE: ../../../third_party/icu/scripts/chrome_ui_languages.list +FILE: ../../../third_party/icu/scripts/currencies.list +---------------------------------------------------------------------------------------------------- +Copyright 2015 The Chromium 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: icu +ORIGIN: ../../../third_party/icu/source/common/unicode/appendable.h +TYPE: LicenseType.icu +FILE: ../../../third_party/icu/APIChangeReport.html +FILE: ../../../third_party/icu/source/Doxyfile.in +FILE: ../../../third_party/icu/source/common/appendable.cpp +FILE: ../../../third_party/icu/source/common/bmpset.cpp +FILE: ../../../third_party/icu/source/common/bmpset.h +FILE: ../../../third_party/icu/source/common/brkeng.cpp +FILE: ../../../third_party/icu/source/common/brkeng.h +FILE: ../../../third_party/icu/source/common/brkiter.cpp +FILE: ../../../third_party/icu/source/common/bytesinkutil.cpp +FILE: ../../../third_party/icu/source/common/bytesinkutil.h +FILE: ../../../third_party/icu/source/common/bytestream.cpp +FILE: ../../../third_party/icu/source/common/bytestrie.cpp +FILE: ../../../third_party/icu/source/common/bytestriebuilder.cpp +FILE: ../../../third_party/icu/source/common/bytestrieiterator.cpp +FILE: ../../../third_party/icu/source/common/caniter.cpp +FILE: ../../../third_party/icu/source/common/capi_helper.h +FILE: ../../../third_party/icu/source/common/characterproperties.cpp +FILE: ../../../third_party/icu/source/common/chariter.cpp +FILE: ../../../third_party/icu/source/common/charstr.cpp +FILE: ../../../third_party/icu/source/common/charstr.h +FILE: ../../../third_party/icu/source/common/charstrmap.h +FILE: ../../../third_party/icu/source/common/cmemory.cpp +FILE: ../../../third_party/icu/source/common/cmemory.h +FILE: ../../../third_party/icu/source/common/common.rc +FILE: ../../../third_party/icu/source/common/cpputils.h +FILE: ../../../third_party/icu/source/common/cstr.cpp +FILE: ../../../third_party/icu/source/common/cstr.h +FILE: ../../../third_party/icu/source/common/cstring.cpp +FILE: ../../../third_party/icu/source/common/cstring.h +FILE: ../../../third_party/icu/source/common/cwchar.cpp +FILE: ../../../third_party/icu/source/common/cwchar.h +FILE: ../../../third_party/icu/source/common/dictbe.cpp +FILE: ../../../third_party/icu/source/common/dictbe.h +FILE: ../../../third_party/icu/source/common/dictionarydata.cpp +FILE: ../../../third_party/icu/source/common/dictionarydata.h +FILE: ../../../third_party/icu/source/common/dtintrv.cpp +FILE: ../../../third_party/icu/source/common/edits.cpp +FILE: ../../../third_party/icu/source/common/errorcode.cpp +FILE: ../../../third_party/icu/source/common/filteredbrk.cpp +FILE: ../../../third_party/icu/source/common/filterednormalizer2.cpp +FILE: ../../../third_party/icu/source/common/hash.h +FILE: ../../../third_party/icu/source/common/icudataver.cpp +FILE: ../../../third_party/icu/source/common/icuplug.cpp +FILE: ../../../third_party/icu/source/common/icuplugimp.h +FILE: ../../../third_party/icu/source/common/loadednormalizer2impl.cpp +FILE: ../../../third_party/icu/source/common/localebuilder.cpp +FILE: ../../../third_party/icu/source/common/localematcher.cpp +FILE: ../../../third_party/icu/source/common/localeprioritylist.cpp +FILE: ../../../third_party/icu/source/common/localeprioritylist.h +FILE: ../../../third_party/icu/source/common/localsvc.h +FILE: ../../../third_party/icu/source/common/locavailable.cpp +FILE: ../../../third_party/icu/source/common/locbased.cpp +FILE: ../../../third_party/icu/source/common/locbased.h +FILE: ../../../third_party/icu/source/common/locdispnames.cpp +FILE: ../../../third_party/icu/source/common/locdistance.cpp +FILE: ../../../third_party/icu/source/common/locdistance.h +FILE: ../../../third_party/icu/source/common/locdspnm.cpp +FILE: ../../../third_party/icu/source/common/locid.cpp +FILE: ../../../third_party/icu/source/common/loclikely.cpp +FILE: ../../../third_party/icu/source/common/loclikelysubtags.cpp +FILE: ../../../third_party/icu/source/common/loclikelysubtags.h +FILE: ../../../third_party/icu/source/common/locmap.cpp +FILE: ../../../third_party/icu/source/common/locmap.h +FILE: ../../../third_party/icu/source/common/locresdata.cpp +FILE: ../../../third_party/icu/source/common/locutil.cpp +FILE: ../../../third_party/icu/source/common/locutil.h +FILE: ../../../third_party/icu/source/common/lsr.cpp +FILE: ../../../third_party/icu/source/common/lsr.h +FILE: ../../../third_party/icu/source/common/messageimpl.h +FILE: ../../../third_party/icu/source/common/messagepattern.cpp +FILE: ../../../third_party/icu/source/common/msvcres.h +FILE: ../../../third_party/icu/source/common/mutex.h +FILE: ../../../third_party/icu/source/common/norm2_nfc_data.h +FILE: ../../../third_party/icu/source/common/norm2allmodes.h +FILE: ../../../third_party/icu/source/common/normalizer2.cpp +FILE: ../../../third_party/icu/source/common/normalizer2impl.cpp +FILE: ../../../third_party/icu/source/common/normalizer2impl.h +FILE: ../../../third_party/icu/source/common/normlzr.cpp +FILE: ../../../third_party/icu/source/common/parsepos.cpp +FILE: ../../../third_party/icu/source/common/patternprops.cpp +FILE: ../../../third_party/icu/source/common/patternprops.h +FILE: ../../../third_party/icu/source/common/pluralmap.cpp +FILE: ../../../third_party/icu/source/common/pluralmap.h +FILE: ../../../third_party/icu/source/common/propname.cpp +FILE: ../../../third_party/icu/source/common/propname.h +FILE: ../../../third_party/icu/source/common/propname_data.h +FILE: ../../../third_party/icu/source/common/propsvec.cpp +FILE: ../../../third_party/icu/source/common/propsvec.h +FILE: ../../../third_party/icu/source/common/punycode.cpp +FILE: ../../../third_party/icu/source/common/punycode.h +FILE: ../../../third_party/icu/source/common/putil.cpp +FILE: ../../../third_party/icu/source/common/putilimp.h +FILE: ../../../third_party/icu/source/common/rbbi.cpp +FILE: ../../../third_party/icu/source/common/rbbi_cache.cpp +FILE: ../../../third_party/icu/source/common/rbbi_cache.h +FILE: ../../../third_party/icu/source/common/rbbidata.cpp +FILE: ../../../third_party/icu/source/common/rbbidata.h +FILE: ../../../third_party/icu/source/common/rbbinode.cpp +FILE: ../../../third_party/icu/source/common/rbbinode.h +FILE: ../../../third_party/icu/source/common/rbbirb.cpp +FILE: ../../../third_party/icu/source/common/rbbirb.h +FILE: ../../../third_party/icu/source/common/rbbirpt.h +FILE: ../../../third_party/icu/source/common/rbbiscan.cpp +FILE: ../../../third_party/icu/source/common/rbbiscan.h +FILE: ../../../third_party/icu/source/common/rbbisetb.cpp +FILE: ../../../third_party/icu/source/common/rbbisetb.h +FILE: ../../../third_party/icu/source/common/rbbistbl.cpp +FILE: ../../../third_party/icu/source/common/rbbitblb.cpp +FILE: ../../../third_party/icu/source/common/rbbitblb.h +FILE: ../../../third_party/icu/source/common/resbund.cpp +FILE: ../../../third_party/icu/source/common/resbund_cnv.cpp +FILE: ../../../third_party/icu/source/common/resource.cpp +FILE: ../../../third_party/icu/source/common/resource.h +FILE: ../../../third_party/icu/source/common/restrace.cpp +FILE: ../../../third_party/icu/source/common/restrace.h +FILE: ../../../third_party/icu/source/common/ruleiter.cpp +FILE: ../../../third_party/icu/source/common/ruleiter.h +FILE: ../../../third_party/icu/source/common/schriter.cpp +FILE: ../../../third_party/icu/source/common/serv.cpp +FILE: ../../../third_party/icu/source/common/serv.h +FILE: ../../../third_party/icu/source/common/servlk.cpp +FILE: ../../../third_party/icu/source/common/servlkf.cpp +FILE: ../../../third_party/icu/source/common/servloc.h +FILE: ../../../third_party/icu/source/common/servls.cpp +FILE: ../../../third_party/icu/source/common/servnotf.cpp +FILE: ../../../third_party/icu/source/common/servnotf.h +FILE: ../../../third_party/icu/source/common/servrbf.cpp +FILE: ../../../third_party/icu/source/common/servslkf.cpp +FILE: ../../../third_party/icu/source/common/sharedobject.cpp +FILE: ../../../third_party/icu/source/common/sharedobject.h +FILE: ../../../third_party/icu/source/common/simpleformatter.cpp +FILE: ../../../third_party/icu/source/common/sprpimpl.h +FILE: ../../../third_party/icu/source/common/static_unicode_sets.cpp +FILE: ../../../third_party/icu/source/common/static_unicode_sets.h +FILE: ../../../third_party/icu/source/common/stringpiece.cpp +FILE: ../../../third_party/icu/source/common/stringtriebuilder.cpp +FILE: ../../../third_party/icu/source/common/uarrsort.cpp +FILE: ../../../third_party/icu/source/common/uarrsort.h +FILE: ../../../third_party/icu/source/common/uassert.h +FILE: ../../../third_party/icu/source/common/ubidi.cpp +FILE: ../../../third_party/icu/source/common/ubidi_props.cpp +FILE: ../../../third_party/icu/source/common/ubidi_props.h +FILE: ../../../third_party/icu/source/common/ubidi_props_data.h +FILE: ../../../third_party/icu/source/common/ubidiimp.h +FILE: ../../../third_party/icu/source/common/ubidiln.cpp +FILE: ../../../third_party/icu/source/common/ubiditransform.cpp +FILE: ../../../third_party/icu/source/common/ubidiwrt.cpp +FILE: ../../../third_party/icu/source/common/ubrk.cpp +FILE: ../../../third_party/icu/source/common/ubrkimpl.h +FILE: ../../../third_party/icu/source/common/ucase.cpp +FILE: ../../../third_party/icu/source/common/ucase.h +FILE: ../../../third_party/icu/source/common/ucase_props_data.h +FILE: ../../../third_party/icu/source/common/ucasemap.cpp +FILE: ../../../third_party/icu/source/common/ucasemap_imp.h +FILE: ../../../third_party/icu/source/common/ucasemap_titlecase_brkiter.cpp +FILE: ../../../third_party/icu/source/common/ucat.cpp +FILE: ../../../third_party/icu/source/common/uchar.cpp +FILE: ../../../third_party/icu/source/common/uchar_props_data.h +FILE: ../../../third_party/icu/source/common/ucharstrie.cpp +FILE: ../../../third_party/icu/source/common/ucharstriebuilder.cpp +FILE: ../../../third_party/icu/source/common/ucharstrieiterator.cpp +FILE: ../../../third_party/icu/source/common/uchriter.cpp +FILE: ../../../third_party/icu/source/common/ucln.h +FILE: ../../../third_party/icu/source/common/ucln_cmn.cpp +FILE: ../../../third_party/icu/source/common/ucln_cmn.h +FILE: ../../../third_party/icu/source/common/ucln_imp.h +FILE: ../../../third_party/icu/source/common/ucmndata.cpp +FILE: ../../../third_party/icu/source/common/ucmndata.h +FILE: ../../../third_party/icu/source/common/ucnv.cpp +FILE: ../../../third_party/icu/source/common/ucnv2022.cpp +FILE: ../../../third_party/icu/source/common/ucnv_bld.cpp +FILE: ../../../third_party/icu/source/common/ucnv_bld.h +FILE: ../../../third_party/icu/source/common/ucnv_cb.cpp +FILE: ../../../third_party/icu/source/common/ucnv_cnv.cpp +FILE: ../../../third_party/icu/source/common/ucnv_cnv.h +FILE: ../../../third_party/icu/source/common/ucnv_ct.cpp +FILE: ../../../third_party/icu/source/common/ucnv_err.cpp +FILE: ../../../third_party/icu/source/common/ucnv_ext.cpp +FILE: ../../../third_party/icu/source/common/ucnv_ext.h +FILE: ../../../third_party/icu/source/common/ucnv_imp.h +FILE: ../../../third_party/icu/source/common/ucnv_io.cpp +FILE: ../../../third_party/icu/source/common/ucnv_io.h +FILE: ../../../third_party/icu/source/common/ucnv_lmb.cpp +FILE: ../../../third_party/icu/source/common/ucnv_set.cpp +FILE: ../../../third_party/icu/source/common/ucnv_u16.cpp +FILE: ../../../third_party/icu/source/common/ucnv_u32.cpp +FILE: ../../../third_party/icu/source/common/ucnv_u7.cpp +FILE: ../../../third_party/icu/source/common/ucnv_u8.cpp +FILE: ../../../third_party/icu/source/common/ucnvbocu.cpp +FILE: ../../../third_party/icu/source/common/ucnvdisp.cpp +FILE: ../../../third_party/icu/source/common/ucnvhz.cpp +FILE: ../../../third_party/icu/source/common/ucnvisci.cpp +FILE: ../../../third_party/icu/source/common/ucnvlat1.cpp +FILE: ../../../third_party/icu/source/common/ucnvmbcs.cpp +FILE: ../../../third_party/icu/source/common/ucnvmbcs.h +FILE: ../../../third_party/icu/source/common/ucnvscsu.cpp +FILE: ../../../third_party/icu/source/common/ucnvsel.cpp +FILE: ../../../third_party/icu/source/common/ucol_data.h +FILE: ../../../third_party/icu/source/common/ucol_swp.cpp +FILE: ../../../third_party/icu/source/common/ucol_swp.h +FILE: ../../../third_party/icu/source/common/ucptrie.cpp +FILE: ../../../third_party/icu/source/common/ucptrie_impl.h +FILE: ../../../third_party/icu/source/common/ucurr.cpp +FILE: ../../../third_party/icu/source/common/ucurrimp.h +FILE: ../../../third_party/icu/source/common/udata.cpp +FILE: ../../../third_party/icu/source/common/udatamem.cpp +FILE: ../../../third_party/icu/source/common/udatamem.h +FILE: ../../../third_party/icu/source/common/udataswp.cpp +FILE: ../../../third_party/icu/source/common/udataswp.h +FILE: ../../../third_party/icu/source/common/uelement.h +FILE: ../../../third_party/icu/source/common/uenum.cpp +FILE: ../../../third_party/icu/source/common/uenumimp.h +FILE: ../../../third_party/icu/source/common/uhash.cpp +FILE: ../../../third_party/icu/source/common/uhash.h +FILE: ../../../third_party/icu/source/common/uhash_us.cpp +FILE: ../../../third_party/icu/source/common/uidna.cpp +FILE: ../../../third_party/icu/source/common/uinit.cpp +FILE: ../../../third_party/icu/source/common/uinvchar.cpp +FILE: ../../../third_party/icu/source/common/uinvchar.h +FILE: ../../../third_party/icu/source/common/uiter.cpp +FILE: ../../../third_party/icu/source/common/ulayout_props.h +FILE: ../../../third_party/icu/source/common/ulist.cpp +FILE: ../../../third_party/icu/source/common/ulist.h +FILE: ../../../third_party/icu/source/common/uloc.cpp +FILE: ../../../third_party/icu/source/common/uloc_keytype.cpp +FILE: ../../../third_party/icu/source/common/uloc_tag.cpp +FILE: ../../../third_party/icu/source/common/ulocimp.h +FILE: ../../../third_party/icu/source/common/umapfile.cpp +FILE: ../../../third_party/icu/source/common/umapfile.h +FILE: ../../../third_party/icu/source/common/umath.cpp +FILE: ../../../third_party/icu/source/common/umutablecptrie.cpp +FILE: ../../../third_party/icu/source/common/umutex.cpp +FILE: ../../../third_party/icu/source/common/umutex.h +FILE: ../../../third_party/icu/source/common/unames.cpp +FILE: ../../../third_party/icu/source/common/unicode/appendable.h +FILE: ../../../third_party/icu/source/common/unicode/brkiter.h +FILE: ../../../third_party/icu/source/common/unicode/bytestream.h +FILE: ../../../third_party/icu/source/common/unicode/bytestrie.h +FILE: ../../../third_party/icu/source/common/unicode/bytestriebuilder.h +FILE: ../../../third_party/icu/source/common/unicode/caniter.h +FILE: ../../../third_party/icu/source/common/unicode/casemap.h +FILE: ../../../third_party/icu/source/common/unicode/char16ptr.h +FILE: ../../../third_party/icu/source/common/unicode/chariter.h +FILE: ../../../third_party/icu/source/common/unicode/dbbi.h +FILE: ../../../third_party/icu/source/common/unicode/docmain.h +FILE: ../../../third_party/icu/source/common/unicode/dtintrv.h +FILE: ../../../third_party/icu/source/common/unicode/edits.h +FILE: ../../../third_party/icu/source/common/unicode/enumset.h +FILE: ../../../third_party/icu/source/common/unicode/errorcode.h +FILE: ../../../third_party/icu/source/common/unicode/filteredbrk.h +FILE: ../../../third_party/icu/source/common/unicode/icudataver.h +FILE: ../../../third_party/icu/source/common/unicode/icuplug.h +FILE: ../../../third_party/icu/source/common/unicode/idna.h +FILE: ../../../third_party/icu/source/common/unicode/localebuilder.h +FILE: ../../../third_party/icu/source/common/unicode/localematcher.h +FILE: ../../../third_party/icu/source/common/unicode/localpointer.h +FILE: ../../../third_party/icu/source/common/unicode/locdspnm.h +FILE: ../../../third_party/icu/source/common/unicode/locid.h +FILE: ../../../third_party/icu/source/common/unicode/messagepattern.h +FILE: ../../../third_party/icu/source/common/unicode/normalizer2.h +FILE: ../../../third_party/icu/source/common/unicode/normlzr.h +FILE: ../../../third_party/icu/source/common/unicode/parseerr.h +FILE: ../../../third_party/icu/source/common/unicode/parsepos.h +FILE: ../../../third_party/icu/source/common/unicode/platform.h +FILE: ../../../third_party/icu/source/common/unicode/ptypes.h +FILE: ../../../third_party/icu/source/common/unicode/putil.h +FILE: ../../../third_party/icu/source/common/unicode/rbbi.h +FILE: ../../../third_party/icu/source/common/unicode/rep.h +FILE: ../../../third_party/icu/source/common/unicode/resbund.h +FILE: ../../../third_party/icu/source/common/unicode/schriter.h +FILE: ../../../third_party/icu/source/common/unicode/simpleformatter.h +FILE: ../../../third_party/icu/source/common/unicode/std_string.h +FILE: ../../../third_party/icu/source/common/unicode/strenum.h +FILE: ../../../third_party/icu/source/common/unicode/stringoptions.h +FILE: ../../../third_party/icu/source/common/unicode/stringpiece.h +FILE: ../../../third_party/icu/source/common/unicode/stringtriebuilder.h +FILE: ../../../third_party/icu/source/common/unicode/symtable.h +FILE: ../../../third_party/icu/source/common/unicode/ubidi.h +FILE: ../../../third_party/icu/source/common/unicode/ubiditransform.h +FILE: ../../../third_party/icu/source/common/unicode/ubrk.h +FILE: ../../../third_party/icu/source/common/unicode/ucasemap.h +FILE: ../../../third_party/icu/source/common/unicode/ucat.h +FILE: ../../../third_party/icu/source/common/unicode/uchar.h +FILE: ../../../third_party/icu/source/common/unicode/ucharstrie.h +FILE: ../../../third_party/icu/source/common/unicode/ucharstriebuilder.h +FILE: ../../../third_party/icu/source/common/unicode/uchriter.h +FILE: ../../../third_party/icu/source/common/unicode/uclean.h +FILE: ../../../third_party/icu/source/common/unicode/ucnv.h +FILE: ../../../third_party/icu/source/common/unicode/ucnv_cb.h +FILE: ../../../third_party/icu/source/common/unicode/ucnv_err.h +FILE: ../../../third_party/icu/source/common/unicode/ucnvsel.h +FILE: ../../../third_party/icu/source/common/unicode/uconfig.h +FILE: ../../../third_party/icu/source/common/unicode/ucpmap.h +FILE: ../../../third_party/icu/source/common/unicode/ucptrie.h +FILE: ../../../third_party/icu/source/common/unicode/ucurr.h +FILE: ../../../third_party/icu/source/common/unicode/udata.h +FILE: ../../../third_party/icu/source/common/unicode/udisplaycontext.h +FILE: ../../../third_party/icu/source/common/unicode/uenum.h +FILE: ../../../third_party/icu/source/common/unicode/uidna.h +FILE: ../../../third_party/icu/source/common/unicode/uiter.h +FILE: ../../../third_party/icu/source/common/unicode/uldnames.h +FILE: ../../../third_party/icu/source/common/unicode/uloc.h +FILE: ../../../third_party/icu/source/common/unicode/umachine.h +FILE: ../../../third_party/icu/source/common/unicode/umisc.h +FILE: ../../../third_party/icu/source/common/unicode/umutablecptrie.h +FILE: ../../../third_party/icu/source/common/unicode/unifilt.h +FILE: ../../../third_party/icu/source/common/unicode/unifunct.h +FILE: ../../../third_party/icu/source/common/unicode/unimatch.h +FILE: ../../../third_party/icu/source/common/unicode/uniset.h +FILE: ../../../third_party/icu/source/common/unicode/unistr.h +FILE: ../../../third_party/icu/source/common/unicode/unorm.h +FILE: ../../../third_party/icu/source/common/unicode/unorm2.h +FILE: ../../../third_party/icu/source/common/unicode/uobject.h +FILE: ../../../third_party/icu/source/common/unicode/urename.h +FILE: ../../../third_party/icu/source/common/unicode/urep.h +FILE: ../../../third_party/icu/source/common/unicode/ures.h +FILE: ../../../third_party/icu/source/common/unicode/uscript.h +FILE: ../../../third_party/icu/source/common/unicode/uset.h +FILE: ../../../third_party/icu/source/common/unicode/usetiter.h +FILE: ../../../third_party/icu/source/common/unicode/ushape.h +FILE: ../../../third_party/icu/source/common/unicode/usprep.h +FILE: ../../../third_party/icu/source/common/unicode/ustring.h +FILE: ../../../third_party/icu/source/common/unicode/ustringtrie.h +FILE: ../../../third_party/icu/source/common/unicode/utext.h +FILE: ../../../third_party/icu/source/common/unicode/utf.h +FILE: ../../../third_party/icu/source/common/unicode/utf16.h +FILE: ../../../third_party/icu/source/common/unicode/utf32.h +FILE: ../../../third_party/icu/source/common/unicode/utf8.h +FILE: ../../../third_party/icu/source/common/unicode/utf_old.h +FILE: ../../../third_party/icu/source/common/unicode/utrace.h +FILE: ../../../third_party/icu/source/common/unicode/utypes.h +FILE: ../../../third_party/icu/source/common/unicode/uvernum.h +FILE: ../../../third_party/icu/source/common/unicode/uversion.h +FILE: ../../../third_party/icu/source/common/unifiedcache.cpp +FILE: ../../../third_party/icu/source/common/unifiedcache.h +FILE: ../../../third_party/icu/source/common/unifilt.cpp +FILE: ../../../third_party/icu/source/common/unifunct.cpp +FILE: ../../../third_party/icu/source/common/uniquecharstr.h +FILE: ../../../third_party/icu/source/common/uniset.cpp +FILE: ../../../third_party/icu/source/common/uniset_closure.cpp +FILE: ../../../third_party/icu/source/common/uniset_props.cpp +FILE: ../../../third_party/icu/source/common/unisetspan.cpp +FILE: ../../../third_party/icu/source/common/unisetspan.h +FILE: ../../../third_party/icu/source/common/unistr.cpp +FILE: ../../../third_party/icu/source/common/unistr_case.cpp +FILE: ../../../third_party/icu/source/common/unistr_case_locale.cpp +FILE: ../../../third_party/icu/source/common/unistr_cnv.cpp +FILE: ../../../third_party/icu/source/common/unistr_props.cpp +FILE: ../../../third_party/icu/source/common/unistr_titlecase_brkiter.cpp +FILE: ../../../third_party/icu/source/common/unistrappender.h +FILE: ../../../third_party/icu/source/common/unorm.cpp +FILE: ../../../third_party/icu/source/common/unormcmp.cpp +FILE: ../../../third_party/icu/source/common/unormimp.h +FILE: ../../../third_party/icu/source/common/uobject.cpp +FILE: ../../../third_party/icu/source/common/uposixdefs.h +FILE: ../../../third_party/icu/source/common/uprops.cpp +FILE: ../../../third_party/icu/source/common/uprops.h +FILE: ../../../third_party/icu/source/common/ures_cnv.cpp +FILE: ../../../third_party/icu/source/common/uresbund.cpp +FILE: ../../../third_party/icu/source/common/uresdata.cpp +FILE: ../../../third_party/icu/source/common/uresdata.h +FILE: ../../../third_party/icu/source/common/uresimp.h +FILE: ../../../third_party/icu/source/common/ureslocs.h +FILE: ../../../third_party/icu/source/common/usc_impl.cpp +FILE: ../../../third_party/icu/source/common/usc_impl.h +FILE: ../../../third_party/icu/source/common/uscript.cpp +FILE: ../../../third_party/icu/source/common/uscript_props.cpp +FILE: ../../../third_party/icu/source/common/uset.cpp +FILE: ../../../third_party/icu/source/common/uset_imp.h +FILE: ../../../third_party/icu/source/common/uset_props.cpp +FILE: ../../../third_party/icu/source/common/usetiter.cpp +FILE: ../../../third_party/icu/source/common/ushape.cpp +FILE: ../../../third_party/icu/source/common/usprep.cpp +FILE: ../../../third_party/icu/source/common/ustack.cpp +FILE: ../../../third_party/icu/source/common/ustr_cnv.cpp +FILE: ../../../third_party/icu/source/common/ustr_cnv.h +FILE: ../../../third_party/icu/source/common/ustr_imp.h +FILE: ../../../third_party/icu/source/common/ustr_titlecase_brkiter.cpp +FILE: ../../../third_party/icu/source/common/ustr_wcs.cpp +FILE: ../../../third_party/icu/source/common/ustrcase.cpp +FILE: ../../../third_party/icu/source/common/ustrcase_locale.cpp +FILE: ../../../third_party/icu/source/common/ustrenum.cpp +FILE: ../../../third_party/icu/source/common/ustrenum.h +FILE: ../../../third_party/icu/source/common/ustrfmt.cpp +FILE: ../../../third_party/icu/source/common/ustrfmt.h +FILE: ../../../third_party/icu/source/common/ustring.cpp +FILE: ../../../third_party/icu/source/common/ustrtrns.cpp +FILE: ../../../third_party/icu/source/common/utext.cpp +FILE: ../../../third_party/icu/source/common/utf_impl.cpp +FILE: ../../../third_party/icu/source/common/util.cpp +FILE: ../../../third_party/icu/source/common/util.h +FILE: ../../../third_party/icu/source/common/util_props.cpp +FILE: ../../../third_party/icu/source/common/utrace.cpp +FILE: ../../../third_party/icu/source/common/utracimp.h +FILE: ../../../third_party/icu/source/common/utrie.cpp +FILE: ../../../third_party/icu/source/common/utrie.h +FILE: ../../../third_party/icu/source/common/utrie2.cpp +FILE: ../../../third_party/icu/source/common/utrie2.h +FILE: ../../../third_party/icu/source/common/utrie2_builder.cpp +FILE: ../../../third_party/icu/source/common/utrie2_impl.h +FILE: ../../../third_party/icu/source/common/utrie_swap.cpp +FILE: ../../../third_party/icu/source/common/uts46.cpp +FILE: ../../../third_party/icu/source/common/utypeinfo.h +FILE: ../../../third_party/icu/source/common/utypes.cpp +FILE: ../../../third_party/icu/source/common/uvector.cpp +FILE: ../../../third_party/icu/source/common/uvector.h +FILE: ../../../third_party/icu/source/common/uvectr32.cpp +FILE: ../../../third_party/icu/source/common/uvectr32.h +FILE: ../../../third_party/icu/source/common/uvectr64.cpp +FILE: ../../../third_party/icu/source/common/uvectr64.h +FILE: ../../../third_party/icu/source/common/wintz.cpp +FILE: ../../../third_party/icu/source/common/wintz.h +FILE: ../../../third_party/icu/source/config/icu-config-bottom +FILE: ../../../third_party/icu/source/config/icu-config.1.in +FILE: ../../../third_party/icu/source/config/icu.pc.in +FILE: ../../../third_party/icu/source/config/make2sh.sed +FILE: ../../../third_party/icu/source/config/mh-aix-gcc +FILE: ../../../third_party/icu/source/config/mh-aix-va +FILE: ../../../third_party/icu/source/config/mh-alpha-linux-cc +FILE: ../../../third_party/icu/source/config/mh-alpha-linux-gcc +FILE: ../../../third_party/icu/source/config/mh-alpha-osf +FILE: ../../../third_party/icu/source/config/mh-beos +FILE: ../../../third_party/icu/source/config/mh-bsd-gcc +FILE: ../../../third_party/icu/source/config/mh-cygwin +FILE: ../../../third_party/icu/source/config/mh-cygwin-msvc +FILE: ../../../third_party/icu/source/config/mh-cygwin64 +FILE: ../../../third_party/icu/source/config/mh-darwin +FILE: ../../../third_party/icu/source/config/mh-haiku +FILE: ../../../third_party/icu/source/config/mh-hpux-acc +FILE: ../../../third_party/icu/source/config/mh-hpux-gcc +FILE: ../../../third_party/icu/source/config/mh-irix +FILE: ../../../third_party/icu/source/config/mh-linux +FILE: ../../../third_party/icu/source/config/mh-linux-va +FILE: ../../../third_party/icu/source/config/mh-mingw +FILE: ../../../third_party/icu/source/config/mh-mingw64 +FILE: ../../../third_party/icu/source/config/mh-mpras +FILE: ../../../third_party/icu/source/config/mh-msys-msvc +FILE: ../../../third_party/icu/source/config/mh-os390 +FILE: ../../../third_party/icu/source/config/mh-os400 +FILE: ../../../third_party/icu/source/config/mh-qnx +FILE: ../../../third_party/icu/source/config/mh-solaris +FILE: ../../../third_party/icu/source/config/mh-solaris-gcc +FILE: ../../../third_party/icu/source/config/mh-unknown +FILE: ../../../third_party/icu/source/config/windows-update.sed.in +FILE: ../../../third_party/icu/source/data/brkitr/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/build.xml +FILE: ../../../third_party/icu/source/data/coll/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/curr/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/dtd/cldr-35.1/common/dtd/ldml.dtd +FILE: ../../../third_party/icu/source/data/dtd/cldr-35.1/common/dtd/ldmlICU.dtd +FILE: ../../../third_party/icu/source/data/icupkg.inc.in +FILE: ../../../third_party/icu/source/data/lang/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/locales/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/makedata.mak +FILE: ../../../third_party/icu/source/data/mappings/cns-11643-1992.ucm +FILE: ../../../third_party/icu/source/data/mappings/ebcdic-xml-us.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-jp-2007.ucm +FILE: ../../../third_party/icu/source/data/mappings/euc-tw-2014.ucm +FILE: ../../../third_party/icu/source/data/mappings/gsm-03.38-2009.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1006_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1025_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1026_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1047_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1051_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1089_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1097_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1098_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1112_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1122_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1123_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1124_P100-1996.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1125_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1129_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1130_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1131_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1132_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1133_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1137_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1140_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1141_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1142_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1143_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1144_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1145_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1146_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1147_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1148_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1149_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1153_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1154_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1155_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1156_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1157_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1158_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1160_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1162_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1164_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1168_P100-2002.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1250_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1251_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1252_P100-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1253_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1254_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1255_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1256_P110-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1257_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1258_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-12712_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1276_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1363_P110-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1363_P11B-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1364_P110-2007.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1371_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1373_P100-2002.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1375_P100-2008.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1383_P110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1386_P100-2001.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1388_P103-2001.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1390_P110-2003.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-1399_P110-2003.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-16684_P110-2003.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-16804_X110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-273_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-277_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-278_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-280_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-284_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-285_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-290_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-297_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-33722_P120-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-33722_P12A_P12A-2004_U2.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-33722_P12A_P12A-2009_U2.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-37_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-420_X120-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-424_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-437_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-4517_P100-2005.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-4899_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-4909_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-4971_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-500_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5012_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5123_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5346_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5347_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5348_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5349_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5350_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5351_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5352_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5353_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5354_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5471_P100-2006.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-5478_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-720_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-737_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-775_P100-1996.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-803_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-813_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-838_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-8482_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-850_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-851_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-852_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-855_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-856_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-857_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-858_P100-1997.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-860_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-861_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-862_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-863_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-864_X110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-865_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-866_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-867_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-868_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-869_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-870_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-871_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-874_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-875_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-878_P100-1996.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-9005_X110-2007.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-901_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-902_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-9067_X100-2005.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-912_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-913_P100-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-914_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-915_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-916_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-918_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-920_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-921_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-922_P100-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-923_P100-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-930_P120-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-933_P110-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-935_P110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-937_P110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-939_P120-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-942_P12A-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-943_P130-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-943_P15A-2003.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-9447_P100-2002.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-9448_X100-2005.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-9449_P100-2002.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-949_P110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-949_P11A-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-950_P110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-954_P101-2007.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-964_P110-1999.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-970_P110_P110-2006_U2.ucm +FILE: ../../../third_party/icu/source/data/mappings/ibm-971_P100-1995.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-25546.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d1.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d2.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d3.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d4.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d5.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d6.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-d7.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-s1.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-s2.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-s3.ucm +FILE: ../../../third_party/icu/source/data/mappings/icu-internal-compound-t.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859_10-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859_11-2001.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-8859_14-1998.ucm +FILE: ../../../third_party/icu/source/data/mappings/iso-ir-165.ucm +FILE: ../../../third_party/icu/source/data/mappings/jisx-212.ucm +FILE: ../../../third_party/icu/source/data/mappings/lmb-excp.ucm +FILE: ../../../third_party/icu/source/data/mappings/macos-0_2-10.2.ucm +FILE: ../../../third_party/icu/source/data/mappings/macos-29-10.2.ucm +FILE: ../../../third_party/icu/source/data/mappings/macos-35-10.2.ucm +FILE: ../../../third_party/icu/source/data/mappings/macos-6_2-10.4.ucm +FILE: ../../../third_party/icu/source/data/mappings/macos-7_3-10.2.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-874-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-949-2000.ucm +FILE: ../../../third_party/icu/source/data/mappings/windows-950-2000.ucm +FILE: ../../../third_party/icu/source/data/misc/icudata.rc +FILE: ../../../third_party/icu/source/data/rbnf/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/region/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/unit/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/data/zone/LOCALE_DEPS.json +FILE: ../../../third_party/icu/source/extra/scrptrun/scrptrun.cpp +FILE: ../../../third_party/icu/source/extra/scrptrun/scrptrun.h +FILE: ../../../third_party/icu/source/extra/scrptrun/srtest.cpp +FILE: ../../../third_party/icu/source/extra/uconv/makedata.mak +FILE: ../../../third_party/icu/source/extra/uconv/pkgdata.inc.in +FILE: ../../../third_party/icu/source/extra/uconv/uconv.1.in +FILE: ../../../third_party/icu/source/extra/uconv/uconv.cpp +FILE: ../../../third_party/icu/source/extra/uconv/unicode/uwmsg.h +FILE: ../../../third_party/icu/source/extra/uconv/uwmsg.c +FILE: ../../../third_party/icu/source/i18n/alphaindex.cpp +FILE: ../../../third_party/icu/source/i18n/anytrans.cpp +FILE: ../../../third_party/icu/source/i18n/anytrans.h +FILE: ../../../third_party/icu/source/i18n/astro.cpp +FILE: ../../../third_party/icu/source/i18n/astro.h +FILE: ../../../third_party/icu/source/i18n/basictz.cpp +FILE: ../../../third_party/icu/source/i18n/bocsu.cpp +FILE: ../../../third_party/icu/source/i18n/bocsu.h +FILE: ../../../third_party/icu/source/i18n/brktrans.cpp +FILE: ../../../third_party/icu/source/i18n/brktrans.h +FILE: ../../../third_party/icu/source/i18n/buddhcal.cpp +FILE: ../../../third_party/icu/source/i18n/buddhcal.h +FILE: ../../../third_party/icu/source/i18n/calendar.cpp +FILE: ../../../third_party/icu/source/i18n/casetrn.cpp +FILE: ../../../third_party/icu/source/i18n/casetrn.h +FILE: ../../../third_party/icu/source/i18n/cecal.cpp +FILE: ../../../third_party/icu/source/i18n/cecal.h +FILE: ../../../third_party/icu/source/i18n/chnsecal.cpp +FILE: ../../../third_party/icu/source/i18n/chnsecal.h +FILE: ../../../third_party/icu/source/i18n/choicfmt.cpp +FILE: ../../../third_party/icu/source/i18n/coleitr.cpp +FILE: ../../../third_party/icu/source/i18n/coll.cpp +FILE: ../../../third_party/icu/source/i18n/collation.cpp +FILE: ../../../third_party/icu/source/i18n/collation.h +FILE: ../../../third_party/icu/source/i18n/collationbuilder.cpp +FILE: ../../../third_party/icu/source/i18n/collationbuilder.h +FILE: ../../../third_party/icu/source/i18n/collationcompare.cpp +FILE: ../../../third_party/icu/source/i18n/collationcompare.h +FILE: ../../../third_party/icu/source/i18n/collationdata.cpp +FILE: ../../../third_party/icu/source/i18n/collationdata.h +FILE: ../../../third_party/icu/source/i18n/collationdatabuilder.cpp +FILE: ../../../third_party/icu/source/i18n/collationdatabuilder.h +FILE: ../../../third_party/icu/source/i18n/collationdatareader.cpp +FILE: ../../../third_party/icu/source/i18n/collationdatareader.h +FILE: ../../../third_party/icu/source/i18n/collationdatawriter.cpp +FILE: ../../../third_party/icu/source/i18n/collationdatawriter.h +FILE: ../../../third_party/icu/source/i18n/collationfastlatin.cpp +FILE: ../../../third_party/icu/source/i18n/collationfastlatin.h +FILE: ../../../third_party/icu/source/i18n/collationfastlatinbuilder.cpp +FILE: ../../../third_party/icu/source/i18n/collationfastlatinbuilder.h +FILE: ../../../third_party/icu/source/i18n/collationfcd.cpp +FILE: ../../../third_party/icu/source/i18n/collationfcd.h +FILE: ../../../third_party/icu/source/i18n/collationiterator.cpp +FILE: ../../../third_party/icu/source/i18n/collationiterator.h +FILE: ../../../third_party/icu/source/i18n/collationkeys.cpp +FILE: ../../../third_party/icu/source/i18n/collationkeys.h +FILE: ../../../third_party/icu/source/i18n/collationroot.cpp +FILE: ../../../third_party/icu/source/i18n/collationroot.h +FILE: ../../../third_party/icu/source/i18n/collationrootelements.cpp +FILE: ../../../third_party/icu/source/i18n/collationrootelements.h +FILE: ../../../third_party/icu/source/i18n/collationruleparser.cpp +FILE: ../../../third_party/icu/source/i18n/collationruleparser.h +FILE: ../../../third_party/icu/source/i18n/collationsets.cpp +FILE: ../../../third_party/icu/source/i18n/collationsets.h +FILE: ../../../third_party/icu/source/i18n/collationsettings.cpp +FILE: ../../../third_party/icu/source/i18n/collationsettings.h +FILE: ../../../third_party/icu/source/i18n/collationtailoring.cpp +FILE: ../../../third_party/icu/source/i18n/collationtailoring.h +FILE: ../../../third_party/icu/source/i18n/collationweights.cpp +FILE: ../../../third_party/icu/source/i18n/collationweights.h +FILE: ../../../third_party/icu/source/i18n/collunsafe.h +FILE: ../../../third_party/icu/source/i18n/compactdecimalformat.cpp +FILE: ../../../third_party/icu/source/i18n/coptccal.cpp +FILE: ../../../third_party/icu/source/i18n/coptccal.h +FILE: ../../../third_party/icu/source/i18n/cpdtrans.cpp +FILE: ../../../third_party/icu/source/i18n/cpdtrans.h +FILE: ../../../third_party/icu/source/i18n/csdetect.cpp +FILE: ../../../third_party/icu/source/i18n/csdetect.h +FILE: ../../../third_party/icu/source/i18n/csmatch.cpp +FILE: ../../../third_party/icu/source/i18n/csmatch.h +FILE: ../../../third_party/icu/source/i18n/csr2022.cpp +FILE: ../../../third_party/icu/source/i18n/csr2022.h +FILE: ../../../third_party/icu/source/i18n/csrecog.cpp +FILE: ../../../third_party/icu/source/i18n/csrecog.h +FILE: ../../../third_party/icu/source/i18n/csrmbcs.cpp +FILE: ../../../third_party/icu/source/i18n/csrmbcs.h +FILE: ../../../third_party/icu/source/i18n/csrsbcs.cpp +FILE: ../../../third_party/icu/source/i18n/csrsbcs.h +FILE: ../../../third_party/icu/source/i18n/csrucode.cpp +FILE: ../../../third_party/icu/source/i18n/csrucode.h +FILE: ../../../third_party/icu/source/i18n/csrutf8.cpp +FILE: ../../../third_party/icu/source/i18n/csrutf8.h +FILE: ../../../third_party/icu/source/i18n/curramt.cpp +FILE: ../../../third_party/icu/source/i18n/currfmt.cpp +FILE: ../../../third_party/icu/source/i18n/currfmt.h +FILE: ../../../third_party/icu/source/i18n/currpinf.cpp +FILE: ../../../third_party/icu/source/i18n/currunit.cpp +FILE: ../../../third_party/icu/source/i18n/dangical.cpp +FILE: ../../../third_party/icu/source/i18n/dangical.h +FILE: ../../../third_party/icu/source/i18n/datefmt.cpp +FILE: ../../../third_party/icu/source/i18n/dayperiodrules.cpp +FILE: ../../../third_party/icu/source/i18n/dayperiodrules.h +FILE: ../../../third_party/icu/source/i18n/dcfmtsym.cpp +FILE: ../../../third_party/icu/source/i18n/decContext.cpp +FILE: ../../../third_party/icu/source/i18n/decContext.h +FILE: ../../../third_party/icu/source/i18n/decNumber.cpp +FILE: ../../../third_party/icu/source/i18n/decNumber.h +FILE: ../../../third_party/icu/source/i18n/decNumberLocal.h +FILE: ../../../third_party/icu/source/i18n/decimfmt.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum-dtoa.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-bignum.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-cached-powers.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-diy-fp.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-double-to-string.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-fast-dtoa.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-ieee.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-string-to-double.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.cpp +FILE: ../../../third_party/icu/source/i18n/double-conversion-strtod.h +FILE: ../../../third_party/icu/source/i18n/double-conversion-utils.h +FILE: ../../../third_party/icu/source/i18n/double-conversion.h +FILE: ../../../third_party/icu/source/i18n/dt_impl.h +FILE: ../../../third_party/icu/source/i18n/dtfmtsym.cpp +FILE: ../../../third_party/icu/source/i18n/dtitv_impl.h +FILE: ../../../third_party/icu/source/i18n/dtitvfmt.cpp +FILE: ../../../third_party/icu/source/i18n/dtitvinf.cpp +FILE: ../../../third_party/icu/source/i18n/dtptngen.cpp +FILE: ../../../third_party/icu/source/i18n/dtptngen_impl.h +FILE: ../../../third_party/icu/source/i18n/dtrule.cpp +FILE: ../../../third_party/icu/source/i18n/erarules.cpp +FILE: ../../../third_party/icu/source/i18n/erarules.h +FILE: ../../../third_party/icu/source/i18n/esctrn.cpp +FILE: ../../../third_party/icu/source/i18n/esctrn.h +FILE: ../../../third_party/icu/source/i18n/ethpccal.cpp +FILE: ../../../third_party/icu/source/i18n/ethpccal.h +FILE: ../../../third_party/icu/source/i18n/fmtable.cpp +FILE: ../../../third_party/icu/source/i18n/fmtable_cnv.cpp +FILE: ../../../third_party/icu/source/i18n/fmtableimp.h +FILE: ../../../third_party/icu/source/i18n/format.cpp +FILE: ../../../third_party/icu/source/i18n/formatted_string_builder.cpp +FILE: ../../../third_party/icu/source/i18n/formatted_string_builder.h +FILE: ../../../third_party/icu/source/i18n/formattedval_impl.h +FILE: ../../../third_party/icu/source/i18n/formattedval_iterimpl.cpp +FILE: ../../../third_party/icu/source/i18n/formattedval_sbimpl.cpp +FILE: ../../../third_party/icu/source/i18n/formattedvalue.cpp +FILE: ../../../third_party/icu/source/i18n/fphdlimp.cpp +FILE: ../../../third_party/icu/source/i18n/fphdlimp.h +FILE: ../../../third_party/icu/source/i18n/fpositer.cpp +FILE: ../../../third_party/icu/source/i18n/funcrepl.cpp +FILE: ../../../third_party/icu/source/i18n/funcrepl.h +FILE: ../../../third_party/icu/source/i18n/gender.cpp +FILE: ../../../third_party/icu/source/i18n/gregocal.cpp +FILE: ../../../third_party/icu/source/i18n/gregoimp.cpp +FILE: ../../../third_party/icu/source/i18n/gregoimp.h +FILE: ../../../third_party/icu/source/i18n/hebrwcal.cpp +FILE: ../../../third_party/icu/source/i18n/hebrwcal.h +FILE: ../../../third_party/icu/source/i18n/i18n.rc +FILE: ../../../third_party/icu/source/i18n/indiancal.cpp +FILE: ../../../third_party/icu/source/i18n/indiancal.h +FILE: ../../../third_party/icu/source/i18n/inputext.cpp +FILE: ../../../third_party/icu/source/i18n/inputext.h +FILE: ../../../third_party/icu/source/i18n/islamcal.cpp +FILE: ../../../third_party/icu/source/i18n/islamcal.h +FILE: ../../../third_party/icu/source/i18n/japancal.cpp +FILE: ../../../third_party/icu/source/i18n/japancal.h +FILE: ../../../third_party/icu/source/i18n/listformatter.cpp +FILE: ../../../third_party/icu/source/i18n/measfmt.cpp +FILE: ../../../third_party/icu/source/i18n/measunit.cpp +FILE: ../../../third_party/icu/source/i18n/measunit_extra.cpp +FILE: ../../../third_party/icu/source/i18n/measunit_impl.h +FILE: ../../../third_party/icu/source/i18n/measure.cpp +FILE: ../../../third_party/icu/source/i18n/msgfmt.cpp +FILE: ../../../third_party/icu/source/i18n/msgfmt_impl.h +FILE: ../../../third_party/icu/source/i18n/name2uni.cpp +FILE: ../../../third_party/icu/source/i18n/name2uni.h +FILE: ../../../third_party/icu/source/i18n/nfrlist.h +FILE: ../../../third_party/icu/source/i18n/nfrs.cpp +FILE: ../../../third_party/icu/source/i18n/nfrs.h +FILE: ../../../third_party/icu/source/i18n/nfrule.cpp +FILE: ../../../third_party/icu/source/i18n/nfrule.h +FILE: ../../../third_party/icu/source/i18n/nfsubs.cpp +FILE: ../../../third_party/icu/source/i18n/nfsubs.h +FILE: ../../../third_party/icu/source/i18n/nortrans.cpp +FILE: ../../../third_party/icu/source/i18n/nortrans.h +FILE: ../../../third_party/icu/source/i18n/nultrans.cpp +FILE: ../../../third_party/icu/source/i18n/nultrans.h +FILE: ../../../third_party/icu/source/i18n/number_affixutils.cpp +FILE: ../../../third_party/icu/source/i18n/number_affixutils.h +FILE: ../../../third_party/icu/source/i18n/number_asformat.cpp +FILE: ../../../third_party/icu/source/i18n/number_asformat.h +FILE: ../../../third_party/icu/source/i18n/number_capi.cpp +FILE: ../../../third_party/icu/source/i18n/number_compact.cpp +FILE: ../../../third_party/icu/source/i18n/number_compact.h +FILE: ../../../third_party/icu/source/i18n/number_currencysymbols.cpp +FILE: ../../../third_party/icu/source/i18n/number_currencysymbols.h +FILE: ../../../third_party/icu/source/i18n/number_decimalquantity.cpp +FILE: ../../../third_party/icu/source/i18n/number_decimalquantity.h +FILE: ../../../third_party/icu/source/i18n/number_decimfmtprops.cpp +FILE: ../../../third_party/icu/source/i18n/number_decimfmtprops.h +FILE: ../../../third_party/icu/source/i18n/number_decnum.h +FILE: ../../../third_party/icu/source/i18n/number_fluent.cpp +FILE: ../../../third_party/icu/source/i18n/number_formatimpl.cpp +FILE: ../../../third_party/icu/source/i18n/number_formatimpl.h +FILE: ../../../third_party/icu/source/i18n/number_grouping.cpp +FILE: ../../../third_party/icu/source/i18n/number_integerwidth.cpp +FILE: ../../../third_party/icu/source/i18n/number_longnames.cpp +FILE: ../../../third_party/icu/source/i18n/number_longnames.h +FILE: ../../../third_party/icu/source/i18n/number_mapper.cpp +FILE: ../../../third_party/icu/source/i18n/number_mapper.h +FILE: ../../../third_party/icu/source/i18n/number_microprops.h +FILE: ../../../third_party/icu/source/i18n/number_modifiers.cpp +FILE: ../../../third_party/icu/source/i18n/number_modifiers.h +FILE: ../../../third_party/icu/source/i18n/number_multiplier.cpp +FILE: ../../../third_party/icu/source/i18n/number_multiplier.h +FILE: ../../../third_party/icu/source/i18n/number_notation.cpp +FILE: ../../../third_party/icu/source/i18n/number_output.cpp +FILE: ../../../third_party/icu/source/i18n/number_padding.cpp +FILE: ../../../third_party/icu/source/i18n/number_patternmodifier.cpp +FILE: ../../../third_party/icu/source/i18n/number_patternmodifier.h +FILE: ../../../third_party/icu/source/i18n/number_patternstring.cpp +FILE: ../../../third_party/icu/source/i18n/number_patternstring.h +FILE: ../../../third_party/icu/source/i18n/number_rounding.cpp +FILE: ../../../third_party/icu/source/i18n/number_roundingutils.h +FILE: ../../../third_party/icu/source/i18n/number_scientific.cpp +FILE: ../../../third_party/icu/source/i18n/number_scientific.h +FILE: ../../../third_party/icu/source/i18n/number_skeletons.cpp +FILE: ../../../third_party/icu/source/i18n/number_skeletons.h +FILE: ../../../third_party/icu/source/i18n/number_symbolswrapper.cpp +FILE: ../../../third_party/icu/source/i18n/number_types.h +FILE: ../../../third_party/icu/source/i18n/number_usageprefs.cpp +FILE: ../../../third_party/icu/source/i18n/number_usageprefs.h +FILE: ../../../third_party/icu/source/i18n/number_utils.cpp +FILE: ../../../third_party/icu/source/i18n/number_utils.h +FILE: ../../../third_party/icu/source/i18n/number_utypes.h +FILE: ../../../third_party/icu/source/i18n/numfmt.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_affixes.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_affixes.h +FILE: ../../../third_party/icu/source/i18n/numparse_compositions.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_compositions.h +FILE: ../../../third_party/icu/source/i18n/numparse_currency.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_currency.h +FILE: ../../../third_party/icu/source/i18n/numparse_decimal.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_decimal.h +FILE: ../../../third_party/icu/source/i18n/numparse_impl.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_impl.h +FILE: ../../../third_party/icu/source/i18n/numparse_parsednumber.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_scientific.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_scientific.h +FILE: ../../../third_party/icu/source/i18n/numparse_symbols.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_symbols.h +FILE: ../../../third_party/icu/source/i18n/numparse_types.h +FILE: ../../../third_party/icu/source/i18n/numparse_utils.h +FILE: ../../../third_party/icu/source/i18n/numparse_validators.cpp +FILE: ../../../third_party/icu/source/i18n/numparse_validators.h +FILE: ../../../third_party/icu/source/i18n/numrange_capi.cpp +FILE: ../../../third_party/icu/source/i18n/numrange_fluent.cpp +FILE: ../../../third_party/icu/source/i18n/numrange_impl.cpp +FILE: ../../../third_party/icu/source/i18n/numrange_impl.h +FILE: ../../../third_party/icu/source/i18n/numsys.cpp +FILE: ../../../third_party/icu/source/i18n/numsys_impl.h +FILE: ../../../third_party/icu/source/i18n/olsontz.cpp +FILE: ../../../third_party/icu/source/i18n/olsontz.h +FILE: ../../../third_party/icu/source/i18n/persncal.cpp +FILE: ../../../third_party/icu/source/i18n/persncal.h +FILE: ../../../third_party/icu/source/i18n/pluralranges.cpp +FILE: ../../../third_party/icu/source/i18n/pluralranges.h +FILE: ../../../third_party/icu/source/i18n/plurfmt.cpp +FILE: ../../../third_party/icu/source/i18n/plurrule.cpp +FILE: ../../../third_party/icu/source/i18n/plurrule_impl.h +FILE: ../../../third_party/icu/source/i18n/quant.cpp +FILE: ../../../third_party/icu/source/i18n/quant.h +FILE: ../../../third_party/icu/source/i18n/quantityformatter.cpp +FILE: ../../../third_party/icu/source/i18n/quantityformatter.h +FILE: ../../../third_party/icu/source/i18n/rbnf.cpp +FILE: ../../../third_party/icu/source/i18n/rbt.cpp +FILE: ../../../third_party/icu/source/i18n/rbt.h +FILE: ../../../third_party/icu/source/i18n/rbt_data.cpp +FILE: ../../../third_party/icu/source/i18n/rbt_data.h +FILE: ../../../third_party/icu/source/i18n/rbt_pars.cpp +FILE: ../../../third_party/icu/source/i18n/rbt_pars.h +FILE: ../../../third_party/icu/source/i18n/rbt_rule.cpp +FILE: ../../../third_party/icu/source/i18n/rbt_rule.h +FILE: ../../../third_party/icu/source/i18n/rbt_set.cpp +FILE: ../../../third_party/icu/source/i18n/rbt_set.h +FILE: ../../../third_party/icu/source/i18n/rbtz.cpp +FILE: ../../../third_party/icu/source/i18n/regexcmp.cpp +FILE: ../../../third_party/icu/source/i18n/regexcmp.h +FILE: ../../../third_party/icu/source/i18n/regexcst.h +FILE: ../../../third_party/icu/source/i18n/regeximp.cpp +FILE: ../../../third_party/icu/source/i18n/regeximp.h +FILE: ../../../third_party/icu/source/i18n/regexst.cpp +FILE: ../../../third_party/icu/source/i18n/regexst.h +FILE: ../../../third_party/icu/source/i18n/regextxt.cpp +FILE: ../../../third_party/icu/source/i18n/regextxt.h +FILE: ../../../third_party/icu/source/i18n/region.cpp +FILE: ../../../third_party/icu/source/i18n/region_impl.h +FILE: ../../../third_party/icu/source/i18n/reldatefmt.cpp +FILE: ../../../third_party/icu/source/i18n/reldtfmt.cpp +FILE: ../../../third_party/icu/source/i18n/reldtfmt.h +FILE: ../../../third_party/icu/source/i18n/rematch.cpp +FILE: ../../../third_party/icu/source/i18n/remtrans.cpp +FILE: ../../../third_party/icu/source/i18n/remtrans.h +FILE: ../../../third_party/icu/source/i18n/repattrn.cpp +FILE: ../../../third_party/icu/source/i18n/rulebasedcollator.cpp +FILE: ../../../third_party/icu/source/i18n/scientificnumberformatter.cpp +FILE: ../../../third_party/icu/source/i18n/scriptset.cpp +FILE: ../../../third_party/icu/source/i18n/scriptset.h +FILE: ../../../third_party/icu/source/i18n/search.cpp +FILE: ../../../third_party/icu/source/i18n/selfmt.cpp +FILE: ../../../third_party/icu/source/i18n/selfmtimpl.h +FILE: ../../../third_party/icu/source/i18n/sharedbreakiterator.cpp +FILE: ../../../third_party/icu/source/i18n/sharedbreakiterator.h +FILE: ../../../third_party/icu/source/i18n/sharedcalendar.h +FILE: ../../../third_party/icu/source/i18n/shareddateformatsymbols.h +FILE: ../../../third_party/icu/source/i18n/sharednumberformat.h +FILE: ../../../third_party/icu/source/i18n/sharedpluralrules.h +FILE: ../../../third_party/icu/source/i18n/simpletz.cpp +FILE: ../../../third_party/icu/source/i18n/smpdtfmt.cpp +FILE: ../../../third_party/icu/source/i18n/smpdtfst.cpp +FILE: ../../../third_party/icu/source/i18n/smpdtfst.h +FILE: ../../../third_party/icu/source/i18n/sortkey.cpp +FILE: ../../../third_party/icu/source/i18n/standardplural.cpp +FILE: ../../../third_party/icu/source/i18n/standardplural.h +FILE: ../../../third_party/icu/source/i18n/string_segment.cpp +FILE: ../../../third_party/icu/source/i18n/string_segment.h +FILE: ../../../third_party/icu/source/i18n/strmatch.cpp +FILE: ../../../third_party/icu/source/i18n/strmatch.h +FILE: ../../../third_party/icu/source/i18n/strrepl.cpp +FILE: ../../../third_party/icu/source/i18n/strrepl.h +FILE: ../../../third_party/icu/source/i18n/stsearch.cpp +FILE: ../../../third_party/icu/source/i18n/taiwncal.cpp +FILE: ../../../third_party/icu/source/i18n/taiwncal.h +FILE: ../../../third_party/icu/source/i18n/timezone.cpp +FILE: ../../../third_party/icu/source/i18n/titletrn.cpp +FILE: ../../../third_party/icu/source/i18n/titletrn.h +FILE: ../../../third_party/icu/source/i18n/tmunit.cpp +FILE: ../../../third_party/icu/source/i18n/tmutamt.cpp +FILE: ../../../third_party/icu/source/i18n/tmutfmt.cpp +FILE: ../../../third_party/icu/source/i18n/tolowtrn.cpp +FILE: ../../../third_party/icu/source/i18n/tolowtrn.h +FILE: ../../../third_party/icu/source/i18n/toupptrn.cpp +FILE: ../../../third_party/icu/source/i18n/toupptrn.h +FILE: ../../../third_party/icu/source/i18n/translit.cpp +FILE: ../../../third_party/icu/source/i18n/transreg.cpp +FILE: ../../../third_party/icu/source/i18n/transreg.h +FILE: ../../../third_party/icu/source/i18n/tridpars.cpp +FILE: ../../../third_party/icu/source/i18n/tridpars.h +FILE: ../../../third_party/icu/source/i18n/tzfmt.cpp +FILE: ../../../third_party/icu/source/i18n/tzgnames.cpp +FILE: ../../../third_party/icu/source/i18n/tzgnames.h +FILE: ../../../third_party/icu/source/i18n/tznames.cpp +FILE: ../../../third_party/icu/source/i18n/tznames_impl.cpp +FILE: ../../../third_party/icu/source/i18n/tznames_impl.h +FILE: ../../../third_party/icu/source/i18n/tzrule.cpp +FILE: ../../../third_party/icu/source/i18n/tztrans.cpp +FILE: ../../../third_party/icu/source/i18n/ucal.cpp +FILE: ../../../third_party/icu/source/i18n/ucln_in.cpp +FILE: ../../../third_party/icu/source/i18n/ucln_in.h +FILE: ../../../third_party/icu/source/i18n/ucol.cpp +FILE: ../../../third_party/icu/source/i18n/ucol_imp.h +FILE: ../../../third_party/icu/source/i18n/ucol_res.cpp +FILE: ../../../third_party/icu/source/i18n/ucol_sit.cpp +FILE: ../../../third_party/icu/source/i18n/ucoleitr.cpp +FILE: ../../../third_party/icu/source/i18n/ucsdet.cpp +FILE: ../../../third_party/icu/source/i18n/udat.cpp +FILE: ../../../third_party/icu/source/i18n/udateintervalformat.cpp +FILE: ../../../third_party/icu/source/i18n/udatpg.cpp +FILE: ../../../third_party/icu/source/i18n/ufieldpositer.cpp +FILE: ../../../third_party/icu/source/i18n/uitercollationiterator.cpp +FILE: ../../../third_party/icu/source/i18n/uitercollationiterator.h +FILE: ../../../third_party/icu/source/i18n/ulistformatter.cpp +FILE: ../../../third_party/icu/source/i18n/ulocdata.cpp +FILE: ../../../third_party/icu/source/i18n/umsg.cpp +FILE: ../../../third_party/icu/source/i18n/umsg_imp.h +FILE: ../../../third_party/icu/source/i18n/unesctrn.cpp +FILE: ../../../third_party/icu/source/i18n/unesctrn.h +FILE: ../../../third_party/icu/source/i18n/uni2name.cpp +FILE: ../../../third_party/icu/source/i18n/uni2name.h +FILE: ../../../third_party/icu/source/i18n/unicode/alphaindex.h +FILE: ../../../third_party/icu/source/i18n/unicode/basictz.h +FILE: ../../../third_party/icu/source/i18n/unicode/calendar.h +FILE: ../../../third_party/icu/source/i18n/unicode/choicfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/coleitr.h +FILE: ../../../third_party/icu/source/i18n/unicode/coll.h +FILE: ../../../third_party/icu/source/i18n/unicode/compactdecimalformat.h +FILE: ../../../third_party/icu/source/i18n/unicode/curramt.h +FILE: ../../../third_party/icu/source/i18n/unicode/currpinf.h +FILE: ../../../third_party/icu/source/i18n/unicode/currunit.h +FILE: ../../../third_party/icu/source/i18n/unicode/datefmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/dcfmtsym.h +FILE: ../../../third_party/icu/source/i18n/unicode/decimfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/dtfmtsym.h +FILE: ../../../third_party/icu/source/i18n/unicode/dtitvfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/dtitvinf.h +FILE: ../../../third_party/icu/source/i18n/unicode/dtptngen.h +FILE: ../../../third_party/icu/source/i18n/unicode/dtrule.h +FILE: ../../../third_party/icu/source/i18n/unicode/fieldpos.h +FILE: ../../../third_party/icu/source/i18n/unicode/fmtable.h +FILE: ../../../third_party/icu/source/i18n/unicode/format.h +FILE: ../../../third_party/icu/source/i18n/unicode/formattedvalue.h +FILE: ../../../third_party/icu/source/i18n/unicode/fpositer.h +FILE: ../../../third_party/icu/source/i18n/unicode/gender.h +FILE: ../../../third_party/icu/source/i18n/unicode/gregocal.h +FILE: ../../../third_party/icu/source/i18n/unicode/listformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/measfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/measunit.h +FILE: ../../../third_party/icu/source/i18n/unicode/measure.h +FILE: ../../../third_party/icu/source/i18n/unicode/msgfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/nounit.h +FILE: ../../../third_party/icu/source/i18n/unicode/numberformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/numberrangeformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/numfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/numsys.h +FILE: ../../../third_party/icu/source/i18n/unicode/plurfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/plurrule.h +FILE: ../../../third_party/icu/source/i18n/unicode/rbnf.h +FILE: ../../../third_party/icu/source/i18n/unicode/rbtz.h +FILE: ../../../third_party/icu/source/i18n/unicode/regex.h +FILE: ../../../third_party/icu/source/i18n/unicode/region.h +FILE: ../../../third_party/icu/source/i18n/unicode/reldatefmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/scientificnumberformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/search.h +FILE: ../../../third_party/icu/source/i18n/unicode/selfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/simpletz.h +FILE: ../../../third_party/icu/source/i18n/unicode/smpdtfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/sortkey.h +FILE: ../../../third_party/icu/source/i18n/unicode/stsearch.h +FILE: ../../../third_party/icu/source/i18n/unicode/tblcoll.h +FILE: ../../../third_party/icu/source/i18n/unicode/timezone.h +FILE: ../../../third_party/icu/source/i18n/unicode/tmunit.h +FILE: ../../../third_party/icu/source/i18n/unicode/tmutamt.h +FILE: ../../../third_party/icu/source/i18n/unicode/tmutfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/translit.h +FILE: ../../../third_party/icu/source/i18n/unicode/tzfmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/tznames.h +FILE: ../../../third_party/icu/source/i18n/unicode/tzrule.h +FILE: ../../../third_party/icu/source/i18n/unicode/tztrans.h +FILE: ../../../third_party/icu/source/i18n/unicode/ucal.h +FILE: ../../../third_party/icu/source/i18n/unicode/ucol.h +FILE: ../../../third_party/icu/source/i18n/unicode/ucoleitr.h +FILE: ../../../third_party/icu/source/i18n/unicode/ucsdet.h +FILE: ../../../third_party/icu/source/i18n/unicode/udat.h +FILE: ../../../third_party/icu/source/i18n/unicode/udateintervalformat.h +FILE: ../../../third_party/icu/source/i18n/unicode/udatpg.h +FILE: ../../../third_party/icu/source/i18n/unicode/ufieldpositer.h +FILE: ../../../third_party/icu/source/i18n/unicode/uformattable.h +FILE: ../../../third_party/icu/source/i18n/unicode/uformattedvalue.h +FILE: ../../../third_party/icu/source/i18n/unicode/ugender.h +FILE: ../../../third_party/icu/source/i18n/unicode/ulistformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/ulocdata.h +FILE: ../../../third_party/icu/source/i18n/unicode/umsg.h +FILE: ../../../third_party/icu/source/i18n/unicode/unirepl.h +FILE: ../../../third_party/icu/source/i18n/unicode/unum.h +FILE: ../../../third_party/icu/source/i18n/unicode/unumberformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/unumberrangeformatter.h +FILE: ../../../third_party/icu/source/i18n/unicode/unumsys.h +FILE: ../../../third_party/icu/source/i18n/unicode/upluralrules.h +FILE: ../../../third_party/icu/source/i18n/unicode/uregex.h +FILE: ../../../third_party/icu/source/i18n/unicode/uregion.h +FILE: ../../../third_party/icu/source/i18n/unicode/ureldatefmt.h +FILE: ../../../third_party/icu/source/i18n/unicode/usearch.h +FILE: ../../../third_party/icu/source/i18n/unicode/uspoof.h +FILE: ../../../third_party/icu/source/i18n/unicode/utmscale.h +FILE: ../../../third_party/icu/source/i18n/unicode/utrans.h +FILE: ../../../third_party/icu/source/i18n/unicode/vtzone.h +FILE: ../../../third_party/icu/source/i18n/units_complexconverter.cpp +FILE: ../../../third_party/icu/source/i18n/units_complexconverter.h +FILE: ../../../third_party/icu/source/i18n/units_converter.cpp +FILE: ../../../third_party/icu/source/i18n/units_converter.h +FILE: ../../../third_party/icu/source/i18n/units_data.cpp +FILE: ../../../third_party/icu/source/i18n/units_data.h +FILE: ../../../third_party/icu/source/i18n/units_router.cpp +FILE: ../../../third_party/icu/source/i18n/units_router.h +FILE: ../../../third_party/icu/source/i18n/unum.cpp +FILE: ../../../third_party/icu/source/i18n/unumsys.cpp +FILE: ../../../third_party/icu/source/i18n/upluralrules.cpp +FILE: ../../../third_party/icu/source/i18n/uregex.cpp +FILE: ../../../third_party/icu/source/i18n/uregexc.cpp +FILE: ../../../third_party/icu/source/i18n/uregion.cpp +FILE: ../../../third_party/icu/source/i18n/usearch.cpp +FILE: ../../../third_party/icu/source/i18n/uspoof.cpp +FILE: ../../../third_party/icu/source/i18n/uspoof_build.cpp +FILE: ../../../third_party/icu/source/i18n/uspoof_conf.cpp +FILE: ../../../third_party/icu/source/i18n/uspoof_conf.h +FILE: ../../../third_party/icu/source/i18n/uspoof_impl.cpp +FILE: ../../../third_party/icu/source/i18n/uspoof_impl.h +FILE: ../../../third_party/icu/source/i18n/usrchimp.h +FILE: ../../../third_party/icu/source/i18n/utf16collationiterator.cpp +FILE: ../../../third_party/icu/source/i18n/utf16collationiterator.h +FILE: ../../../third_party/icu/source/i18n/utf8collationiterator.cpp +FILE: ../../../third_party/icu/source/i18n/utf8collationiterator.h +FILE: ../../../third_party/icu/source/i18n/utmscale.cpp +FILE: ../../../third_party/icu/source/i18n/utrans.cpp +FILE: ../../../third_party/icu/source/i18n/vtzone.cpp +FILE: ../../../third_party/icu/source/i18n/vzone.cpp +FILE: ../../../third_party/icu/source/i18n/vzone.h +FILE: ../../../third_party/icu/source/i18n/windtfmt.cpp +FILE: ../../../third_party/icu/source/i18n/windtfmt.h +FILE: ../../../third_party/icu/source/i18n/winnmfmt.cpp +FILE: ../../../third_party/icu/source/i18n/winnmfmt.h +FILE: ../../../third_party/icu/source/i18n/wintzimpl.cpp +FILE: ../../../third_party/icu/source/i18n/wintzimpl.h +FILE: ../../../third_party/icu/source/i18n/zonemeta.cpp +FILE: ../../../third_party/icu/source/i18n/zonemeta.h +FILE: ../../../third_party/icu/source/i18n/zrule.cpp +FILE: ../../../third_party/icu/source/i18n/zrule.h +FILE: ../../../third_party/icu/source/i18n/ztrans.cpp +FILE: ../../../third_party/icu/source/i18n/ztrans.h +FILE: ../../../third_party/icu/source/icudefs.mk.in +FILE: ../../../third_party/icu/source/io/io.rc +FILE: ../../../third_party/icu/source/io/locbund.cpp +FILE: ../../../third_party/icu/source/io/locbund.h +FILE: ../../../third_party/icu/source/io/sprintf.cpp +FILE: ../../../third_party/icu/source/io/sscanf.cpp +FILE: ../../../third_party/icu/source/io/ucln_io.cpp +FILE: ../../../third_party/icu/source/io/ucln_io.h +FILE: ../../../third_party/icu/source/io/ufile.cpp +FILE: ../../../third_party/icu/source/io/ufile.h +FILE: ../../../third_party/icu/source/io/ufmt_cmn.cpp +FILE: ../../../third_party/icu/source/io/ufmt_cmn.h +FILE: ../../../third_party/icu/source/io/unicode/ustdio.h +FILE: ../../../third_party/icu/source/io/unicode/ustream.h +FILE: ../../../third_party/icu/source/io/uprintf.cpp +FILE: ../../../third_party/icu/source/io/uprintf.h +FILE: ../../../third_party/icu/source/io/uprntf_p.cpp +FILE: ../../../third_party/icu/source/io/uscanf.cpp +FILE: ../../../third_party/icu/source/io/uscanf.h +FILE: ../../../third_party/icu/source/io/uscanf_p.cpp +FILE: ../../../third_party/icu/source/io/ustdio.cpp +FILE: ../../../third_party/icu/source/io/ustream.cpp +FILE: ../../../third_party/icu/source/python/icutools/databuilder/filtration_schema.json +FILE: ../../../third_party/icu/source/stubdata/stubdata.cpp +FILE: ../../../third_party/icu/source/tools/ctestfw/ctest.c +FILE: ../../../third_party/icu/source/tools/ctestfw/datamap.cpp +FILE: ../../../third_party/icu/source/tools/ctestfw/testdata.cpp +FILE: ../../../third_party/icu/source/tools/ctestfw/tstdtmod.cpp +FILE: ../../../third_party/icu/source/tools/ctestfw/ucln_ct.c +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/ctest.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/datamap.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/testdata.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/testlog.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/testtype.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/tstdtmod.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/uperf.h +FILE: ../../../third_party/icu/source/tools/ctestfw/unicode/utimer.h +FILE: ../../../third_party/icu/source/tools/ctestfw/uperf.cpp +FILE: ../../../third_party/icu/source/tools/escapesrc/cptbl.h +FILE: ../../../third_party/icu/source/tools/escapesrc/escapesrc.cpp +FILE: ../../../third_party/icu/source/tools/escapesrc/expect-simple.cpp +FILE: ../../../third_party/icu/source/tools/escapesrc/tblgen.cpp +FILE: ../../../third_party/icu/source/tools/escapesrc/test-nochange.cpp +FILE: ../../../third_party/icu/source/tools/escapesrc/test-simple.cpp +FILE: ../../../third_party/icu/source/tools/genbrk/genbrk.1.in +FILE: ../../../third_party/icu/source/tools/genbrk/genbrk.cpp +FILE: ../../../third_party/icu/source/tools/genccode/genccode.8.in +FILE: ../../../third_party/icu/source/tools/genccode/genccode.c +FILE: ../../../third_party/icu/source/tools/gencfu/gencfu.1.in +FILE: ../../../third_party/icu/source/tools/gencfu/gencfu.cpp +FILE: ../../../third_party/icu/source/tools/gencmn/gencmn.8.in +FILE: ../../../third_party/icu/source/tools/gencmn/gencmn.c +FILE: ../../../third_party/icu/source/tools/gencnval/gencnval.1.in +FILE: ../../../third_party/icu/source/tools/gencnval/gencnval.c +FILE: ../../../third_party/icu/source/tools/gencolusb/extract_unsafe_backwards.cpp +FILE: ../../../third_party/icu/source/tools/gencolusb/verify_uset.cpp +FILE: ../../../third_party/icu/source/tools/gendict/gendict.1.in +FILE: ../../../third_party/icu/source/tools/gendict/gendict.cpp +FILE: ../../../third_party/icu/source/tools/gennorm2/extradata.cpp +FILE: ../../../third_party/icu/source/tools/gennorm2/extradata.h +FILE: ../../../third_party/icu/source/tools/gennorm2/gennorm2.cpp +FILE: ../../../third_party/icu/source/tools/gennorm2/n2builder.cpp +FILE: ../../../third_party/icu/source/tools/gennorm2/n2builder.h +FILE: ../../../third_party/icu/source/tools/gennorm2/norms.cpp +FILE: ../../../third_party/icu/source/tools/gennorm2/norms.h +FILE: ../../../third_party/icu/source/tools/genrb/derb.1.in +FILE: ../../../third_party/icu/source/tools/genrb/derb.cpp +FILE: ../../../third_party/icu/source/tools/genrb/errmsg.c +FILE: ../../../third_party/icu/source/tools/genrb/errmsg.h +FILE: ../../../third_party/icu/source/tools/genrb/filterrb.cpp +FILE: ../../../third_party/icu/source/tools/genrb/filterrb.h +FILE: ../../../third_party/icu/source/tools/genrb/genrb.1.in +FILE: ../../../third_party/icu/source/tools/genrb/genrb.cpp +FILE: ../../../third_party/icu/source/tools/genrb/genrb.h +FILE: ../../../third_party/icu/source/tools/genrb/parse.cpp +FILE: ../../../third_party/icu/source/tools/genrb/parse.h +FILE: ../../../third_party/icu/source/tools/genrb/prscmnts.cpp +FILE: ../../../third_party/icu/source/tools/genrb/prscmnts.h +FILE: ../../../third_party/icu/source/tools/genrb/rbutil.c +FILE: ../../../third_party/icu/source/tools/genrb/rbutil.h +FILE: ../../../third_party/icu/source/tools/genrb/read.c +FILE: ../../../third_party/icu/source/tools/genrb/read.h +FILE: ../../../third_party/icu/source/tools/genrb/reslist.cpp +FILE: ../../../third_party/icu/source/tools/genrb/reslist.h +FILE: ../../../third_party/icu/source/tools/genrb/rle.c +FILE: ../../../third_party/icu/source/tools/genrb/rle.h +FILE: ../../../third_party/icu/source/tools/genrb/ustr.c +FILE: ../../../third_party/icu/source/tools/genrb/ustr.h +FILE: ../../../third_party/icu/source/tools/genrb/wrtjava.cpp +FILE: ../../../third_party/icu/source/tools/genrb/wrtxml.cpp +FILE: ../../../third_party/icu/source/tools/gensprep/gensprep.8.in +FILE: ../../../third_party/icu/source/tools/gensprep/gensprep.c +FILE: ../../../third_party/icu/source/tools/gensprep/gensprep.h +FILE: ../../../third_party/icu/source/tools/gensprep/store.c +FILE: ../../../third_party/icu/source/tools/gentest/genres32.c +FILE: ../../../third_party/icu/source/tools/gentest/gentest.c +FILE: ../../../third_party/icu/source/tools/gentest/gentest.h +FILE: ../../../third_party/icu/source/tools/icuinfo/icuinfo.cpp +FILE: ../../../third_party/icu/source/tools/icuinfo/testplug.c +FILE: ../../../third_party/icu/source/tools/icupkg/icupkg.8.in +FILE: ../../../third_party/icu/source/tools/icupkg/icupkg.cpp +FILE: ../../../third_party/icu/source/tools/icuswap/icuswap.cpp +FILE: ../../../third_party/icu/source/tools/makeconv/gencnvex.c +FILE: ../../../third_party/icu/source/tools/makeconv/genmbcs.cpp +FILE: ../../../third_party/icu/source/tools/makeconv/genmbcs.h +FILE: ../../../third_party/icu/source/tools/makeconv/makeconv.1.in +FILE: ../../../third_party/icu/source/tools/makeconv/makeconv.cpp +FILE: ../../../third_party/icu/source/tools/makeconv/makeconv.h +FILE: ../../../third_party/icu/source/tools/makeconv/ucnvstat.c +FILE: ../../../third_party/icu/source/tools/pkgdata/pkgdata.1.in +FILE: ../../../third_party/icu/source/tools/pkgdata/pkgdata.cpp +FILE: ../../../third_party/icu/source/tools/pkgdata/pkgtypes.c +FILE: ../../../third_party/icu/source/tools/pkgdata/pkgtypes.h +FILE: ../../../third_party/icu/source/tools/toolutil/collationinfo.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/collationinfo.h +FILE: ../../../third_party/icu/source/tools/toolutil/dbgutil.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/dbgutil.h +FILE: ../../../third_party/icu/source/tools/toolutil/denseranges.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/denseranges.h +FILE: ../../../third_party/icu/source/tools/toolutil/filestrm.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/filestrm.h +FILE: ../../../third_party/icu/source/tools/toolutil/filetools.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/filetools.h +FILE: ../../../third_party/icu/source/tools/toolutil/flagparser.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/flagparser.h +FILE: ../../../third_party/icu/source/tools/toolutil/package.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/package.h +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_genc.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_genc.h +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_gencmn.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_gencmn.h +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_icu.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_icu.h +FILE: ../../../third_party/icu/source/tools/toolutil/pkg_imp.h +FILE: ../../../third_party/icu/source/tools/toolutil/pkgitems.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/ppucd.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/ppucd.h +FILE: ../../../third_party/icu/source/tools/toolutil/swapimpl.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/swapimpl.h +FILE: ../../../third_party/icu/source/tools/toolutil/toolutil.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/toolutil.h +FILE: ../../../third_party/icu/source/tools/toolutil/ucbuf.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/ucbuf.h +FILE: ../../../third_party/icu/source/tools/toolutil/ucln_tu.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/ucm.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/ucm.h +FILE: ../../../third_party/icu/source/tools/toolutil/ucmstate.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/udbgutil.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/udbgutil.h +FILE: ../../../third_party/icu/source/tools/toolutil/unewdata.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/unewdata.h +FILE: ../../../third_party/icu/source/tools/toolutil/uoptions.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/uoptions.h +FILE: ../../../third_party/icu/source/tools/toolutil/uparse.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/uparse.h +FILE: ../../../third_party/icu/source/tools/toolutil/writesrc.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/writesrc.h +FILE: ../../../third_party/icu/source/tools/toolutil/xmlparser.cpp +FILE: ../../../third_party/icu/source/tools/toolutil/xmlparser.h +FILE: ../../../third_party/icu/source/tools/tzcode/icuregions +FILE: ../../../third_party/icu/source/tools/tzcode/icuzdump.cpp +FILE: ../../../third_party/icu/source/tools/tzcode/icuzones +FILE: ../../../third_party/icu/source/tools/tzcode/tz2icu.cpp +FILE: ../../../third_party/icu/source/tools/tzcode/tz2icu.h +---------------------------------------------------------------------------------------------------- +Unicode® Terms of Use +For the general privacy policy governing access to this site, see the Unicode Privacy Policy. For trademark usage, see the Unicode® Consortium Name and Trademark Usage Policy. + +A. Unicode Copyright. +1. Copyright © 1991-2017 Unicode, Inc. All rights reserved. +2. Certain documents and files on this website contain a legend indicating that "Modification is permitted." Any person is hereby authorized, without fee, to modify such documents and files to create derivative works conforming to the Unicode® Standard, subject to Terms and Conditions herein. +3. Any person is hereby authorized, without fee, to view, use, reproduce, and distribute all documents and files solely for informational purposes and in the creation of products supporting the Unicode Standard, subject to the Terms and Conditions herein. +4. Further specifications of rights and restrictions pertaining to the use of the particular set of data files known as the "Unicode Character Database" can be found in the License. +5. Each version of the Unicode Standard has further specifications of rights and restrictions of use. For the book editions (Unicode 5.0 and earlier), these are found on the back of the title page. The online code charts carry specific restrictions. All other files, including online documentation of the core specification for Unicode 6.0 and later, are covered under these general Terms of Use. +6. No license is granted to "mirror" the Unicode website where a fee is charged for access to the "mirror" site. +7. Modification is not permitted with respect to this document. All copies of this document must be verbatim. +B. Restricted Rights Legend. Any technical data or software which is licensed to the United States of America, its agencies and/or instrumentalities under this Agreement is commercial technical data or commercial computer software developed exclusively at private expense as defined in FAR 2.101, or DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, duplication, or disclosure by the Government is subject to restrictions as set forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov 1995) and this Agreement. For Software, in accordance with FAR 12-212 or DFARS 227-7202, as applicable, use, duplication or disclosure by the Government is subject to the restrictions set forth in this Agreement. +C. Warranties and Disclaimers. +1. This publication and/or website may include technical or typographical errors or other inaccuracies . Changes are periodically added to the information herein; these changes will be incorporated in new editions of the publication and/or website. Unicode may make improvements and/or changes in the product(s) and/or program(s) described in this publication and/or website at any time. +2. If this file has been purchased on magnetic or optical media from Unicode, Inc. the sole and exclusive remedy for any claim will be exchange of the defective media within ninety (90) days of original purchase. +3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UNICODE AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR OMISSIONS IN THIS PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH ARE REFERENCED BY OR LINKED TO THIS PUBLICATION OR THE UNICODE WEBSITE. +D. Waiver of Damages. In no event shall Unicode or its licensors be liable for any special, incidental, indirect or consequential damages of any kind, or any damages whatsoever, whether or not Unicode was advised of the possibility of the damage, including, without limitation, those resulting from the following: loss of use, data or profits, in connection with the use, modification or distribution of this information or its derivatives. +E. Trademarks & Logos. +1. The Unicode Word Mark and the Unicode Logo are trademarks of Unicode, Inc. “The Unicode Consortium” and “Unicode, Inc.” are trade names of Unicode, Inc. Use of the information and materials found on this website indicates your acknowledgement of Unicode, Inc.’s exclusive worldwide rights in the Unicode Word Mark, the Unicode Logo, and the Unicode trade names. +2. The Unicode Consortium Name and Trademark Usage Policy (“Trademark Policy”) are incorporated herein by reference and you agree to abide by the provisions of the Trademark Policy, which may be changed from time to time in the sole discretion of Unicode, Inc. +3. All third party trademarks referenced herein are the property of their respective owners. +F. Miscellaneous. +1. Jurisdiction and Venue. This server is operated from a location in the State of California, United States of America. Unicode makes no representation that the materials are appropriate for use in other locations. If you access this server from other locations, you are responsible for compliance with local laws. This Agreement, all use of this site and any claims and damages resulting from use of this site are governed solely by the laws of the State of California without regard to any principles which would apply the laws of a different jurisdiction. The user agrees that any disputes regarding this site shall be resolved solely in the courts located in Santa Clara County, California. The user agrees said courts have personal jurisdiction and agree to waive any right to transfer the dispute to any other forum. +2. Modification by Unicode Unicode shall have the right to modify this Agreement at any time by posting it to this site. The user may not assign any part of this Agreement without Unicode’s prior written consent. +3. Taxes. The user agrees to pay any taxes arising from access to this website or use of the information herein, except for those based on Unicode’s net income. +4. Severability. If any provision of this Agreement is declared invalid or unenforceable, the remaining provisions of this Agreement shall remain in effect. +5. Entire Agreement. This Agreement constitutes the entire agreement between the parties. + +EXHIBIT 1 +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +Unicode Data Files do not include PDF online code charts under the +directory http://www.unicode.org/Public/. + +Software includes any source code published in the Unicode Standard +or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2017 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/source/data/mappings/iso-8859_10-1998.ucm +TYPE: LicenseType.unknown +FILE: ../../../third_party/icu/source/data/mappings/iso-8859_10-1998.ucm +---------------------------------------------------------------------------------------------------- +Copyright (c) 1999 Unicode, Inc. All Rights reserved. + Copyright (C) 2002-2005, International Business Machines + Corporation and others. All Rights Reserved. + +This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +No claims are made as to fitness for any particular purpose. No +warranties of any kind are expressed or implied. The recipient +agrees to determine applicability of information provided. If this +file has been provided on optical media by Unicode, Inc., the sole +remedy for any claim will be exchange of defective media within 90 +days of receipt. + +Unicode, Inc. hereby grants the right to freely use the information +supplied in this file in the creation of products supporting the +Unicode Standard, and to make copies of this file in any form for +internal or external distribution as long as this notice remains +attached. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/source/data/mappings/iso-8859_11-2001.ucm +TYPE: LicenseType.unknown +FILE: ../../../third_party/icu/source/data/mappings/iso-8859_11-2001.ucm +---------------------------------------------------------------------------------------------------- +Copyright (c) 2002 Unicode, Inc. All Rights reserved. + Copyright (C) 2002-2005, International Business Machines + Corporation and others. All Rights Reserved. + +This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +No claims are made as to fitness for any particular purpose. No +warranties of any kind are expressed or implied. The recipient +agrees to determine applicability of information provided. If this +file has been provided on optical media by Unicode, Inc., the sole +remedy for any claim will be exchange of defective media within 90 +days of receipt. + +Unicode, Inc. hereby grants the right to freely use the information +supplied in this file in the creation of products supporting the +Unicode Standard, and to make copies of this file in any form for +internal or external distribution as long as this notice remains +attached. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/source/data/mappings/iso-8859_14-1998.ucm +TYPE: LicenseType.unknown +FILE: ../../../third_party/icu/source/data/mappings/iso-8859_14-1998.ucm +---------------------------------------------------------------------------------------------------- +Copyright (c) 1998 - 1999 Unicode, Inc. All Rights reserved. + Copyright (C) 2002-2005, International Business Machines + Corporation and others. All Rights Reserved. + +This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +No claims are made as to fitness for any particular purpose. No +warranties of any kind are expressed or implied. The recipient +agrees to determine applicability of information provided. If this +file has been provided on optical media by Unicode, Inc., the sole +remedy for any claim will be exchange of defective media within 90 +days of receipt. + +Unicode, Inc. hereby grants the right to freely use the information +supplied in this file in the creation of products supporting the +Unicode Standard, and to make copies of this file in any form for +internal or external distribution as long as this notice remains +attached. +==================================================================================================== + +==================================================================================================== +LIBRARY: icu +ORIGIN: ../../../third_party/icu/source/i18n/decContext.cpp + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/source/i18n/decContext.cpp +---------------------------------------------------------------------------------------------------- +Copyright (c) IBM Corporation, 2000-2012. 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: icu +ORIGIN: ../../../third_party/icu/source/i18n/decContext.h + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/source/i18n/decContext.h +---------------------------------------------------------------------------------------------------- +Copyright (c) IBM Corporation, 2000-2011. 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: icu +ORIGIN: ../../../third_party/icu/source/i18n/decNumber.cpp + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/source/i18n/decNumber.cpp +---------------------------------------------------------------------------------------------------- +Copyright (c) IBM Corporation, 2000-2014. 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: icu +ORIGIN: ../../../third_party/icu/source/i18n/decNumber.h + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/source/i18n/decNumber.h +---------------------------------------------------------------------------------------------------- +Copyright (c) IBM Corporation, 2000-2010. 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: icu +ORIGIN: ../../../third_party/icu/source/i18n/decNumberLocal.h + ../../../third_party/dart/runtime/third_party/double-conversion/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/icu/source/i18n/decNumberLocal.h ---------------------------------------------------------------------------------------------------- -University of Illinois/NCSA -Open Source License +Copyright (c) IBM Corporation, 2000-2016. All rights reserved. -Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -All rights reserved. + * 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. -Developed by: +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. +==================================================================================================== - LLVM Team +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/EGL/eglplatform.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/khronos/EGL/eglplatform.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2007-2016 The Khronos Group Inc. - University of Illinois at Urbana-Champaign +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: - http://llvm.org +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/GLES3/gl31.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/khronos/GLES3/gl31.h +FILE: ../../../third_party/khronos/GLES3/gl32.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2016 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/KHR/khrplatform.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/khronos/KHR/khrplatform.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2008-2009 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/LICENSE +TYPE: LicenseType.mit +FILE: ../../../third_party/khronos/DEPS +---------------------------------------------------------------------------------------------------- +Copyright (c) 2007-2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + +Copyright (C) 1992 Silicon Graphics, Inc. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal with +this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimers. +The above copyright notice including the dates of first publication and either +this permission notice or a reference to http://oss.sgi.com/projects/FreeB +shall be included in all copies or substantial portions of the Software. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimers in the - documentation and/or other materials provided with the distribution. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON +GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * Neither the names of the LLVM Team, University of Illinois at - Urbana-Champaign, nor the names of its contributors may be used to - endorse or promote products derived from this Software without specific - prior written permission. +Except as contained in this notice, the name of Silicon Graphics, Inc. shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from Silicon +Graphics, Inc. +==================================================================================================== -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE -SOFTWARE. ==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/noninclude/GL/glxext.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/khronos/noninclude/GL/glxext.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2007-2012 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ==================================================================================================== -LIBRARY: libcxx -LIBRARY: libcxxabi -ORIGIN: null -TYPE: LicenseType.mit -FILE: ../../../third_party/libcxx/.arcconfig -FILE: ../../../third_party/libcxx/appveyor-reqs-install.cmd -FILE: ../../../third_party/libcxx/appveyor.yml -FILE: ../../../third_party/libcxx/benchmarks/CartesianBenchmarks.hpp -FILE: ../../../third_party/libcxx/benchmarks/ContainerBenchmarks.hpp -FILE: ../../../third_party/libcxx/benchmarks/GenerateInput.hpp -FILE: ../../../third_party/libcxx/benchmarks/algorithms.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/algorithms.partition_point.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/filesystem.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/function.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/lit.site.cfg.py.in -FILE: ../../../third_party/libcxx/benchmarks/ordered_set.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/string.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/stringstream.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/unordered_set_operations.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/util_smartptr.bench.cpp -FILE: ../../../third_party/libcxx/benchmarks/vector_operations.bench.cpp -FILE: ../../../third_party/libcxx/docs/BuildingLibcxx.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ABIVersioning.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/AvailabilityMarkup.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/CapturingConfigInfo.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/DebugMode.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ExperimentalFeatures.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/FeatureTestMacros.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/FileTimeType.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/ThreadingSupportAPI.rst -FILE: ../../../third_party/libcxx/docs/DesignDocs/VisibilityMacros.rst -FILE: ../../../third_party/libcxx/docs/FeatureTestMacroTable.rst -FILE: ../../../third_party/libcxx/docs/Makefile.sphinx -FILE: ../../../third_party/libcxx/docs/ReleaseNotes.rst -FILE: ../../../third_party/libcxx/docs/TestingLibcxx.rst -FILE: ../../../third_party/libcxx/docs/UsingLibcxx.rst -FILE: ../../../third_party/libcxx/docs/index.rst + +==================================================================================================== +LIBRARY: libcxx +LIBRARY: libcxxabi +ORIGIN: ../../../third_party/libcxx/benchmarks/CartesianBenchmarks.hpp +TYPE: LicenseType.apache +FILE: ../../../third_party/libcxx/benchmarks/CartesianBenchmarks.hpp +FILE: ../../../third_party/libcxx/benchmarks/function.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/ordered_set.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/util_smartptr.bench.cpp +FILE: ../../../third_party/libcxx/docs/DesignDocs/CapturingConfigInfo.rst FILE: ../../../third_party/libcxx/fuzzing/fuzz_test.cpp FILE: ../../../third_party/libcxx/fuzzing/fuzzing.cpp FILE: ../../../third_party/libcxx/fuzzing/fuzzing.h @@ -18694,12 +20122,10 @@ FILE: ../../../third_party/libcxx/include/__functional_03 FILE: ../../../third_party/libcxx/include/__functional_base FILE: ../../../third_party/libcxx/include/__functional_base_03 FILE: ../../../third_party/libcxx/include/__hash_table -FILE: ../../../third_party/libcxx/include/__libcpp_version FILE: ../../../third_party/libcxx/include/__locale FILE: ../../../third_party/libcxx/include/__mutex_base FILE: ../../../third_party/libcxx/include/__node_handle FILE: ../../../third_party/libcxx/include/__nullptr -FILE: ../../../third_party/libcxx/include/__split_buffer FILE: ../../../third_party/libcxx/include/__sso_allocator FILE: ../../../third_party/libcxx/include/__std_stream FILE: ../../../third_party/libcxx/include/__string @@ -18796,7 +20222,6 @@ FILE: ../../../third_party/libcxx/include/locale.h FILE: ../../../third_party/libcxx/include/map FILE: ../../../third_party/libcxx/include/math.h FILE: ../../../third_party/libcxx/include/memory -FILE: ../../../third_party/libcxx/include/module.modulemap FILE: ../../../third_party/libcxx/include/mutex FILE: ../../../third_party/libcxx/include/new FILE: ../../../third_party/libcxx/include/numeric @@ -18856,6 +20281,476 @@ FILE: ../../../third_party/libcxx/include/vector FILE: ../../../third_party/libcxx/include/version FILE: ../../../third_party/libcxx/include/wchar.h FILE: ../../../third_party/libcxx/include/wctype.h +FILE: ../../../third_party/libcxx/src/algorithm.cpp +FILE: ../../../third_party/libcxx/src/any.cpp +FILE: ../../../third_party/libcxx/src/bind.cpp +FILE: ../../../third_party/libcxx/src/charconv.cpp +FILE: ../../../third_party/libcxx/src/chrono.cpp +FILE: ../../../third_party/libcxx/src/condition_variable.cpp +FILE: ../../../third_party/libcxx/src/condition_variable_destructor.cpp +FILE: ../../../third_party/libcxx/src/debug.cpp +FILE: ../../../third_party/libcxx/src/exception.cpp +FILE: ../../../third_party/libcxx/src/experimental/memory_resource.cpp +FILE: ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp +FILE: ../../../third_party/libcxx/src/filesystem/filesystem_common.h +FILE: ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp +FILE: ../../../third_party/libcxx/src/filesystem/operations.cpp +FILE: ../../../third_party/libcxx/src/functional.cpp +FILE: ../../../third_party/libcxx/src/future.cpp +FILE: ../../../third_party/libcxx/src/hash.cpp +FILE: ../../../third_party/libcxx/src/include/apple_availability.h +FILE: ../../../third_party/libcxx/src/include/atomic_support.h +FILE: ../../../third_party/libcxx/src/include/config_elast.h +FILE: ../../../third_party/libcxx/src/include/refstring.h +FILE: ../../../third_party/libcxx/src/ios.cpp +FILE: ../../../third_party/libcxx/src/iostream.cpp +FILE: ../../../third_party/libcxx/src/locale.cpp +FILE: ../../../third_party/libcxx/src/memory.cpp +FILE: ../../../third_party/libcxx/src/mutex.cpp +FILE: ../../../third_party/libcxx/src/mutex_destructor.cpp +FILE: ../../../third_party/libcxx/src/new.cpp +FILE: ../../../third_party/libcxx/src/optional.cpp +FILE: ../../../third_party/libcxx/src/random.cpp +FILE: ../../../third_party/libcxx/src/regex.cpp +FILE: ../../../third_party/libcxx/src/shared_mutex.cpp +FILE: ../../../third_party/libcxx/src/stdexcept.cpp +FILE: ../../../third_party/libcxx/src/string.cpp +FILE: ../../../third_party/libcxx/src/strstream.cpp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp +FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp +FILE: ../../../third_party/libcxx/src/support/win32/locale_win32.cpp +FILE: ../../../third_party/libcxx/src/support/win32/support.cpp +FILE: ../../../third_party/libcxx/src/support/win32/thread_win32.cpp +FILE: ../../../third_party/libcxx/src/system_error.cpp +FILE: ../../../third_party/libcxx/src/thread.cpp +FILE: ../../../third_party/libcxx/src/typeinfo.cpp +FILE: ../../../third_party/libcxx/src/utility.cpp +FILE: ../../../third_party/libcxx/src/valarray.cpp +FILE: ../../../third_party/libcxx/src/variant.cpp +FILE: ../../../third_party/libcxx/src/vector.cpp +FILE: ../../../third_party/libcxxabi/include/__cxxabi_config.h +FILE: ../../../third_party/libcxxabi/include/cxxabi.h +FILE: ../../../third_party/libcxxabi/src/abort_message.cpp +FILE: ../../../third_party/libcxxabi/src/abort_message.h +FILE: ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_demangle.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_exception.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_exception.hpp +FILE: ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_guard.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_guard_impl.h +FILE: ../../../third_party/libcxxabi/src/cxa_handlers.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_handlers.hpp +FILE: ../../../third_party/libcxxabi/src/cxa_noexception.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_personality.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_unexpected.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_vector.cpp +FILE: ../../../third_party/libcxxabi/src/cxa_virtual.cpp +FILE: ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h +FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h +FILE: ../../../third_party/libcxxabi/src/demangle/StringView.h +FILE: ../../../third_party/libcxxabi/src/demangle/Utility.h +FILE: ../../../third_party/libcxxabi/src/fallback_malloc.cpp +FILE: ../../../third_party/libcxxabi/src/fallback_malloc.h +FILE: ../../../third_party/libcxxabi/src/include/atomic_support.h +FILE: ../../../third_party/libcxxabi/src/include/refstring.h +FILE: ../../../third_party/libcxxabi/src/private_typeinfo.cpp +FILE: ../../../third_party/libcxxabi/src/private_typeinfo.h +FILE: ../../../third_party/libcxxabi/src/stdlib_exception.cpp +FILE: ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp +FILE: ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp +FILE: ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp +---------------------------------------------------------------------------------------------------- +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +==================================================================================================== + +==================================================================================================== +LIBRARY: libcxx +LIBRARY: libcxxabi +ORIGIN: null +TYPE: LicenseType.bsd +FILE: ../../../third_party/libcxx/.arcconfig +FILE: ../../../third_party/libcxx/appveyor-reqs-install.cmd +FILE: ../../../third_party/libcxx/appveyor.yml +FILE: ../../../third_party/libcxx/benchmarks/ContainerBenchmarks.hpp +FILE: ../../../third_party/libcxx/benchmarks/GenerateInput.hpp +FILE: ../../../third_party/libcxx/benchmarks/algorithms.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/algorithms.partition_point.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/filesystem.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/lit.site.cfg.py.in +FILE: ../../../third_party/libcxx/benchmarks/string.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/stringstream.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/unordered_set_operations.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/vector_operations.bench.cpp +FILE: ../../../third_party/libcxx/docs/BuildingLibcxx.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ABIVersioning.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/AvailabilityMarkup.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/DebugMode.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ExperimentalFeatures.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/FeatureTestMacros.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/FileTimeType.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ThreadingSupportAPI.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/VisibilityMacros.rst +FILE: ../../../third_party/libcxx/docs/FeatureTestMacroTable.rst +FILE: ../../../third_party/libcxx/docs/Makefile.sphinx +FILE: ../../../third_party/libcxx/docs/ReleaseNotes.rst +FILE: ../../../third_party/libcxx/docs/TestingLibcxx.rst +FILE: ../../../third_party/libcxx/docs/UsingLibcxx.rst +FILE: ../../../third_party/libcxx/docs/index.rst +FILE: ../../../third_party/libcxx/include/__libcpp_version +FILE: ../../../third_party/libcxx/include/__split_buffer +FILE: ../../../third_party/libcxx/include/module.modulemap +FILE: ../../../third_party/libcxx/lib/abi/3.9/x86_64-apple-darwin16.abilist +FILE: ../../../third_party/libcxx/lib/abi/3.9/x86_64-linux-gnu.abilist +FILE: ../../../third_party/libcxx/lib/abi/4.0/x86_64-apple-darwin16.abilist +FILE: ../../../third_party/libcxx/lib/abi/4.0/x86_64-unknown-linux-gnu.abilist +FILE: ../../../third_party/libcxx/lib/abi/5.0/x86_64-apple-darwin16.abilist +FILE: ../../../third_party/libcxx/lib/abi/5.0/x86_64-unknown-linux-gnu.abilist +FILE: ../../../third_party/libcxx/lib/abi/6.0/x86_64-apple-darwin16.abilist +FILE: ../../../third_party/libcxx/lib/abi/6.0/x86_64-unknown-linux-gnu.abilist +FILE: ../../../third_party/libcxx/lib/abi/8.0/x86_64-apple-darwin.v1.abilist +FILE: ../../../third_party/libcxx/lib/abi/8.0/x86_64-apple-darwin.v2.abilist +FILE: ../../../third_party/libcxx/lib/abi/8.0/x86_64-unknown-linux-gnu.v1.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.v1.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-apple-darwin.v2.abilist +FILE: ../../../third_party/libcxx/lib/abi/x86_64-unknown-linux-gnu.v1.abilist +FILE: ../../../third_party/libcxx/lib/libc++abi-new-delete.exp +FILE: ../../../third_party/libcxx/lib/libc++abi.v1.exp +FILE: ../../../third_party/libcxx/lib/libc++abi.v2.exp +FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.v1.exp +FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.v2.exp +FILE: ../../../third_party/libcxx/lib/libc++unexp.exp +FILE: ../../../third_party/libcxx/lib/notweak.exp +FILE: ../../../third_party/libcxx/lib/weak.exp +FILE: ../../../third_party/libcxx/www/atomic_design.html +FILE: ../../../third_party/libcxx/www/atomic_design_a.html +FILE: ../../../third_party/libcxx/www/atomic_design_b.html +FILE: ../../../third_party/libcxx/www/atomic_design_c.html +FILE: ../../../third_party/libcxx/www/cxx1y_status.html +FILE: ../../../third_party/libcxx/www/cxx1z_status.html +FILE: ../../../third_party/libcxx/www/cxx2a_status.html +FILE: ../../../third_party/libcxx/www/index.html +FILE: ../../../third_party/libcxx/www/ts1z_status.html +FILE: ../../../third_party/libcxx/www/type_traits_design.html +FILE: ../../../third_party/libcxx/www/upcoming_meeting.html +FILE: ../../../third_party/libcxxabi/.arcconfig +FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp +FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp +FILE: ../../../third_party/libcxxabi/lib/new-delete.exp +FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp +FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp +FILE: ../../../third_party/libcxxabi/www/index.html +FILE: ../../../third_party/libcxxabi/www/spec.html +---------------------------------------------------------------------------------------------------- +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. +==================================================================================================== + +==================================================================================================== +LIBRARY: libcxx +LIBRARY: libcxxabi +ORIGIN: null +TYPE: LicenseType.mit +FILE: ../../../third_party/libcxx/.arcconfig +FILE: ../../../third_party/libcxx/appveyor-reqs-install.cmd +FILE: ../../../third_party/libcxx/appveyor.yml +FILE: ../../../third_party/libcxx/benchmarks/ContainerBenchmarks.hpp +FILE: ../../../third_party/libcxx/benchmarks/GenerateInput.hpp +FILE: ../../../third_party/libcxx/benchmarks/algorithms.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/algorithms.partition_point.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/filesystem.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/lit.site.cfg.py.in +FILE: ../../../third_party/libcxx/benchmarks/string.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/stringstream.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/unordered_set_operations.bench.cpp +FILE: ../../../third_party/libcxx/benchmarks/vector_operations.bench.cpp +FILE: ../../../third_party/libcxx/docs/BuildingLibcxx.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ABIVersioning.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/AvailabilityMarkup.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/DebugMode.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ExperimentalFeatures.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/FeatureTestMacros.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/FileTimeType.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ThreadingSupportAPI.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/VisibilityMacros.rst +FILE: ../../../third_party/libcxx/docs/FeatureTestMacroTable.rst +FILE: ../../../third_party/libcxx/docs/Makefile.sphinx +FILE: ../../../third_party/libcxx/docs/ReleaseNotes.rst +FILE: ../../../third_party/libcxx/docs/TestingLibcxx.rst +FILE: ../../../third_party/libcxx/docs/UsingLibcxx.rst +FILE: ../../../third_party/libcxx/docs/index.rst +FILE: ../../../third_party/libcxx/include/__libcpp_version +FILE: ../../../third_party/libcxx/include/__split_buffer +FILE: ../../../third_party/libcxx/include/module.modulemap FILE: ../../../third_party/libcxx/lib/abi/3.9/x86_64-apple-darwin16.abilist FILE: ../../../third_party/libcxx/lib/abi/3.9/x86_64-linux-gnu.abilist FILE: ../../../third_party/libcxx/lib/abi/4.0/x86_64-apple-darwin16.abilist @@ -18878,63 +20773,6 @@ FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.v2.exp FILE: ../../../third_party/libcxx/lib/libc++unexp.exp FILE: ../../../third_party/libcxx/lib/notweak.exp FILE: ../../../third_party/libcxx/lib/weak.exp -FILE: ../../../third_party/libcxx/src/algorithm.cpp -FILE: ../../../third_party/libcxx/src/any.cpp -FILE: ../../../third_party/libcxx/src/bind.cpp -FILE: ../../../third_party/libcxx/src/charconv.cpp -FILE: ../../../third_party/libcxx/src/chrono.cpp -FILE: ../../../third_party/libcxx/src/condition_variable.cpp -FILE: ../../../third_party/libcxx/src/condition_variable_destructor.cpp -FILE: ../../../third_party/libcxx/src/debug.cpp -FILE: ../../../third_party/libcxx/src/exception.cpp -FILE: ../../../third_party/libcxx/src/experimental/memory_resource.cpp -FILE: ../../../third_party/libcxx/src/filesystem/directory_iterator.cpp -FILE: ../../../third_party/libcxx/src/filesystem/filesystem_common.h -FILE: ../../../third_party/libcxx/src/filesystem/int128_builtins.cpp -FILE: ../../../third_party/libcxx/src/filesystem/operations.cpp -FILE: ../../../third_party/libcxx/src/functional.cpp -FILE: ../../../third_party/libcxx/src/future.cpp -FILE: ../../../third_party/libcxx/src/hash.cpp -FILE: ../../../third_party/libcxx/src/include/apple_availability.h -FILE: ../../../third_party/libcxx/src/include/atomic_support.h -FILE: ../../../third_party/libcxx/src/include/config_elast.h -FILE: ../../../third_party/libcxx/src/include/refstring.h -FILE: ../../../third_party/libcxx/src/ios.cpp -FILE: ../../../third_party/libcxx/src/iostream.cpp -FILE: ../../../third_party/libcxx/src/locale.cpp -FILE: ../../../third_party/libcxx/src/memory.cpp -FILE: ../../../third_party/libcxx/src/mutex.cpp -FILE: ../../../third_party/libcxx/src/mutex_destructor.cpp -FILE: ../../../third_party/libcxx/src/new.cpp -FILE: ../../../third_party/libcxx/src/optional.cpp -FILE: ../../../third_party/libcxx/src/random.cpp -FILE: ../../../third_party/libcxx/src/regex.cpp -FILE: ../../../third_party/libcxx/src/shared_mutex.cpp -FILE: ../../../third_party/libcxx/src/stdexcept.cpp -FILE: ../../../third_party/libcxx/src/string.cpp -FILE: ../../../third_party/libcxx/src/strstream.cpp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_fallback.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_glibcxx.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxabi.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_libcxxrt.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_msvc.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_cxxabi.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_glibcxx.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_msvc.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/exception_pointer_unimplemented.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/new_handler_fallback.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_default.ipp -FILE: ../../../third_party/libcxx/src/support/runtime/stdexcept_vcruntime.ipp -FILE: ../../../third_party/libcxx/src/support/win32/locale_win32.cpp -FILE: ../../../third_party/libcxx/src/support/win32/support.cpp -FILE: ../../../third_party/libcxx/src/support/win32/thread_win32.cpp -FILE: ../../../third_party/libcxx/src/system_error.cpp -FILE: ../../../third_party/libcxx/src/thread.cpp -FILE: ../../../third_party/libcxx/src/typeinfo.cpp -FILE: ../../../third_party/libcxx/src/utility.cpp -FILE: ../../../third_party/libcxx/src/valarray.cpp -FILE: ../../../third_party/libcxx/src/variant.cpp -FILE: ../../../third_party/libcxx/src/vector.cpp FILE: ../../../third_party/libcxx/www/atomic_design.html FILE: ../../../third_party/libcxx/www/atomic_design_a.html FILE: ../../../third_party/libcxx/www/atomic_design_b.html @@ -18948,44 +20786,10 @@ FILE: ../../../third_party/libcxx/www/type_traits_design.html FILE: ../../../third_party/libcxx/www/upcoming_meeting.html FILE: ../../../third_party/libcxxabi/.arcconfig FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp -FILE: ../../../third_party/libcxxabi/include/__cxxabi_config.h -FILE: ../../../third_party/libcxxabi/include/cxxabi.h FILE: ../../../third_party/libcxxabi/lib/itanium-base.exp FILE: ../../../third_party/libcxxabi/lib/new-delete.exp FILE: ../../../third_party/libcxxabi/lib/personality-sjlj.exp FILE: ../../../third_party/libcxxabi/lib/personality-v0.exp -FILE: ../../../third_party/libcxxabi/src/abort_message.cpp -FILE: ../../../third_party/libcxxabi/src/abort_message.h -FILE: ../../../third_party/libcxxabi/src/cxa_aux_runtime.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_default_handlers.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_demangle.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception.hpp -FILE: ../../../third_party/libcxxabi/src/cxa_exception_storage.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_guard.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_guard_impl.h -FILE: ../../../third_party/libcxxabi/src/cxa_handlers.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_handlers.hpp -FILE: ../../../third_party/libcxxabi/src/cxa_noexception.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_personality.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_thread_atexit.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_unexpected.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_vector.cpp -FILE: ../../../third_party/libcxxabi/src/cxa_virtual.cpp -FILE: ../../../third_party/libcxxabi/src/demangle/DemangleConfig.h -FILE: ../../../third_party/libcxxabi/src/demangle/ItaniumDemangle.h -FILE: ../../../third_party/libcxxabi/src/demangle/StringView.h -FILE: ../../../third_party/libcxxabi/src/demangle/Utility.h -FILE: ../../../third_party/libcxxabi/src/fallback_malloc.cpp -FILE: ../../../third_party/libcxxabi/src/fallback_malloc.h -FILE: ../../../third_party/libcxxabi/src/include/atomic_support.h -FILE: ../../../third_party/libcxxabi/src/include/refstring.h -FILE: ../../../third_party/libcxxabi/src/private_typeinfo.cpp -FILE: ../../../third_party/libcxxabi/src/private_typeinfo.h -FILE: ../../../third_party/libcxxabi/src/stdlib_exception.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_new_delete.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_stdexcept.cpp -FILE: ../../../third_party/libcxxabi/src/stdlib_typeinfo.cpp FILE: ../../../third_party/libcxxabi/www/index.html FILE: ../../../third_party/libcxxabi/www/spec.html ---------------------------------------------------------------------------------------------------- @@ -20464,7 +22268,6 @@ POSSIBILITY OF SUCH DAMAGE. LIBRARY: libpng ORIGIN: ../../../third_party/libpng/LICENSE TYPE: LicenseType.libpng -FILE: ../../../third_party/libpng/contrib/intel/intel_sse.patch FILE: ../../../third_party/libpng/pngprefix.h ---------------------------------------------------------------------------------------------------- @@ -20477,9 +22280,9 @@ TYPE: LicenseType.libpng FILE: ../../../third_party/libpng/arm/arm_init.c FILE: ../../../third_party/libpng/arm/filter_neon.S FILE: ../../../third_party/libpng/arm/filter_neon_intrinsics.c -FILE: ../../../third_party/libpng/contrib/intel/filter_sse2_intrinsics.c -FILE: ../../../third_party/libpng/contrib/intel/intel_init.c -FILE: ../../../third_party/libpng/contrib/intel/intel_sse.patch +FILE: ../../../third_party/libpng/arm/palette_neon_intrinsics.c +FILE: ../../../third_party/libpng/intel/filter_sse2_intrinsics.c +FILE: ../../../third_party/libpng/intel/intel_init.c FILE: ../../../third_party/libpng/png.c FILE: ../../../third_party/libpng/pngconf.h FILE: ../../../third_party/libpng/pngdebug.h @@ -20512,6 +22315,7 @@ ORIGIN: ../../../third_party/libwebp/COPYING TYPE: LicenseType.bsd FILE: ../../../third_party/libwebp/.mailmap FILE: ../../../third_party/libwebp/Makefile.vc +FILE: ../../../third_party/libwebp/cmake/WebPConfig.cmake.in FILE: ../../../third_party/libwebp/cmake/config.h.in FILE: ../../../third_party/libwebp/doc/TODO FILE: ../../../third_party/libwebp/doc/template.html @@ -20526,6 +22330,10 @@ FILE: ../../../third_party/libwebp/src/libwebpdecoder.pc.in FILE: ../../../third_party/libwebp/src/libwebpdecoder.rc FILE: ../../../third_party/libwebp/src/mux/libwebpmux.pc.in FILE: ../../../third_party/libwebp/src/mux/libwebpmux.rc +FILE: ../../../third_party/libwebp/webp_js/index.html +FILE: ../../../third_party/libwebp/webp_js/index_wasm.html +FILE: ../../../third_party/libwebp/webp_js/test_webp_js.webp +FILE: ../../../third_party/libwebp/webp_js/test_webp_wasm.webp ---------------------------------------------------------------------------------------------------- Copyright (c) 2010, Google Inc. All rights reserved. @@ -20579,8 +22387,6 @@ FILE: ../../../third_party/libwebp/src/dsp/lossless_enc_sse2.c FILE: ../../../third_party/libwebp/src/dsp/lossless_enc_sse41.c FILE: ../../../third_party/libwebp/src/dsp/rescaler_neon.c FILE: ../../../third_party/libwebp/src/dsp/rescaler_sse2.c -FILE: ../../../third_party/libwebp/src/enc/delta_palettization_enc.c -FILE: ../../../third_party/libwebp/src/enc/delta_palettization_enc.h ---------------------------------------------------------------------------------------------------- Copyright 2015 Google Inc. All Rights Reserved. @@ -20626,6 +22432,7 @@ FILE: ../../../third_party/libwebp/imageio/image_enc.h FILE: ../../../third_party/libwebp/imageio/imageio_util.c FILE: ../../../third_party/libwebp/imageio/imageio_util.h FILE: ../../../third_party/libwebp/src/dsp/common_sse2.h +FILE: ../../../third_party/libwebp/src/dsp/common_sse41.h FILE: ../../../third_party/libwebp/src/dsp/dec_msa.c FILE: ../../../third_party/libwebp/src/dsp/enc_msa.c FILE: ../../../third_party/libwebp/src/dsp/filters_msa.c @@ -20668,6 +22475,53 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: libwebp +ORIGIN: ../../../third_party/libwebp/extras/vwebp_sdl.c + ../../../third_party/libwebp/COPYING +TYPE: LicenseType.bsd +FILE: ../../../third_party/libwebp/extras/vwebp_sdl.c +FILE: ../../../third_party/libwebp/extras/webp_to_sdl.c +FILE: ../../../third_party/libwebp/extras/webp_to_sdl.h +FILE: ../../../third_party/libwebp/imageio/pnmdec.c +FILE: ../../../third_party/libwebp/imageio/pnmdec.h +FILE: ../../../third_party/libwebp/src/dsp/alpha_processing_neon.c +FILE: ../../../third_party/libwebp/src/dsp/filters_neon.c +FILE: ../../../third_party/libwebp/src/dsp/ssim.c +FILE: ../../../third_party/libwebp/src/dsp/ssim_sse2.c +FILE: ../../../third_party/libwebp/src/dsp/yuv_neon.c +FILE: ../../../third_party/libwebp/src/enc/backward_references_cost_enc.c +---------------------------------------------------------------------------------------------------- +Copyright 2017 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 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 +HOLDER 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: libwebp ORIGIN: ../../../third_party/libwebp/imageio/jpegdec.c + ../../../third_party/libwebp/COPYING @@ -20745,16 +22599,12 @@ FILE: ../../../third_party/libwebp/imageio/webpdec.c FILE: ../../../third_party/libwebp/imageio/webpdec.h FILE: ../../../third_party/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c FILE: ../../../third_party/libwebp/src/dsp/alpha_processing_sse2.c -FILE: ../../../third_party/libwebp/src/dsp/argb.c -FILE: ../../../third_party/libwebp/src/dsp/argb_mips_dsp_r2.c -FILE: ../../../third_party/libwebp/src/dsp/argb_sse2.c FILE: ../../../third_party/libwebp/src/dsp/cost.c FILE: ../../../third_party/libwebp/src/dsp/cost_mips32.c FILE: ../../../third_party/libwebp/src/dsp/cost_mips_dsp_r2.c FILE: ../../../third_party/libwebp/src/dsp/dec_clip_tables.c FILE: ../../../third_party/libwebp/src/dsp/dec_mips32.c FILE: ../../../third_party/libwebp/src/dsp/dec_mips_dsp_r2.c -FILE: ../../../third_party/libwebp/src/dsp/enc_avx2.c FILE: ../../../third_party/libwebp/src/dsp/enc_mips32.c FILE: ../../../third_party/libwebp/src/dsp/enc_mips_dsp_r2.c FILE: ../../../third_party/libwebp/src/dsp/filters_mips_dsp_r2.c @@ -20770,6 +22620,7 @@ FILE: ../../../third_party/libwebp/src/dsp/upsampling_mips_dsp_r2.c FILE: ../../../third_party/libwebp/src/dsp/yuv_mips32.c FILE: ../../../third_party/libwebp/src/dsp/yuv_mips_dsp_r2.c FILE: ../../../third_party/libwebp/src/dsp/yuv_sse2.c +FILE: ../../../third_party/libwebp/src/dsp/yuv_sse41.c FILE: ../../../third_party/libwebp/src/enc/near_lossless_enc.c FILE: ../../../third_party/libwebp/src/enc/picture_csp_enc.c FILE: ../../../third_party/libwebp/src/enc/picture_psnr_enc.c @@ -20872,6 +22723,7 @@ FILE: ../../../third_party/libwebp/src/dsp/filters.c FILE: ../../../third_party/libwebp/src/dsp/upsampling.c FILE: ../../../third_party/libwebp/src/dsp/upsampling_neon.c FILE: ../../../third_party/libwebp/src/dsp/upsampling_sse2.c +FILE: ../../../third_party/libwebp/src/dsp/upsampling_sse41.c FILE: ../../../third_party/libwebp/src/enc/alpha_enc.c FILE: ../../../third_party/libwebp/src/enc/analysis_enc.c FILE: ../../../third_party/libwebp/src/enc/config_enc.c @@ -20987,12 +22839,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: libwebp -ORIGIN: ../../../third_party/libwebp/src/dsp/alpha_processing_neon.c + ../../../third_party/libwebp/COPYING +ORIGIN: ../../../third_party/libwebp/src/dsp/cost_neon.c + ../../../third_party/libwebp/COPYING TYPE: LicenseType.bsd -FILE: ../../../third_party/libwebp/src/dsp/alpha_processing_neon.c -FILE: ../../../third_party/libwebp/src/dsp/filters_neon.c +FILE: ../../../third_party/libwebp/src/dsp/cost_neon.c +FILE: ../../../third_party/libwebp/src/dsp/quant.h ---------------------------------------------------------------------------------------------------- -Copyright 2017 Google Inc. All Rights Reserved. +Copyright 2018 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 @@ -21208,52 +23060,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================================================== -==================================================================================================== -LIBRARY: rapidjson -ORIGIN: ../../../third_party/rapidjson/include/rapidjson/error/en.h -TYPE: LicenseType.mit -FILE: ../../../third_party/rapidjson/include/rapidjson/allocators.h -FILE: ../../../third_party/rapidjson/include/rapidjson/cursorstreamwrapper.h -FILE: ../../../third_party/rapidjson/include/rapidjson/document.h -FILE: ../../../third_party/rapidjson/include/rapidjson/encodedstream.h -FILE: ../../../third_party/rapidjson/include/rapidjson/encodings.h -FILE: ../../../third_party/rapidjson/include/rapidjson/error/en.h -FILE: ../../../third_party/rapidjson/include/rapidjson/error/error.h -FILE: ../../../third_party/rapidjson/include/rapidjson/filereadstream.h -FILE: ../../../third_party/rapidjson/include/rapidjson/filewritestream.h -FILE: ../../../third_party/rapidjson/include/rapidjson/fwd.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/biginteger.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/diyfp.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/dtoa.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/ieee754.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/itoa.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/meta.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/pow10.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/regex.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/stack.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/strfunc.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/strtod.h -FILE: ../../../third_party/rapidjson/include/rapidjson/internal/swap.h -FILE: ../../../third_party/rapidjson/include/rapidjson/istreamwrapper.h -FILE: ../../../third_party/rapidjson/include/rapidjson/memorybuffer.h -FILE: ../../../third_party/rapidjson/include/rapidjson/memorystream.h -FILE: ../../../third_party/rapidjson/include/rapidjson/ostreamwrapper.h -FILE: ../../../third_party/rapidjson/include/rapidjson/pointer.h -FILE: ../../../third_party/rapidjson/include/rapidjson/prettywriter.h -FILE: ../../../third_party/rapidjson/include/rapidjson/rapidjson.h -FILE: ../../../third_party/rapidjson/include/rapidjson/reader.h -FILE: ../../../third_party/rapidjson/include/rapidjson/schema.h -FILE: ../../../third_party/rapidjson/include/rapidjson/stream.h -FILE: ../../../third_party/rapidjson/include/rapidjson/stringbuffer.h -FILE: ../../../third_party/rapidjson/include/rapidjson/writer.h ----------------------------------------------------------------------------------------------------- -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==================================================================================================== - ==================================================================================================== LIBRARY: rapidjson ORIGIN: ../../../third_party/rapidjson/include/rapidjson/msinttypes/inttypes.h @@ -22072,41 +23878,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: wasmer -ORIGIN: ../../../third_party/dart/third_party/wasmer/LICENSE -TYPE: LicenseType.mit -FILE: ../../../third_party/dart/third_party/wasmer/Cargo.toml -FILE: ../../../third_party/dart/third_party/wasmer/wasmer.hh -FILE: ../../../third_party/dart/third_party/wasmer/wasmer.rs ----------------------------------------------------------------------------------------------------- -MIT License - -Copyright (c) 2019-present Wasmer, Inc. and its affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -==================================================================================================== - ==================================================================================================== LIBRARY: wuffs ORIGIN: ../../../third_party/wuffs/LICENSE TYPE: LicenseType.apache +FILE: ../../../third_party/wuffs/doc/logo/wuffs-acronym-logo-1536x1024.png +FILE: ../../../third_party/wuffs/doc/logo/wuffs-logo-1024x724.png +FILE: ../../../third_party/wuffs/doc/logo/wuffs-logo-2048x1448.png +FILE: ../../../third_party/wuffs/doc/logo/wuffs-logo-256x181.png +FILE: ../../../third_party/wuffs/doc/logo/wuffs-logo.svg FILE: ../../../third_party/wuffs/go.mod FILE: ../../../third_party/wuffs/go.sum FILE: ../../../third_party/wuffs/script/bench-rust-deflate/Cargo.toml @@ -22385,6 +24165,8 @@ FILE: ../../../third_party/zlib/patches/0002-uninitializedcheck.patch FILE: ../../../third_party/zlib/patches/0003-uninitializedjump.patch FILE: ../../../third_party/zlib/patches/0004-fix-uwp.patch FILE: ../../../third_party/zlib/patches/0005-infcover-gtest.patch +FILE: ../../../third_party/zlib/patches/0006-fix-check_match.patch +FILE: ../../../third_party/zlib/patches/0007-zero-init-deflate-window.patch FILE: ../../../third_party/zlib/trees.h ---------------------------------------------------------------------------------------------------- version 1.2.11, January 15th, 2017 @@ -23119,4 +24901,4 @@ freely, subject to the following restrictions: misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ==================================================================================================== -Total license count: 367 +Total license count: 376 diff --git a/ci/licenses_golden/tool_signature b/ci/licenses_golden/tool_signature index 710e27a49734a..10c876e21dff2 100644 --- a/ci/licenses_golden/tool_signature +++ b/ci/licenses_golden/tool_signature @@ -1,2 +1,2 @@ -Signature: af78da7ecf2921dc867b7088d451f3af +Signature: 2b08c736bc3af2c5d770099c53112c83 diff --git a/ci/lint.sh b/ci/lint.sh index e2698f255f7b5..eeddd3c7b4ce3 100755 --- a/ci/lint.sh +++ b/ci/lint.sh @@ -31,17 +31,16 @@ SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")") SRC_DIR="$(cd "$SCRIPT_DIR/../.."; pwd -P)" DART_BIN="${SRC_DIR}/third_party/dart/tools/sdks/dart-sdk/bin" DART="${DART_BIN}/dart" -PUB="${DART_BIN}/pub" -COMPILE_COMMANDS="$SRC_DIR/out/compile_commands.json" +COMPILE_COMMANDS="$SRC_DIR/out/host_debug/compile_commands.json" if [ ! -f "$COMPILE_COMMANDS" ]; then (cd "$SRC_DIR"; ./flutter/tools/gn) fi cd "$SCRIPT_DIR" -"$PUB" get && "$DART" \ +"$DART" \ --disable-dart-dev \ - bin/lint.dart \ + "$SRC_DIR/flutter/tools/clang_tidy/bin/main.dart" \ --compile-commands="$COMPILE_COMMANDS" \ --repo="$SRC_DIR/flutter" \ "$@" diff --git a/ci/pubspec.yaml b/ci/pubspec.yaml index eba8dd49bfbbf..b9681b29bf177 100644 --- a/ci/pubspec.yaml +++ b/ci/pubspec.yaml @@ -3,12 +3,41 @@ # found in the LICENSE file. name: ci_scripts +publish_to: none +environment: + sdk: '>=2.12.0-0 <3.0.0' + +# Do not add any dependencies that require more than what is provided in +# //third_party/pkg, //third_party/dart/pkg, or +# //third_party/dart/third_party/pkg. In particular, package:test is not usable +# here. + +# If you do add packages here, make sure you can run `pub get --offline`, and +# check the .packages and .package_config to make sure all the paths are +# relative to this directory into //third_party/dart dependencies: - args: ^1.6.0 - path: ^1.7.0 - isolate: ^2.0.3 - process_runner: ^3.0.0 + args: any + meta: any + path: any + process_runner: any -environment: - sdk: '>=2.8.0 <3.0.0' +dependency_overrides: + args: + path: ../../third_party/dart/third_party/pkg/args + async: + path: ../../third_party/dart/third_party/pkg/async + collection: + path: ../../third_party/dart/third_party/pkg/collection + file: + path: ../../third_party/pkg/file/packages/file + meta: + path: ../../third_party/dart/pkg/meta + path: + path: ../../third_party/dart/third_party/pkg/path + platform: + path: ../../third_party/pkg/platform + process: + path: ../../third_party/pkg/process + process_runner: + path: ../../third_party/pkg/process_runner diff --git a/common/config.gni b/common/config.gni index f7b122a3f480e..11b4ba0c9288b 100644 --- a/common/config.gni +++ b/common/config.gni @@ -14,11 +14,14 @@ declare_args() { # The runtime mode ("debug", "profile", "release", or "jit_release") flutter_runtime_mode = "debug" - # Whether to use the Skia text shaper module + # Whether to link the Skia text shaper module into the engine flutter_enable_skshaper = false - # Whether to use the legacy embedder when building for Fuchsia. - flutter_enable_legacy_fuchsia_embedder = true + # Whether to use the Skia text shaper module for all text rendering + flutter_always_use_skshaper = false + + # Whether to use a prebuilt Dart SDK instead of building one. + flutter_prebuilt_dart_sdk = false } # feature_defines_list --------------------------------------------------------- @@ -28,6 +31,7 @@ feature_defines_list = [ "FLUTTER_RUNTIME_MODE_PROFILE=2", "FLUTTER_RUNTIME_MODE_RELEASE=3", "FLUTTER_RUNTIME_MODE_JIT_RELEASE=4", + "DART_LEGACY_API=[[deprecated]]", ] if (flutter_runtime_mode == "debug") { @@ -58,4 +62,20 @@ if (is_ios || is_mac) { "-Werror=undeclared-selector", ] flutter_cflags_objcc = flutter_cflags_objc + flutter_cflags_objc_arc = flutter_cflags_objc + [ "-fobjc-arc" ] + flutter_cflags_objcc_arc = flutter_cflags_objc_arc +} + +# prebuilt Dart SDK location + +if (flutter_prebuilt_dart_sdk) { + _os_name = target_os + if (_os_name == "mac") { + _os_name = "macos" + } else if (_os_name == "win" || _os_name == "winuwp") { + _os_name = "windows" + } + target_prebuilt_dart_sdk = + "//flutter/prebuilts/$_os_name-$target_cpu/dart-sdk" + host_prebuilt_dart_sdk = "//flutter/prebuilts/$_os_name-$host_cpu/dart-sdk" } diff --git a/common/graphics/BUILD.gn b/common/graphics/BUILD.gn new file mode 100644 index 0000000000000..5afc6cae533ed --- /dev/null +++ b/common/graphics/BUILD.gn @@ -0,0 +1,30 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/common/config.gni") +import("//flutter/testing/testing.gni") + +source_set("graphics") { + sources = [ + "gl_context_switch.cc", + "gl_context_switch.h", + "persistent_cache.cc", + "persistent_cache.h", + "texture.cc", + "texture.h", + ] + + # Heed caution when adding targets to the dependencies. This is a minimal + # target containing graphics sources that embedders can depend on. Any + # additions here could result in added app sizes across embeddings. + deps = [ + "//flutter/assets", + "//flutter/fml", + "//flutter/shell/version:version", + "//third_party/rapidjson", + "//third_party/skia", + ] + + public_configs = [ "//flutter:config" ] +} diff --git a/flow/gl_context_switch.cc b/common/graphics/gl_context_switch.cc similarity index 94% rename from flow/gl_context_switch.cc rename to common/graphics/gl_context_switch.cc index bace0ebee4698..71ab99c5654b8 100644 --- a/flow/gl_context_switch.cc +++ b/common/graphics/gl_context_switch.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/gl_context_switch.h" +#include "flutter/common/graphics/gl_context_switch.h" namespace flutter { diff --git a/flow/gl_context_switch.h b/common/graphics/gl_context_switch.h similarity index 95% rename from flow/gl_context_switch.h rename to common/graphics/gl_context_switch.h index 35976f7325f23..18710ce627a60 100644 --- a/flow/gl_context_switch.h +++ b/common/graphics/gl_context_switch.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_FLOW_GL_CONTEXT_SWITCH_H_ -#define FLUTTER_FLOW_GL_CONTEXT_SWITCH_H_ +#ifndef FLUTTER_COMMON_GRAPHICS_GL_CONTEXT_SWITCH_H_ +#define FLUTTER_COMMON_GRAPHICS_GL_CONTEXT_SWITCH_H_ #include #include @@ -111,4 +111,4 @@ class GLContextSwitch final : public GLContextResult { } // namespace flutter -#endif // FLUTTER_FLOW_GL_CONTEXT_SWITCH_H_ +#endif // FLUTTER_COMMON_GRAPHICS_GL_CONTEXT_SWITCH_H_ diff --git a/shell/common/persistent_cache.cc b/common/graphics/persistent_cache.cc similarity index 79% rename from shell/common/persistent_cache.cc rename to common/graphics/persistent_cache.cc index ce6f624019a4b..69c906fdd3fd8 100644 --- a/shell/common/persistent_cache.cc +++ b/common/graphics/persistent_cache.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/common/persistent_cache.h" +#include "flutter/common/graphics/persistent_cache.h" #include #include @@ -18,6 +18,7 @@ #include "flutter/fml/trace_event.h" #include "flutter/shell/version/version.h" #include "rapidjson/document.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/utils/SkBase64.h" namespace flutter { @@ -85,15 +86,24 @@ bool PersistentCache::Purge() { FML_CHECK(GetWorkerTaskRunner()); std::promise removed; - GetWorkerTaskRunner()->PostTask( - [&removed, cache_directory = cache_directory_]() { - if (cache_directory->is_valid()) { - FML_LOG(INFO) << "Purge persistent cache."; - removed.set_value(RemoveFilesInDirectory(*cache_directory)); - } else { - removed.set_value(false); + GetWorkerTaskRunner()->PostTask([&removed, + cache_directory = cache_directory_]() { + if (cache_directory->is_valid()) { + // Only remove files but not directories. + FML_LOG(INFO) << "Purge persistent cache."; + fml::FileVisitor delete_file = [](const fml::UniqueFD& directory, + const std::string& filename) { + // Do not delete directories. Return true to continue with other files. + if (fml::IsDirectory(directory, filename.c_str())) { + return true; } - }); + return fml::UnlinkFile(directory, filename.c_str()); + }; + removed.set_value(VisitFilesRecursively(*cache_directory, delete_file)); + } else { + removed.set_value(false); + } + }); return removed.get_future().get(); } @@ -160,17 +170,53 @@ sk_sp ParseBase32(const std::string& input) { } sk_sp ParseBase64(const std::string& input) { - SkBase64 decoder; - auto error = decoder.decode(input.c_str(), input.length()); + SkBase64::Error error; + + size_t output_len; + error = SkBase64::Decode(input.c_str(), input.length(), nullptr, &output_len); if (error != SkBase64::Error::kNoError) { FML_LOG(ERROR) << "Base64 decode error: " << error; FML_LOG(ERROR) << "Base64 can't decode: " << input; return nullptr; } - return SkData::MakeWithCopy(decoder.getData(), decoder.getDataSize()); + + sk_sp data = SkData::MakeUninitialized(output_len); + void* output = data->writable_data(); + error = SkBase64::Decode(input.c_str(), input.length(), output, &output_len); + if (error != SkBase64::Error::kNoError) { + FML_LOG(ERROR) << "Base64 decode error: " << error; + FML_LOG(ERROR) << "Base64 can't decode: " << input; + return nullptr; + } + + return data; +} + +size_t PersistentCache::PrecompileKnownSkSLs(GrDirectContext* context) const { + auto known_sksls = LoadSkSLs(); + // A trace must be present even if no precompilations have been completed. + FML_TRACE_EVENT("flutter", "PersistentCache::PrecompileKnownSkSLs", "count", + known_sksls.size()); + + if (context == nullptr) { + return 0; + } + + size_t precompiled_count = 0; + for (const auto& sksl : known_sksls) { + TRACE_EVENT0("flutter", "PrecompilingSkSL"); + if (context->precompileShader(*sksl.first, *sksl.second)) { + precompiled_count++; + } + } + + FML_TRACE_COUNTER("flutter", "PersistentCache::PrecompiledSkSLs", + reinterpret_cast(this), // Trace Counter ID + "Successful", precompiled_count); + return precompiled_count; } -std::vector PersistentCache::LoadSkSLs() { +std::vector PersistentCache::LoadSkSLs() const { TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs"); std::vector result; fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory, @@ -279,20 +325,18 @@ static void PersistentCacheStore(fml::RefPtr worker, std::shared_ptr cache_directory, std::string key, std::unique_ptr value) { - auto task = - fml::MakeCopyable([cache_directory, // - file_name = std::move(key), // - mapping = std::move(value) // + auto task = fml::MakeCopyable([cache_directory, // + file_name = std::move(key), // + mapping = std::move(value) // ]() mutable { - TRACE_EVENT0("flutter", "PersistentCacheStore"); - if (!fml::WriteAtomically(*cache_directory, // - file_name.c_str(), // - *mapping) // - ) { - FML_DLOG(WARNING) - << "Could not write cache contents to persistent store."; - } - }); + TRACE_EVENT0("flutter", "PersistentCacheStore"); + if (!fml::WriteAtomically(*cache_directory, // + file_name.c_str(), // + *mapping) // + ) { + FML_LOG(WARNING) << "Could not write cache contents to persistent store."; + } + }); if (!worker) { FML_LOG(WARNING) @@ -384,4 +428,14 @@ void PersistentCache::SetAssetManager(std::shared_ptr value) { asset_manager_ = value; } +std::vector> +PersistentCache::GetSkpsFromAssetManager() const { + if (!asset_manager_) { + FML_LOG(ERROR) + << "PersistentCache::GetSkpsFromAssetManager: Asset manager not set!"; + return std::vector>(); + } + return asset_manager_->GetAsMappings(".*\\.skp$", "shaders"); +} + } // namespace flutter diff --git a/shell/common/persistent_cache.h b/common/graphics/persistent_cache.h similarity index 78% rename from shell/common/persistent_cache.h rename to common/graphics/persistent_cache.h index 905eb9b55faf4..00b6c299b58f3 100644 --- a/shell/common/persistent_cache.h +++ b/common/graphics/persistent_cache.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ -#define FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ +#ifndef FLUTTER_COMMON_GRAPHICS_PERSISTENT_CACHE_H_ +#define FLUTTER_COMMON_GRAPHICS_PERSISTENT_CACHE_H_ #include #include @@ -17,6 +17,10 @@ namespace flutter { +namespace testing { +class ShellTest; +} + /// A cache of SkData that gets stored to disk. /// /// This is mainly used for Shaders but is also written to by Dart. It is @@ -68,14 +72,37 @@ class PersistentCache : public GrContextOptions::PersistentCache { using SkSLCache = std::pair, sk_sp>; /// Load all the SkSL shader caches in the right directory. - std::vector LoadSkSLs(); + std::vector LoadSkSLs() const; + + //---------------------------------------------------------------------------- + /// @brief Precompile SkSLs packaged with the application and gathered + /// during previous runs in the given context. + /// + /// @warning The context must be the rendering context. This context may be + /// destroyed during application suspension and subsequently + /// recreated. The SkSLs must be precompiled again in the new + /// context. + /// + /// @param context The rendering context to precompile shaders in. + /// + /// @return The number of SkSLs precompiled. + /// + size_t PrecompileKnownSkSLs(GrDirectContext* context) const; + + // Return mappings for all skp's accessible through the AssetManager + std::vector> GetSkpsFromAssetManager() const; /// Set the asset manager from which PersistentCache can load SkLSs. A nullptr /// can be provided to clear the asset manager. static void SetAssetManager(std::shared_ptr value); + static std::shared_ptr asset_manager() { + return asset_manager_; + } static bool cache_sksl() { return cache_sksl_; } + static void SetCacheSkSL(bool value); + static void MarkStrategySet() { strategy_set_ = true; } static constexpr char kSkSLSubdirName[] = "sksl"; @@ -120,9 +147,11 @@ class PersistentCache : public GrContextOptions::PersistentCache { fml::RefPtr GetWorkerTaskRunner() const; + friend class testing::ShellTest; + FML_DISALLOW_COPY_AND_ASSIGN(PersistentCache); }; } // namespace flutter -#endif // FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ +#endif // FLUTTER_COMMON_GRAPHICS_PERSISTENT_CACHE_H_ diff --git a/flow/texture.cc b/common/graphics/texture.cc similarity index 96% rename from flow/texture.cc rename to common/graphics/texture.cc index c81314bf017fe..c5ad5fdf29af8 100644 --- a/flow/texture.cc +++ b/common/graphics/texture.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/texture.h" +#include "flutter/common/graphics/texture.h" namespace flutter { diff --git a/flow/texture.h b/common/graphics/texture.h similarity index 80% rename from flow/texture.h rename to common/graphics/texture.h index dd136942d36ec..5ed8dafa668f4 100644 --- a/flow/texture.h +++ b/common/graphics/texture.h @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_FLOW_TEXTURE_H_ -#define FLUTTER_FLOW_TEXTURE_H_ +#ifndef FLUTTER_COMMON_GRAPHICS_TEXTURE_H_ +#define FLUTTER_COMMON_GRAPHICS_TEXTURE_H_ #include #include "flutter/fml/macros.h" #include "flutter/fml/synchronization/waitable_event.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSamplingOptions.h" class GrDirectContext; @@ -17,15 +18,15 @@ namespace flutter { class Texture { public: - Texture(int64_t id); // Called from UI or raster thread. - virtual ~Texture(); // Called from raster thread. + explicit Texture(int64_t id); // Called from UI or raster thread. + virtual ~Texture(); // Called from raster thread. // Called from raster thread. virtual void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality quality) = 0; + const SkSamplingOptions& sampling) = 0; // Called from raster thread. virtual void OnGrContextCreated() = 0; @@ -74,4 +75,4 @@ class TextureRegistry { } // namespace flutter -#endif // FLUTTER_FLOW_TEXTURE_H_ +#endif // FLUTTER_COMMON_GRAPHICS_TEXTURE_H_ diff --git a/common/settings.cc b/common/settings.cc index 1508e213bcc6c..ec8ddf60f9c8d 100644 --- a/common/settings.cc +++ b/common/settings.cc @@ -46,6 +46,8 @@ std::string Settings::ToString() const { stream << "enable_dart_profiling: " << enable_dart_profiling << std::endl; stream << "disable_dart_asserts: " << disable_dart_asserts << std::endl; stream << "enable_observatory: " << enable_observatory << std::endl; + stream << "enable_observatory_publication: " << enable_observatory_publication + << std::endl; stream << "observatory_host: " << observatory_host << std::endl; stream << "observatory_port: " << observatory_port << std::endl; stream << "use_test_fonts: " << use_test_fonts << std::endl; diff --git a/common/settings.h b/common/settings.h index 46f88399b407e..332e3285547f1 100644 --- a/common/settings.h +++ b/common/settings.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -28,19 +29,25 @@ class FrameTiming { kBuildFinish, kRasterStart, kRasterFinish, + kRasterFinishWallTime, kCount }; static constexpr Phase kPhases[kCount] = { - kVsyncStart, kBuildStart, kBuildFinish, kRasterStart, kRasterFinish}; + kVsyncStart, kBuildStart, kBuildFinish, + kRasterStart, kRasterFinish, kRasterFinishWallTime}; fml::TimePoint Get(Phase phase) const { return data_[phase]; } fml::TimePoint Set(Phase phase, fml::TimePoint value) { return data_[phase] = value; } + uint64_t GetFrameNumber() const { return frame_number_; } + void SetFrameNumber(uint64_t frame_number) { frame_number_ = frame_number; } + private: fml::TimePoint data_[kCount]; + uint64_t frame_number_; }; using TaskObserverAdd = @@ -49,16 +56,20 @@ using TaskObserverRemove = std::function; using UnhandledExceptionCallback = std::function; +using LogMessageCallback = + std::function; -// TODO(chinmaygarde): Deprecate all the "path" struct members in favor of the +// TODO(26783): Deprecate all the "path" struct members in favor of the // callback that generates the mapping from these paths. -// https://github.com/flutter/flutter/issues/26783 using MappingCallback = std::function(void)>; -using MappingsCallback = - std::function>(void)>; +using Mappings = std::vector>; +using MappingsCallback = std::function; using FrameRasterizedCallback = std::function; +class DartIsolate; + struct Settings { Settings(); @@ -99,7 +110,8 @@ struct Settings { bool enable_checked_mode = false; bool start_paused = false; bool trace_skia = false; - std::string trace_allowlist; + std::vector trace_allowlist; + std::optional> trace_skia_allowlist; bool trace_startup = false; bool trace_systrace = false; bool dump_skp_on_shader_compilation = false; @@ -126,6 +138,11 @@ struct Settings { // Whether the Dart VM service should be enabled. bool enable_observatory = false; + // Whether to publish the observatory URL over mDNS. + // On iOS 14 this prompts a local network permission dialog, + // which cannot be accepted or dismissed in a CI environment. + bool enable_observatory_publication = true; + // The IP address to which the Dart VM service is bound. std::string observatory_host; @@ -145,6 +162,12 @@ struct Settings { // Font settings bool use_test_fonts = false; + // Selects the SkParagraph implementation of the text layout engine. + bool enable_skparagraph = false; + + // Selects the DisplayList for storage of rendering operations. + bool enable_display_list = true; + // All shells in the process share the same VM. The last shell to shutdown // should typically shut down the VM as well. However, applications depend on // the behavior of "warming-up" the VM by creating a shell that does not do @@ -164,12 +187,22 @@ struct Settings { TaskObserverRemove task_observer_remove; // The main isolate is current when this callback is made. This is a good spot // to perform native Dart bindings for libraries not built in. - fml::closure root_isolate_create_callback; + std::function root_isolate_create_callback; + // TODO(68738): Update isolate callbacks in settings to accept an additional + // DartIsolate parameter. fml::closure isolate_create_callback; // The isolate is not current and may have already been destroyed when this // call is made. fml::closure root_isolate_shutdown_callback; fml::closure isolate_shutdown_callback; + // A callback made in the isolate scope of the service isolate when it is + // launched. Care must be taken to ensure that callers are assigning callbacks + // to the settings object used to launch the VM. If an existing VM is used to + // launch an isolate using these settings, the callback will be ignored as the + // service isolate has already been launched. Also, this callback will only be + // made in the modes in which the service isolate is eligible for launch + // (debug and profile). + fml::closure service_isolate_create_callback; // The callback made on the UI thread in an isolate scope when the engine // detects that the framework is idle. The VM also uses this time to perform // tasks suitable when idling. Due to this, embedders are still advised to be @@ -181,6 +214,11 @@ struct Settings { // managed thread and embedders must re-thread as necessary. Performing // blocking calls in this callback will cause applications to jank. UnhandledExceptionCallback unhandled_exception_callback; + // A callback given to the embedder to log print messages from the running + // Flutter application. This callback is made on an internal engine managed + // thread and embedders must re-thread if necessary. Performing blocking + // calls in this callback will cause applications to jank. + LogMessageCallback log_message_callback; bool enable_software_rendering = false; bool skia_deterministic_rendering_on_cpu = false; bool verbose_logging = false; @@ -204,11 +242,11 @@ struct Settings { FrameRasterizedCallback frame_rasterized_callback; // This data will be available to the isolate immediately on launch via the - // Window.getPersistentIsolateData callback. This is meant for information - // that the isolate cannot request asynchronously (platform messages can be - // used for that purpose). This data is held for the lifetime of the shell and - // is available on isolate restarts in the shell instance. Due to this, - // the buffer must be as small as possible. + // PlatformDispatcher.getPersistentIsolateData callback. This is meant for + // information that the isolate cannot request asynchronously (platform + // messages can be used for that purpose). This data is held for the lifetime + // of the shell and is available on isolate restarts in the shell instance. + // Due to this, the buffer must be as small as possible. std::shared_ptr persistent_isolate_data; /// Max size of old gen heap size in MB, or 0 for unlimited, -1 for default diff --git a/docs/dart_split_aot_compilation.svg b/docs/dart_split_aot_compilation.svg new file mode 100644 index 0000000000000..09ba3739a18ed --- /dev/null +++ b/docs/dart_split_aot_compilation.svg @@ -0,0 +1,57 @@ + + + + + View 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/deferred_components_architecture.svg b/docs/deferred_components_architecture.svg new file mode 100644 index 0000000000000..77068d6a7bba7 --- /dev/null +++ b/docs/deferred_components_architecture.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/deferred_components_call_lifecycle.svg b/docs/deferred_components_call_lifecycle.svg new file mode 100644 index 0000000000000..c89c87fb75b89 --- /dev/null +++ b/docs/deferred_components_call_lifecycle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/e2etests/web/regular_integration_tests/README.md b/e2etests/web/regular_integration_tests/README.md deleted file mode 100644 index ba7f0c6c735d2..0000000000000 --- a/e2etests/web/regular_integration_tests/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Flutter for Web Engine Integration testing - -This directory is for Flutter Web engine integration tests that does not need a specific configuration. If an integration test needs specialized app configuration (e.g. PWA vs non-PWA packaging), please create another directory under e2etests/web. Otherwise tests such as text_editing, history, scrolling, pointer events... should all go under this package. - -Tests can be run on both 'debug', 'release' and 'profile' modes. However 'profile'/'release' modes will shorten the error stack trace. 'release' mode is for testing release code. Use 'debug' mode for troubleshooting purposes and seeing full stack traces (if there is an error). For more details on build [modes](https://flutter.dev/docs/testing/build-modes). - -## To run the application under test for troubleshooting purposes - -``` -flutter run -d web-server lib/text_editing_main.dart --local-engine=host_debug_unopt -``` - -## To run the Text Editing test and use the developer tools in the browser - -``` -flutter run test_driver/text_editing_integration.dart -d web-server --web-port=8080 --profile --local-engine=host_debug_unopt -``` - -## To run the test for Text Editing with driver - -Either of the following options: - -``` -flutter drive -v --target=test_driver/text_editing_integration.dart -d web-server --profile --local-engine=host_debug_unopt -``` - -``` -flutter drive -v --target=test_driver/text_editing_integration.dart -d web-server --release --local-engine=host_debug_unopt -``` - -## Using different browsers - -The default browser is Chrome, you can also use `android-chrome`, `safari`,`ios-safari`, `firefox` or `edge` as your browser choice. Example: - -``` -flutter drive -v --target=test_driver/text_editing_integration.dart -d web-server --release --browser-name=firefox --local-engine=host_debug_unopt -``` - -More details for "Running Flutter Driver tests with Web" can be found in [wiki](https://github.com/flutter/flutter/wiki/Running-Flutter-Driver-tests-with-Web). diff --git a/e2etests/web/regular_integration_tests/assets/images/1.5x/sample_image1.png b/e2etests/web/regular_integration_tests/assets/images/1.5x/sample_image1.png deleted file mode 100644 index f1e0ffab20007..0000000000000 Binary files a/e2etests/web/regular_integration_tests/assets/images/1.5x/sample_image1.png and /dev/null differ diff --git a/e2etests/web/regular_integration_tests/assets/images/2.0x/sample_image1.png b/e2etests/web/regular_integration_tests/assets/images/2.0x/sample_image1.png deleted file mode 100644 index d80b0ff28c502..0000000000000 Binary files a/e2etests/web/regular_integration_tests/assets/images/2.0x/sample_image1.png and /dev/null differ diff --git a/e2etests/web/regular_integration_tests/assets/images/sample_image1.png b/e2etests/web/regular_integration_tests/assets/images/sample_image1.png deleted file mode 100644 index 0d1393b805213..0000000000000 Binary files a/e2etests/web/regular_integration_tests/assets/images/sample_image1.png and /dev/null differ diff --git a/e2etests/web/regular_integration_tests/lib/image_loading_main.dart b/e2etests/web/regular_integration_tests/lib/image_loading_main.dart deleted file mode 100644 index 78991d9652eb2..0000000000000 --- a/e2etests/web/regular_integration_tests/lib/image_loading_main.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() async { - const MethodChannel channel = - OptionalMethodChannel('flutter/web_test_e2e', JSONMethodCodec()); - await channel.invokeMethod( - 'setDevicePixelRatio', - '1.5', - ); - runApp(MyApp()); -} - -class MyApp extends StatefulWidget { - @override - MyAppState createState() => MyAppState(); -} - -class MyAppState extends State { - @override - Widget build(BuildContext context) { - return MaterialApp( - key: const Key('mainapp'), - title: 'Integration Test App', - home: Image.asset('assets/images/sample_image1.png') - ); - } -} diff --git a/e2etests/web/regular_integration_tests/lib/platform_messages_main.dart b/e2etests/web/regular_integration_tests/lib/platform_messages_main.dart deleted file mode 100644 index 4201da2b47b77..0000000000000 --- a/e2etests/web/regular_integration_tests/lib/platform_messages_main.dart +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() => runApp(MyApp()); - -Future dataFuture; - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - key: const Key('mainapp'), - title: 'Integration Test App For Platform Messages', - home: MyHomePage(title: 'Integration Test App For Platform Messages'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - final TextEditingController _controller = - TextEditingController(text: 'Text1'); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Hello World', - ), - // Create a text form field since we can't test clipboard unless - // html document has focus. - TextFormField( - key: const Key('input'), - enabled: true, - controller: _controller, - //initialValue: 'Text1', - decoration: const InputDecoration( - labelText: 'Text Input Field:', - ), - ), - ], - ), - ), - ); - } -} diff --git a/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart b/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart deleted file mode 100644 index 26dba545b9261..0000000000000 --- a/e2etests/web/regular_integration_tests/lib/profile_diagnostics_main.dart +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatefulWidget { - @override - MyAppState createState() => MyAppState(); -} - -class MyAppState extends State { - String kMessage = 'ABC'; - @override - Widget build(BuildContext context) { - // cause cast error. - print(kMessage as int); - return Text('Hello'); - } -} diff --git a/e2etests/web/regular_integration_tests/lib/target_platform_main.dart b/e2etests/web/regular_integration_tests/lib/target_platform_main.dart deleted file mode 100644 index a526cfe025ba3..0000000000000 --- a/e2etests/web/regular_integration_tests/lib/target_platform_main.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -void main() => runApp(MyApp()); - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - key: const Key('mainapp'), - title: 'Platform Test', - home: MyHomePage(), - ); - } -} - -class MyHomePage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (defaultTargetPlatform == TargetPlatform.macOS) - const Text( - 'I am running on MacOS', - key: Key('macOSKey'), - ), - if (defaultTargetPlatform == TargetPlatform.iOS) - const Text( - 'I am running on MacOS', - key: Key('iOSKey'), - ), - if (defaultTargetPlatform == TargetPlatform.android) - const Text( - 'I am running on Android', - key: Key('androidKey'), - ), - ], - ), - ), - ); - } -} diff --git a/e2etests/web/regular_integration_tests/lib/text_editing_main.dart b/e2etests/web/regular_integration_tests/lib/text_editing_main.dart deleted file mode 100644 index f1aefa7ef1967..0000000000000 --- a/e2etests/web/regular_integration_tests/lib/text_editing_main.dart +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -void main() => runApp(MyApp()); - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - key: const Key('mainapp'), - title: 'Integration Test App', - home: MyHomePage(title: 'Integration Test App'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - String infoText = 'no-enter'; - - // Controller with no inital value; - final TextEditingController _emptyController = TextEditingController(); - - final TextEditingController _controller = - TextEditingController(text: 'Text1'); - - final TextEditingController _controller2 = - TextEditingController(text: 'Text2'); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Text Editing Test 1', - ), - TextFormField( - key: const Key('empty-input'), - enabled: true, - controller: _emptyController, - decoration: const InputDecoration( - labelText: 'Empty Input Field:', - ), - ), - const Text( - 'Text Editing Test 2', - ), - TextFormField( - key: const Key('input'), - enabled: true, - controller: _controller, - decoration: const InputDecoration( - labelText: 'Text Input Field:', - ), - ), - const Text( - 'Text Editing Test 3', - ), - TextFormField( - key: const Key('input2'), - enabled: true, - controller: _controller2, - decoration: const InputDecoration( - labelText: 'Text Input Field 2:', - ), - onFieldSubmitted: (String str) { - print('event received'); - setState(() => infoText = 'enter pressed'); - }, - ), - Text( - infoText, - key: const Key('text'), - ), - const Padding( - padding: EdgeInsets.all(12.0), - child: SelectableText( - 'Lorem ipsum dolor sit amet', - key: Key('selectable'), - style: TextStyle(fontFamily: 'Roboto', fontSize: 20.0), - ), - ), - ], - ), - ), - ); - } -} diff --git a/e2etests/web/regular_integration_tests/lib/treeshaking_main.dart b/e2etests/web/regular_integration_tests/lib/treeshaking_main.dart deleted file mode 100644 index 0df900d0597b4..0000000000000 --- a/e2etests/web/regular_integration_tests/lib/treeshaking_main.dart +++ /dev/null @@ -1,45 +0,0 @@ - -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -void main() => runApp(MyApp()); - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - key: const Key('mainapp'), - title: 'Integration Test App', - home: MyHomePage(title: 'Integration Test App'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - final TextEditingController _controller = - TextEditingController(text: 'TreeshakingThings1'); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: const Center( - child: Text('TreeshakingThings'), - ), - ); - } -} diff --git a/e2etests/web/regular_integration_tests/pubspec.yaml b/e2etests/web/regular_integration_tests/pubspec.yaml deleted file mode 100644 index 26ee22f810be2..0000000000000 --- a/e2etests/web/regular_integration_tests/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: regular_integration_tests -publish_to: none - -environment: - sdk: ">=2.2.2 <3.0.0" - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - flutter_driver: - sdk: flutter - flutter_test: - sdk: flutter - integration_test: 0.9.0 - http: 0.12.0+2 - test: any - -flutter: - assets: - - assets/images/ diff --git a/e2etests/web/regular_integration_tests/test_driver/image_loading_integration.dart b/e2etests/web/regular_integration_tests/test_driver/image_loading_integration.dart deleted file mode 100644 index 2d87590b95386..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/image_loading_integration.dart +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:html' as html; -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/image_loading_main.dart' as app; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Image loads asset variant based on device pixel ratio', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - final html.ImageElement imageElement = html.querySelector('img') as html.ImageElement; - expect(imageElement.naturalWidth, 1.5 * 100); - expect(imageElement.naturalHeight, 1.5 * 100); - expect(imageElement.width, 100); - expect(imageElement.height, 100); - }); -} diff --git a/e2etests/web/regular_integration_tests/test_driver/image_loading_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/image_loading_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/image_loading_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart b/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart deleted file mode 100644 index af77ea1a1437b..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration.dart +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:html' as html; -// ignore: undefined_shown_name -import 'dart:ui' as ui show platformViewRegistry; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/platform_messages_main.dart' as app; - -import 'package:integration_test/integration_test.dart'; - -void main() async { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('platform message for Clipboard.setData reply with future', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 - SystemChannels.textInput.setMockMethodCallHandler(null); - // Focus on a TextFormField. - final Finder finder = find.byKey(const Key('input')); - expect(finder, findsOneWidget); - await tester.tap(find.byKey(const Key('input'))); - // Focus in input, otherwise clipboard will fail with - // 'document is not focused' platform exception. - html.document.querySelector('input').focus(); - await Clipboard.setData(const ClipboardData(text: 'sample text')); - }, skip: true); // https://github.com/flutter/flutter/issues/54296 - - testWidgets('Should create and dispose view embedder', - (WidgetTester tester) async { - int viewInstanceCount = 0; - - final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); - // ignore: undefined_prefixed_name - ui.platformViewRegistry.registerViewFactory('MyView', (int viewId) { - ++viewInstanceCount; - return html.DivElement(); - }); - - app.main(); - await tester.pumpAndSettle(); - final Map createArgs = { - 'id': '567', - 'viewType': 'MyView', - }; - await SystemChannels.platform_views.invokeMethod('create', createArgs); - final Map disposeArgs = { - 'id': '567', - }; - await SystemChannels.platform_views.invokeMethod('dispose', disposeArgs); - expect(viewInstanceCount, 1); - }); -} diff --git a/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/platform_messages_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/profile_diagnostics_integration.dart b/e2etests/web/regular_integration_tests/test_driver/profile_diagnostics_integration.dart deleted file mode 100644 index 95c77b380a2c8..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/profile_diagnostics_integration.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/profile_diagnostics_main.dart' as app; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('App build method exception should form valid FlutterErrorDetails', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - final dynamic appError = tester.takeException(); - expect(appError, isA()); - }); -} diff --git a/e2etests/web/regular_integration_tests/test_driver/profile_diagnostics_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/profile_diagnostics_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/profile_diagnostics_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/target_platform_android_integration.dart b/e2etests/web/regular_integration_tests/test_driver/target_platform_android_integration.dart deleted file mode 100644 index d914d54693296..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/target_platform_android_integration.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/target_platform_main.dart' as app; -import 'package:flutter/material.dart'; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Should detect android platform when running on android device', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - final Finder macOSFinder = find.byKey(const Key('macOSKey')); - expect(macOSFinder, findsNothing); - - final Finder iOSFinder = find.byKey(const Key('iOSKey')); - expect(iOSFinder, findsNothing); - - final Finder androidFinder = find.byKey(const Key('androidKey')); - expect(androidFinder, findsOneWidget); - }); -} diff --git a/e2etests/web/regular_integration_tests/test_driver/target_platform_android_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/target_platform_android_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/target_platform_android_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/target_platform_ios_integration.dart b/e2etests/web/regular_integration_tests/test_driver/target_platform_ios_integration.dart deleted file mode 100644 index 72a74fc73e97d..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/target_platform_ios_integration.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/target_platform_main.dart' as app; -import 'package:flutter/material.dart'; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Should detect iOS platform when running on iOS device', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - final Finder macOSFinder = find.byKey(const Key('macOSKey')); - expect(macOSFinder, findsNothing); - - final Finder iOSFinder = find.byKey(const Key('iOSKey')); - expect(iOSFinder, findsOneWidget); - - final Finder androidFinder = find.byKey(const Key('androidKey')); - expect(androidFinder, findsNothing); - }); -} diff --git a/e2etests/web/regular_integration_tests/test_driver/target_platform_ios_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/target_platform_ios_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/target_platform_ios_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/target_platform_macos_integration.dart b/e2etests/web/regular_integration_tests/test_driver/target_platform_macos_integration.dart deleted file mode 100644 index 4a20827769eb9..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/target_platform_macos_integration.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/target_platform_main.dart' as app; -import 'package:flutter/material.dart'; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Should detect MacOS platform when running on MacOS', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - final Finder macOSFinder = find.byKey(const Key('macOSKey')); - expect(macOSFinder, findsOneWidget); - - final Finder iOSFinder = find.byKey(const Key('iOSKey')); - expect(iOSFinder, findsNothing); - - final Finder androidFinder = find.byKey(const Key('androidKey')); - expect(androidFinder, findsNothing); - }); -} diff --git a/e2etests/web/regular_integration_tests/test_driver/target_platform_macos_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/target_platform_macos_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/target_platform_macos_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart b/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart deleted file mode 100644 index 67263f054ca97..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration.dart +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:html'; -import 'dart:js_util' as js_util; -import 'package:flutter/gestures.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/text_editing_main.dart' as app; -import 'package:flutter/material.dart'; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Focused text field creates a native input element', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 - SystemChannels.textInput.setMockMethodCallHandler(null); - - // Focus on a TextFormField. - final Finder finder = find.byKey(const Key('input')); - expect(finder, findsOneWidget); - await tester.tap(find.byKey(const Key('input'))); - - // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); - expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; - // The element's value will be the same as the textFormField's value. - expect(input.value, 'Text1'); - - // Change the value of the TextFormField. - final TextFormField textFormField = tester.widget(finder); - textFormField.controller.text = 'New Value'; - // DOM element's value also changes. - expect(input.value, 'New Value'); - }); - - testWidgets('Input field with no initial value works', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 - SystemChannels.textInput.setMockMethodCallHandler(null); - - // Focus on a TextFormField. - final Finder finder = find.byKey(const Key('empty-input')); - expect(finder, findsOneWidget); - await tester.tap(find.byKey(const Key('empty-input'))); - - // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); - expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; - // The element's value will be empty. - expect(input.value, ''); - - // Change the value of the TextFormField. - final TextFormField textFormField = tester.widget(finder); - textFormField.controller.text = 'New Value'; - // DOM element's value also changes. - expect(input.value, 'New Value'); - }); - - testWidgets('Pressing enter on the text field triggers submit', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 - SystemChannels.textInput.setMockMethodCallHandler(null); - - // This text will show no-enter initially. It will have 'enter-pressed' - // after `onFieldSubmitted` of TextField is triggered. - final Finder textFinder = find.byKey(const Key('text')); - expect(textFinder, findsOneWidget); - final Text text = tester.widget(textFinder); - expect(text.data, 'no-enter'); - - // Focus on a TextFormField. - final Finder textFormFielsFinder = find.byKey(const Key('input2')); - expect(textFormFielsFinder, findsOneWidget); - await tester.tap(find.byKey(const Key('input2'))); - - // // Press Tab. This should trigger `onFieldSubmitted` of TextField. - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; - dispatchKeyboardEvent(input, 'keydown', { - 'keyCode': 13, // Enter. - 'cancelable': true, - }); - - await tester.pumpAndSettle(); - - final Finder textFinder2 = find.byKey(const Key('text')); - expect(textFinder2, findsOneWidget); - final Text text2 = tester.widget(textFinder2); - expect(text2.data, 'enter pressed'); - }); - - testWidgets('Jump between TextFormFields with tab key', - (WidgetTester tester) async { - app.main(); - await tester.pumpAndSettle(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 - SystemChannels.textInput.setMockMethodCallHandler(null); - - // Focus on a TextFormField. - final Finder finder = find.byKey(const Key('input')); - expect(finder, findsOneWidget); - await tester.tap(find.byKey(const Key('input'))); - - // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('input'); - expect(nodeList.length, equals(1)); - final InputElement input = - document.getElementsByTagName('input')[0] as InputElement; - - // Press Tab. The focus should move to the next TextFormField. - dispatchKeyboardEvent(input, 'keydown', { - 'key': 'Tab', - 'code': 'Tab', - 'bubbles': true, - 'cancelable': true, - }); - - await tester.pumpAndSettle(); - - // A native input element for the next TextField should be attached to the - // DOM. - final InputElement input2 = - document.getElementsByTagName('input')[0] as InputElement; - expect(input2.value, 'Text2'); - }); - - testWidgets('Read-only fields work', (WidgetTester tester) async { - const String text = 'Lorem ipsum dolor sit amet'; - app.main(); - await tester.pumpAndSettle(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 - SystemChannels.textInput.setMockMethodCallHandler(null); - - // Select something from the selectable text. - final Finder finder = find.byKey(const Key('selectable')); - expect(finder, findsOneWidget); - final RenderBox selectable = tester.renderObject(finder); - final Offset topLeft = selectable.localToGlobal(Offset.zero); - final Offset topRight = - selectable.localToGlobal(Offset(selectable.size.width, 0.0)); - - // Drag by mouse to select the entire selectable text. - TestGesture gesture = - await tester.startGesture(topLeft, kind: PointerDeviceKind.mouse); - addTearDown(gesture.removePointer); - await gesture.moveTo(topRight); - await gesture.up(); - - // A native input element will be appended to the DOM. - final List nodeList = document.getElementsByTagName('textarea'); - expect(nodeList.length, equals(1)); - final TextAreaElement input = nodeList[0] as TextAreaElement; - // The element's value should contain the selectable text. - expect(input.value, text); - expect(input.hasAttribute('readonly'), isTrue); - - // Make sure the entire text is selected. - TextRange range = - TextRange(start: input.selectionStart, end: input.selectionEnd); - expect(range.textInside(text), text); - - // Double tap to select the first word. - final Offset firstWordOffset = topLeft.translate(10.0, 0.0); - gesture = await tester.startGesture( - firstWordOffset, - kind: PointerDeviceKind.mouse, - ); - addTearDown(gesture.removePointer); - await gesture.up(); - await gesture.down(firstWordOffset); - await gesture.up(); - range = TextRange(start: input.selectionStart, end: input.selectionEnd); - expect(range.textInside(text), 'Lorem'); - - // Double tap to select the last word. - final Offset lastWordOffset = topRight.translate(-10.0, 0.0); - gesture = await tester.startGesture( - lastWordOffset, - kind: PointerDeviceKind.mouse, - ); - addTearDown(gesture.removePointer); - await gesture.up(); - await gesture.down(lastWordOffset); - await gesture.up(); - range = TextRange(start: input.selectionStart, end: input.selectionEnd); - expect(range.textInside(text), 'amet'); - }); -} - -KeyboardEvent dispatchKeyboardEvent( - EventTarget target, String type, Map args) { - final dynamic jsKeyboardEvent = js_util.getProperty(window, 'KeyboardEvent'); - final List eventArgs = [ - type, - args, - ]; - - final KeyboardEvent event = js_util.callConstructor( - jsKeyboardEvent, js_util.jsify(eventArgs) as List) - as KeyboardEvent; - target.dispatchEvent(event); - - return event; -} diff --git a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/text_editing_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/text_editing_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/test_driver/treeshaking_integration.dart b/e2etests/web/regular_integration_tests/test_driver/treeshaking_integration.dart deleted file mode 100644 index 9acd9e5eb63c8..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/treeshaking_integration.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:html' as html; -import 'package:flutter_test/flutter_test.dart'; -import 'package:regular_integration_tests/treeshaking_main.dart' as app; -import 'package:flutter/material.dart'; - -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('debug+Fill+Properties for widgets is tree shaken', - (WidgetTester tester) async { - // About 11 instances are used by DiagnosticsNode and diagnostics - // for flutter framework itself. Widgets have > 100. So we check for 20 to - // so this test fails when tree-shaking is broken. - await testOccurenceCountBelow(tester, '${debugPrefix}FillProperties', 20); - }); -} - -// Used to prevent compiler optimization that will generate const string. -// Preventing counting test strings. -String get debugPrefix => ['d','e','b','u','g'].join(''); - -Future testOccurenceCountBelow(WidgetTester tester, String methodName, int count) async { - app.main(); - await tester.pumpAndSettle(); - - // Make sure app loaded. - final Finder finder = find.byKey(const Key('mainapp')); - expect(finder, findsOneWidget); - - await _loadBundleAndCheck(methodName, count); -} - -String fileContents; - -Future _loadBundleAndCheck(String methodName, int count) async { - fileContents ??= await html.HttpRequest.getString('main.dart.js'); - expect(fileContents, contains('RenderObjectToWidgetElement')); - expect(occurrenceCount(fileContents, methodName), lessThan(count)); -} - -int occurrenceCount(String contents, String word) { - int count = 0; - final int wordLength = word.length; - int pos = contents.indexOf(word); - final int contentLength = contents.length; - while (pos != -1) { - ++count; - pos += wordLength; - if (pos >= contentLength || count > 100) { - break; - } - pos = contents.indexOf(word, pos); - } - return count; -} diff --git a/e2etests/web/regular_integration_tests/test_driver/treeshaking_integration_test.dart b/e2etests/web/regular_integration_tests/test_driver/treeshaking_integration_test.dart deleted file mode 100644 index 96b5ad0bf52a4..0000000000000 --- a/e2etests/web/regular_integration_tests/test_driver/treeshaking_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart' as test; - -Future main() async => test.integrationDriver(); diff --git a/e2etests/web/regular_integration_tests/web/index.html b/e2etests/web/regular_integration_tests/web/index.html deleted file mode 100644 index 4134d6bc48c1f..0000000000000 --- a/e2etests/web/regular_integration_tests/web/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Web Integration Tests - - - - - diff --git a/examples/glfw/README.md b/examples/glfw/README.md index c38cae4617946..762e9c53afee4 100644 --- a/examples/glfw/README.md +++ b/examples/glfw/README.md @@ -24,6 +24,6 @@ In order to **build** and **run** the example you should be able to go into this ## Troubleshooting There are a few things you might have to tweak in order to get your build working: - * Flutter Engine Location - Inside the `CMakeList.txt` file you will see that it is setup to search for the header and library for the Flutter Engine in specific locations, those might not be the location of your Flutter Engine. + * Flutter Engine Location - Inside the `CMakeList.txt` file you will see that it is set up to search for the header and library for the Flutter Engine in specific locations, those might not be the location of your Flutter Engine. * Pixel Ratio - If the project runs but is drawing at the wrong scale you may have to tweak the `kPixelRatio` variable in `FlutterEmbedderGLFW.cc` file. * GLFW Location - Inside the `CMakeLists.txt` we are searching for the GLFW library, if CMake can't find it you may have to edit that. diff --git a/flow/BUILD.gn b/flow/BUILD.gn index f769ee405afd5..b6c881f902f7d 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -10,10 +10,18 @@ source_set("flow") { sources = [ "compositor_context.cc", "compositor_context.h", + "diff_context.cc", + "diff_context.h", + "display_list.cc", + "display_list.h", + "display_list_canvas.cc", + "display_list_canvas.h", + "display_list_utils.cc", + "display_list_utils.h", "embedded_views.cc", "embedded_views.h", - "gl_context_switch.cc", - "gl_context_switch.h", + "frame_timings.cc", + "frame_timings.h", "instrumentation.cc", "instrumentation.h", "layers/backdrop_filter_layer.cc", @@ -28,6 +36,8 @@ source_set("flow") { "layers/color_filter_layer.h", "layers/container_layer.cc", "layers/container_layer.h", + "layers/display_list_layer.cc", + "layers/display_list_layer.h", "layers/image_filter_layer.cc", "layers/image_filter_layer.h", "layers/layer.cc", @@ -52,6 +62,8 @@ source_set("flow") { "layers/transform_layer.h", "matrix_decomposition.cc", "matrix_decomposition.h", + "paint_region.cc", + "paint_region.h", "paint_utils.cc", "paint_utils.h", "raster_cache.cc", @@ -66,35 +78,16 @@ source_set("flow") { "surface.h", "surface_frame.cc", "surface_frame.h", - "texture.cc", - "texture.h", ] public_configs = [ "//flutter:config" ] deps = [ "//flutter/common", + "//flutter/common/graphics", "//flutter/fml", "//third_party/skia", ] - - if (is_fuchsia && flutter_enable_legacy_fuchsia_embedder) { - sources += [ - "layers/child_scene_layer.cc", - "layers/child_scene_layer.h", - "scene_update_context.cc", - "scene_update_context.h", - "view_holder.cc", - "view_holder.h", - ] - - public_deps = [ - "$fuchsia_sdk_root/fidl:fuchsia.ui.app", - "$fuchsia_sdk_root/fidl:fuchsia.ui.gfx", - "$fuchsia_sdk_root/fidl:fuchsia.ui.views", - "$fuchsia_sdk_root/pkg:scenic_cpp", - ] - } } if (enable_unittests) { @@ -106,9 +99,13 @@ if (enable_unittests) { testonly = true sources = [ + "testing/diff_context_test.cc", + "testing/diff_context_test.h", "testing/gl_context_switch_test.cc", "testing/gl_context_switch_test.h", "testing/layer_test.h", + "testing/mock_embedder.cc", + "testing/mock_embedder.h", "testing/mock_layer.cc", "testing/mock_layer.h", "testing/mock_raster_cache.cc", @@ -124,17 +121,23 @@ if (enable_unittests) { "//third_party/googletest:gtest", ] - deps = [ ":flow" ] + deps = [ + ":flow", + "//flutter/common/graphics", + ] } executable("flow_unittests") { testonly = true sources = [ + "display_list_canvas_unittests.cc", + "display_list_unittests.cc", "embedded_view_params_unittests.cc", "flow_run_all_unittests.cc", "flow_test_utils.cc", "flow_test_utils.h", + "frame_timings_recorder_unittests.cc", "gl_context_switch_unittests.cc", "layers/backdrop_filter_layer_unittests.cc", "layers/checkerboard_layertree_unittests.cc", @@ -143,6 +146,7 @@ if (enable_unittests) { "layers/clip_rrect_layer_unittests.cc", "layers/color_filter_layer_unittests.cc", "layers/container_layer_unittests.cc", + "layers/display_list_layer_unittests.cc", "layers/image_filter_layer_unittests.cc", "layers/layer_tree_unittests.cc", "layers/opacity_layer_unittests.cc", @@ -167,6 +171,7 @@ if (enable_unittests) { ":flow", ":flow_fixtures", ":flow_testing", + "//flutter/common/graphics", "//flutter/fml", "//flutter/testing:skia", "//flutter/testing:testing_lib", @@ -182,11 +187,5 @@ if (enable_unittests) { # Required for M_PI and others. defines += [ "_USE_MATH_DEFINES" ] } - - if (is_fuchsia && flutter_enable_legacy_fuchsia_embedder) { - sources += [ "layers/fuchsia_layer_unittests.cc" ] - - deps += [ "//build/fuchsia/pkg:sys_cpp_testing" ] - } } } diff --git a/flow/README.md b/flow/README.md index 150281e2288e4..f36ae77db9694 100644 --- a/flow/README.md +++ b/flow/README.md @@ -2,5 +2,5 @@ Flow ==== Flow is a simple compositor based on Skia that the Flutter engine uses to cache -recoded paint commands and pixels generated from those recordings. Flow runs on +recorded paint commands and pixels generated from those recordings. Flow runs on the raster thread and uploads information to Skia. diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 7631e7e41ee2a..ca1bb662f68cf 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -8,10 +8,10 @@ #include #include +#include "flutter/common/graphics/texture.h" #include "flutter/flow/embedded_views.h" #include "flutter/flow/instrumentation.h" #include "flutter/flow/raster_cache.h" -#include "flutter/flow/texture.h" #include "flutter/fml/macros.h" #include "flutter/fml/raster_thread_merger.h" #include "third_party/skia/include/core/SkCanvas.h" diff --git a/flow/diff_context.cc b/flow/diff_context.cc new file mode 100644 index 0000000000000..a73335f3d1ed4 --- /dev/null +++ b/flow/diff_context.cc @@ -0,0 +1,165 @@ +// 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/flow/diff_context.h" +#include "flutter/flow/layers/layer.h" + +namespace flutter { + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +DiffContext::DiffContext(SkISize frame_size, + double frame_device_pixel_ratio, + PaintRegionMap& this_frame_paint_region_map, + const PaintRegionMap& last_frame_paint_region_map) + : rects_(std::make_shared>()), + frame_size_(frame_size), + frame_device_pixel_ratio_(frame_device_pixel_ratio), + this_frame_paint_region_map_(this_frame_paint_region_map), + last_frame_paint_region_map_(last_frame_paint_region_map) {} + +void DiffContext::BeginSubtree() { + state_stack_.push_back(state_); + state_.rect_index_ = rects_->size(); +} + +void DiffContext::EndSubtree() { + FML_DCHECK(!state_stack_.empty()); + state_ = std::move(state_stack_.back()); + state_stack_.pop_back(); +} + +DiffContext::State::State() + : dirty(false), cull_rect(kGiantRect), rect_index_(0) {} + +void DiffContext::PushTransform(const SkMatrix& transform) { + state_.transform.preConcat(transform); + SkMatrix inverse_transform; + // Perspective projections don't produce rectangles that are useful for + // culling for some reason. + if (!transform.hasPerspective() && transform.invert(&inverse_transform)) { + inverse_transform.mapRect(&state_.cull_rect); + } else { + state_.cull_rect = kGiantRect; + } +} + +Damage DiffContext::ComputeDamage( + const SkIRect& accumulated_buffer_damage) const { + SkRect buffer_damage = SkRect::Make(accumulated_buffer_damage); + buffer_damage.join(damage_); + SkRect frame_damage(damage_); + + for (const auto& r : readbacks_) { + SkRect rect = SkRect::Make(r.rect); + if (rect.intersects(frame_damage)) { + frame_damage.join(rect); + } + if (rect.intersects(buffer_damage)) { + buffer_damage.join(rect); + } + } + + Damage res; + buffer_damage.roundOut(&res.buffer_damage); + frame_damage.roundOut(&res.frame_damage); + + SkIRect frame_clip = SkIRect::MakeSize(frame_size_); + res.buffer_damage.intersect(frame_clip); + res.frame_damage.intersect(frame_clip); + return res; +} + +bool DiffContext::PushCullRect(const SkRect& clip) { + return state_.cull_rect.intersect(clip); +} + +void DiffContext::MarkSubtreeDirty(const PaintRegion& previous_paint_region) { + FML_DCHECK(!IsSubtreeDirty()); + if (previous_paint_region.is_valid()) { + AddDamage(previous_paint_region); + } + state_.dirty = true; +} + +void DiffContext::AddLayerBounds(const SkRect& rect) { + SkRect r(rect); + if (r.intersect(state_.cull_rect)) { + state_.transform.mapRect(&r); + if (!r.isEmpty()) { + rects_->push_back(r); + if (IsSubtreeDirty()) { + AddDamage(r); + } + } + } +} + +void DiffContext::AddExistingPaintRegion(const PaintRegion& region) { + // Adding paint region for retained layer implies that current subtree is not + // dirty, so we know, for example, that the inherited transforms must match + FML_DCHECK(!IsSubtreeDirty()); + if (region.is_valid()) { + rects_->insert(rects_->end(), region.begin(), region.end()); + } +} + +void DiffContext::AddReadbackRegion(const SkIRect& rect) { + Readback readback; + readback.rect = rect; + readback.position = rects_->size(); + // Push empty rect as a placeholder for position in current subtree + rects_->push_back(SkRect::MakeEmpty()); + readbacks_.push_back(std::move(readback)); +} + +PaintRegion DiffContext::CurrentSubtreeRegion() const { + bool has_readback = std::any_of( + readbacks_.begin(), readbacks_.end(), + [&](const Readback& r) { return r.position >= state_.rect_index_; }); + return PaintRegion(rects_, state_.rect_index_, rects_->size(), has_readback); +} + +void DiffContext::AddDamage(const PaintRegion& damage) { + FML_DCHECK(damage.is_valid()); + for (const auto& r : damage) { + damage_.join(r); + } +} + +void DiffContext::AddDamage(const SkRect& rect) { + damage_.join(rect); +} + +void DiffContext::SetLayerPaintRegion(const Layer* layer, + const PaintRegion& region) { + this_frame_paint_region_map_[layer->unique_id()] = region; +} + +PaintRegion DiffContext::GetOldLayerPaintRegion(const Layer* layer) const { + auto i = last_frame_paint_region_map_.find(layer->unique_id()); + if (i != last_frame_paint_region_map_.end()) { + return i->second; + } else { + // This is valid when Layer::PreservePaintRegion is called for retained + // layer with zero sized parent clip (these layers are not diffed) + return PaintRegion(); + } +} + +void DiffContext::Statistics::LogStatistics() { +#if !FLUTTER_RELEASE + FML_TRACE_COUNTER("flutter", "DiffContext", reinterpret_cast(this), + "NewPictures", new_pictures_, "PicturesTooComplexToCompare", + pictures_too_complex_to_compare_, "DeepComparePictures", + deep_compare_pictures_, "SameInstancePictures", + same_instance_pictures_, + "DifferentInstanceButEqualPictures", + different_instance_but_equal_pictures_); +#endif // !FLUTTER_RELEASE +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +} // namespace flutter diff --git a/flow/diff_context.h b/flow/diff_context.h new file mode 100644 index 0000000000000..50af1c714060c --- /dev/null +++ b/flow/diff_context.h @@ -0,0 +1,212 @@ +// 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_FLOW_DIFF_CONTEXT_H_ +#define FLUTTER_FLOW_DIFF_CONTEXT_H_ + +#include +#include +#include "flutter/flow/paint_region.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkRect.h" + +namespace flutter { + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +class Layer; + +// Represents area that needs to be updated in front buffer (frame_damage) and +// area that is going to be painted to in back buffer (buffer_damage). +struct Damage { + // This is the damage between current and previous frame; + // If embedder supports partial update, this is the region that needs to be + // repainted. + // Corresponds to "surface damage" from EGL_KHR_partial_update. + SkIRect frame_damage; + + // Reflects actual change to target framebuffer; This is frame_damage + + // damage previously acumulated for target framebuffer. + // All drawing will be clipped to this region. Knowing the affected area + // upfront may be useful for tile based GPUs. + // Corresponds to "buffer damage" from EGL_KHR_partial_update. + SkIRect buffer_damage; +}; + +// Layer Unique Id to PaintRegion +using PaintRegionMap = std::map; + +// Tracks state during tree diffing process and computes resulting damage +class DiffContext { + public: + explicit DiffContext(SkISize frame_size, + double device_pixel_aspect_ratio, + PaintRegionMap& this_frame_paint_region_map, + const PaintRegionMap& last_frame_paint_region_map); + + // Starts a new subtree. + void BeginSubtree(); + + // Ends current subtree; All modifications to state (transform, cullrect, + // dirty) will be restored + void EndSubtree(); + + // Creates subtree in current scope and closes it on scope exit + class AutoSubtreeRestore { + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(AutoSubtreeRestore); + + public: + explicit AutoSubtreeRestore(DiffContext* context) : context_(context) { + context->BeginSubtree(); + } + ~AutoSubtreeRestore() { context_->EndSubtree(); } + + private: + DiffContext* context_; + }; + + // Pushes additional transform for current subtree + void PushTransform(const SkMatrix& transform); + + // Pushes cull rect for current subtree + bool PushCullRect(const SkRect& clip); + + // Returns transform matrix for current subtree + const SkMatrix& GetTransform() const { return state_.transform; } + + // Return cull rect for current subtree (in local coordinates) + const SkRect& GetCullRect() const { return state_.cull_rect; } + + // Sets the dirty flag on current subtree; + // + // previous_paint_region, which should represent region of previous subtree + // at this level will be added to damage area. + // + // Each paint region added to dirty subtree (through AddPaintRegion) is also + // added to damage. + void MarkSubtreeDirty( + const PaintRegion& previous_paint_region = PaintRegion()); + + bool IsSubtreeDirty() const { return state_.dirty; } + + // Add layer bounds to current paint region; rect is in "local" (layer) + // coordinates. + void AddLayerBounds(const SkRect& rect); + + // Add entire paint region of retained layer for current subtree. This can + // only be used in subtrees that are not dirty, otherwise ancestor transforms + // or clips may result in different paint region. + void AddExistingPaintRegion(const PaintRegion& region); + + // The idea of readback region is that if any part of the readback region + // needs to be repainted, then the whole readback region must be repainted; + // + // Readback rect is in screen coordinates. + void AddReadbackRegion(const SkIRect& rect); + + // Returns the paint region for current subtree; Each rect in paint region is + // in screen coordinates; Once a layer accumulates the paint regions of its + // children, this PaintRegion value can be associated with the current layer + // using DiffContext::SetLayerPaintRegion. + PaintRegion CurrentSubtreeRegion() const; + + // Computes final damage + // + // additional_damage is the previously accumulated frame_damage for + // current framebuffer + Damage ComputeDamage(const SkIRect& additional_damage) const; + + double frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; }; + + // Adds the region to current damage. Used for removed layers, where instead + // of diffing the layer its paint region is direcly added to damage. + void AddDamage(const PaintRegion& damage); + + // Associates the paint region with specified layer and current layer tree. + // The paint region can not be stored directly in layer itself, because same + // retained layer instance can possibly paint in different locations depending + // on ancestor layers. + void SetLayerPaintRegion(const Layer* layer, const PaintRegion& region); + + // Retrieves the paint region associated with specified layer and previous + // frame layer tree. + PaintRegion GetOldLayerPaintRegion(const Layer* layer) const; + + class Statistics { + public: + // Picture replaced by different picture + void AddNewPicture() { ++new_pictures_; } + + // Picture that would require deep comparison but was considered too complex + // to serialize and thus was treated as new picture + void AddPictureTooComplexToCompare() { ++pictures_too_complex_to_compare_; } + + // Picture that has identical instance between frames + void AddSameInstancePicture() { ++same_instance_pictures_; }; + + // Picture that had to be serialized to compare for equality + void AddDeepComparePicture() { ++deep_compare_pictures_; } + + // Picture that had to be serialized to compare (different instances), + // but were equal + void AddDifferentInstanceButEqualPicture() { + ++different_instance_but_equal_pictures_; + }; + + // Logs the statistics to trace counter + void LogStatistics(); + + private: + int new_pictures_ = 0; + int pictures_too_complex_to_compare_ = 0; + int same_instance_pictures_ = 0; + int deep_compare_pictures_ = 0; + int different_instance_but_equal_pictures_ = 0; + }; + + Statistics& statistics() { return statistics_; } + + private: + struct State { + State(); + + bool dirty; + SkRect cull_rect; + SkMatrix transform; + size_t rect_index_; + }; + + std::shared_ptr> rects_; + State state_; + SkISize frame_size_; + double frame_device_pixel_ratio_; + std::vector state_stack_; + + SkRect damage_ = SkRect::MakeEmpty(); + + PaintRegionMap& this_frame_paint_region_map_; + const PaintRegionMap& last_frame_paint_region_map_; + + void AddDamage(const SkRect& rect); + + struct Readback { + // Index of rects_ entry that this readback belongs to. Used to + // determine if subtree has any readback + size_t position; + + // readback area, in screen coordinates + SkIRect rect; + }; + + std::vector readbacks_; + Statistics statistics_; +}; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +} // namespace flutter + +#endif // FLUTTER_FLOW_DIFF_CONTEXT_H_ diff --git a/flow/display_list.cc b/flow/display_list.cc new file mode 100644 index 0000000000000..6178eb2fed93b --- /dev/null +++ b/flow/display_list.cc @@ -0,0 +1,1371 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/flow/display_list.h" +#include "flutter/flow/display_list_canvas.h" +#include "flutter/flow/display_list_utils.h" +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +namespace flutter { + +const SkSamplingOptions DisplayList::NearestSampling = + SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); +const SkSamplingOptions DisplayList::LinearSampling = + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); +const SkSamplingOptions DisplayList::MipmapSampling = + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); +const SkSamplingOptions DisplayList::CubicSampling = + SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}); + +// Most Ops can be bulk compared using memcmp because they contain +// only numeric values or constructs that are constructed from numeric +// values. +// +// Some contain sk_sp<> references which can also be bulk compared +// to see if they are pointing to the same reference. (Note that +// two sk_sp<> that refer to the same object are themselves ==.) +// +// Only a DLOp that wants to do a deep compare needs to override the +// DLOp::equals() method and return a value of kEqual or kNotEqual. +enum class DisplayListCompare { + // The Op is deferring comparisons to a bulk memcmp performed lazily + // across all bulk-comparable ops. + kUseBulkCompare, + + // The Op provided a specific equals method that spotted a difference + kNotEqual, + + // The Op provided a specific equals method that saw no differences + kEqual, +}; + +#pragma pack(push, DLOp_Alignment, 8) + +// Assuming a 64-bit platform (most of our platforms at this time?) +// the following comments are a "worst case" assessment of how well +// these structures pack into memory. They may be packed more tightly +// on some of the 32-bit platforms that we see in older phones. +// +// Struct allocation in the DL memory is aligned to a void* boundary +// which means that the minimum (aligned) struct size will be 8 bytes. +// The DLOp base uses 4 bytes so each Op-specific struct gets 4 bytes +// of data for "free" and works best when it packs well into an 8-byte +// aligned size. +struct DLOp { + DisplayListOpType type : 8; + uint32_t size : 24; + + DisplayListCompare equals(const DLOp* other) const { + return DisplayListCompare::kUseBulkCompare; + } +}; + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +#define DEFINE_SET_BOOL_OP(name) \ + struct Set##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSet##name; \ + \ + Set##name##Op(bool value) : value(value) {} \ + \ + const bool value; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(value); \ + } \ + }; +DEFINE_SET_BOOL_OP(AA) +DEFINE_SET_BOOL_OP(Dither) +DEFINE_SET_BOOL_OP(InvertColors) +#undef DEFINE_SET_BOOL_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +#define DEFINE_SET_ENUM_OP(name) \ + struct Set##name##s##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSet##name##s; \ + \ + Set##name##s##Op(SkPaint::name value) : value(value) {} \ + \ + const SkPaint::name value; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name##s(value); \ + } \ + }; +DEFINE_SET_ENUM_OP(Cap) +DEFINE_SET_ENUM_OP(Join) +#undef DEFINE_SET_ENUM_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetDrawStyleOp final : DLOp { + static const auto kType = DisplayListOpType::kSetDrawStyle; + + SetDrawStyleOp(SkPaint::Style style) : style(style) {} + + const SkPaint::Style style; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setDrawStyle(style); + } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetStrokeWidthOp final : DLOp { + static const auto kType = DisplayListOpType::kSetStrokeWidth; + + SetStrokeWidthOp(SkScalar width) : width(width) {} + + const SkScalar width; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setStrokeWidth(width); + } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetMiterLimitOp final : DLOp { + static const auto kType = DisplayListOpType::kSetMiterLimit; + + SetMiterLimitOp(SkScalar limit) : limit(limit) {} + + const SkScalar limit; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setMiterLimit(limit); + } +}; + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetColorOp final : DLOp { + static const auto kType = DisplayListOpType::kSetColor; + + SetColorOp(SkColor color) : color(color) {} + + const SkColor color; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.setColor(color); } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetBlendModeOp final : DLOp { + static const auto kType = DisplayListOpType::kSetBlendMode; + + SetBlendModeOp(SkBlendMode mode) : mode(mode) {} + + const SkBlendMode mode; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.setBlendMode(mode); } +}; + +// Clear: 4 byte header + unused 4 byte payload uses 8 bytes +// (4 bytes unused) +// Set: 4 byte header + an sk_sp (ptr) uses 16 bytes due to the +// alignment of the ptr. +// (4 bytes unused) +#define DEFINE_SET_CLEAR_SKREF_OP(name, field) \ + struct Clear##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kClear##name; \ + \ + Clear##name##Op() {} \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(nullptr); \ + } \ + }; \ + struct Set##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSet##name; \ + \ + Set##name##Op(sk_sp field) : field(std::move(field)) {} \ + \ + sk_sp field; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(field); \ + } \ + }; +DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) +DEFINE_SET_CLEAR_SKREF_OP(Shader, shader) +DEFINE_SET_CLEAR_SKREF_OP(ImageFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(ColorFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(MaskFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(PathEffect, effect) +#undef DEFINE_SET_CLEAR_SKREF_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +// Note that the "blur style" is packed into the OpType to prevent +// needing an additional 8 bytes for a 4-value enum. +#define DEFINE_MASK_BLUR_FILTER_OP(name, style) \ + struct SetMaskBlurFilter##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSetMaskBlurFilter##name; \ + \ + SetMaskBlurFilter##name##Op(SkScalar sigma) : sigma(sigma) {} \ + \ + SkScalar sigma; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.setMaskBlurFilter(style, sigma); \ + } \ + }; +DEFINE_MASK_BLUR_FILTER_OP(Normal, kNormal_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Solid, kSolid_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Inner, kInner_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Outer, kOuter_SkBlurStyle) +#undef DEFINE_MASK_BLUR_FILTER_OP + +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct SaveOp final : DLOp { + static const auto kType = DisplayListOpType::kSave; + + SaveOp() {} + + void dispatch(Dispatcher& dispatcher) const { dispatcher.save(); } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SaveLayerOp final : DLOp { + static const auto kType = DisplayListOpType::kSaveLayer; + + SaveLayerOp(bool with_paint) : with_paint(with_paint) {} + + bool with_paint; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.saveLayer(nullptr, with_paint); + } +}; +// 4 byte header + 20 byte payload packs evenly into 24 bytes +struct SaveLayerBoundsOp final : DLOp { + static const auto kType = DisplayListOpType::kSaveLayerBounds; + + SaveLayerBoundsOp(SkRect rect, bool with_paint) + : with_paint(with_paint), rect(rect) {} + + bool with_paint; + const SkRect rect; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.saveLayer(&rect, with_paint); + } +}; +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct RestoreOp final : DLOp { + static const auto kType = DisplayListOpType::kRestore; + + RestoreOp() {} + + void dispatch(Dispatcher& dispatcher) const { dispatcher.restore(); } +}; + +// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes +// (4 bytes unused) +struct TranslateOp final : DLOp { + static const auto kType = DisplayListOpType::kTranslate; + + TranslateOp(SkScalar tx, SkScalar ty) : tx(tx), ty(ty) {} + + const SkScalar tx; + const SkScalar ty; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.translate(tx, ty); } +}; +// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes +// (4 bytes unused) +struct ScaleOp final : DLOp { + static const auto kType = DisplayListOpType::kScale; + + ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + + const SkScalar sx; + const SkScalar sy; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.scale(sx, sy); } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct RotateOp final : DLOp { + static const auto kType = DisplayListOpType::kRotate; + + RotateOp(SkScalar degrees) : degrees(degrees) {} + + const SkScalar degrees; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.rotate(degrees); } +}; +// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes +// (4 bytes unused) +struct SkewOp final : DLOp { + static const auto kType = DisplayListOpType::kSkew; + + SkewOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + + const SkScalar sx; + const SkScalar sy; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.skew(sx, sy); } +}; +// 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes +// (4 bytes unused) +struct Transform2x3Op final : DLOp { + static const auto kType = DisplayListOpType::kTransform2x3; + + Transform2x3Op(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) + : mxx(mxx), mxy(mxy), mxt(mxt), myx(myx), myy(myy), myt(myt) {} + + const SkScalar mxx, mxy, mxt; + const SkScalar myx, myy, myt; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.transform2x3(mxx, mxy, mxt, myx, myy, myt); + } +}; +// 4 byte header + 36 byte payload packs evenly into 40 bytes +struct Transform3x3Op final : DLOp { + static const auto kType = DisplayListOpType::kTransform3x3; + + Transform3x3Op(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) + : mxx(mxx), + mxy(mxy), + mxt(mxt), + myx(myx), + myy(myy), + myt(myt), + px(px), + py(py), + pt(pt) {} + + const SkScalar mxx, mxy, mxt; + const SkScalar myx, myy, myt; + const SkScalar px, py, pt; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.transform3x3(mxx, mxy, mxt, myx, myy, myt, px, py, pt); + } +}; + +// 4 byte header + 4 byte common payload packs into minimum 8 bytes +// SkRect is 16 more bytes, which packs efficiently into 24 bytes total +// SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused) +// which packs into 64 bytes total +// SkPath is 16 more bytes, which packs efficiently into 24 bytes total +// +// We could pack the clip_op and the bool both into the free 4 bytes after +// the header, but the Windows compiler keeps wanting to expand that +// packing into more bytes than needed (even when they are declared as +// packed bit fields!) +#define DEFINE_CLIP_SHAPE_OP(shapetype, clipop) \ + struct Clip##clipop##shapetype##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kClip##clipop##shapetype; \ + \ + Clip##clipop##shapetype##Op(Sk##shapetype shape, bool is_aa) \ + : is_aa(is_aa), shape(shape) {} \ + \ + const bool is_aa; \ + const Sk##shapetype shape; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.clip##shapetype(shape, is_aa, SkClipOp::k##clipop); \ + } \ + }; +DEFINE_CLIP_SHAPE_OP(Rect, Intersect) +DEFINE_CLIP_SHAPE_OP(RRect, Intersect) +DEFINE_CLIP_SHAPE_OP(Rect, Difference) +DEFINE_CLIP_SHAPE_OP(RRect, Difference) +#undef DEFINE_CLIP_SHAPE_OP + +#define DEFINE_CLIP_PATH_OP(clipop) \ + struct Clip##clipop##PathOp final : DLOp { \ + static const auto kType = DisplayListOpType::kClip##clipop##Path; \ + \ + Clip##clipop##PathOp(SkPath path, bool is_aa) \ + : is_aa(is_aa), path(path) {} \ + \ + const bool is_aa; \ + const SkPath path; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.clipPath(path, is_aa, SkClipOp::k##clipop); \ + } \ + \ + DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \ + return is_aa == other->is_aa && path == other->path \ + ? DisplayListCompare::kEqual \ + : DisplayListCompare::kNotEqual; \ + } \ + }; +DEFINE_CLIP_PATH_OP(Intersect) +DEFINE_CLIP_PATH_OP(Difference) +#undef DEFINE_CLIP_PATH_OP + +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct DrawPaintOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawPaint; + + DrawPaintOp() {} + + void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPaint(); } +}; +// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes +// (4 bytes unused) +struct DrawColorOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawColor; + + DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} + + const SkColor color; + const SkBlendMode mode; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawColor(color, mode); + } +}; + +// The common data is a 4 byte header with an unused 4 bytes +// SkRect is 16 more bytes, using 20 bytes which rounds up to 24 bytes total +// (4 bytes unused) +// SkOval is same as SkRect +// SkRRect is 52 more bytes, which packs efficiently into 56 bytes total +#define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \ + struct Draw##op_name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##op_name; \ + \ + Draw##op_name##Op(arg_type arg_name) : arg_name(arg_name) {} \ + \ + const arg_type arg_name; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.draw##op_name(arg_name); \ + } \ + }; +DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect) +DEFINE_DRAW_1ARG_OP(Oval, SkRect, oval) +DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect) +#undef DEFINE_DRAW_1ARG_OP + +// 4 byte header + 16 byte payload uses 20 bytes but is rounded up to 24 bytes +// (4 bytes unused) +struct DrawPathOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawPath; + + DrawPathOp(SkPath path) : path(path) {} + + const SkPath path; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPath(path); } + + DisplayListCompare equals(const DrawPathOp* other) const { + return path == other->path ? DisplayListCompare::kEqual + : DisplayListCompare::kNotEqual; + } +}; + +// The common data is a 4 byte header with an unused 4 bytes +// 2 x SkPoint is 16 more bytes, using 20 bytes rounding up to 24 bytes total +// (4 bytes unused) +// SkPoint + SkScalar is 12 more bytes, packing efficiently into 16 bytes total +// 2 x SkRRect is 104 more bytes, using 108 and rounding up to 112 bytes total +// (4 bytes unused) +#define DEFINE_DRAW_2ARG_OP(op_name, type1, name1, type2, name2) \ + struct Draw##op_name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##op_name; \ + \ + Draw##op_name##Op(type1 name1, type2 name2) \ + : name1(name1), name2(name2) {} \ + \ + const type1 name1; \ + const type2 name2; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.draw##op_name(name1, name2); \ + } \ + }; +DEFINE_DRAW_2ARG_OP(Line, SkPoint, p0, SkPoint, p1) +DEFINE_DRAW_2ARG_OP(Circle, SkPoint, center, SkScalar, radius) +DEFINE_DRAW_2ARG_OP(DRRect, SkRRect, outer, SkRRect, inner) +#undef DEFINE_DRAW_2ARG_OP + +// 4 byte header + 28 byte payload packs efficiently into 32 bytes +struct DrawArcOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawArc; + + DrawArcOp(SkRect bounds, SkScalar start, SkScalar sweep, bool center) + : bounds(bounds), start(start), sweep(sweep), center(center) {} + + const SkRect bounds; + const SkScalar start; + const SkScalar sweep; + const bool center; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawArc(bounds, start, sweep, center); + } +}; + +// 4 byte header + 4 byte fixed payload packs efficiently into 8 bytes +// But then there is a list of points following the structure which +// is guaranteed to be a multiple of 8 bytes (SkPoint is 8 bytes) +// so this op will always pack efficiently +// The point type is packed into 3 different OpTypes to avoid expanding +// the fixed payload beyond the 8 bytes +#define DEFINE_DRAW_POINTS_OP(name, mode) \ + struct Draw##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##name; \ + \ + Draw##name##Op(uint32_t count) : count(count) {} \ + \ + const uint32_t count; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + const SkPoint* pts = reinterpret_cast(this + 1); \ + dispatcher.drawPoints(SkCanvas::PointMode::mode, count, pts); \ + } \ + }; +DEFINE_DRAW_POINTS_OP(Points, kPoints_PointMode); +DEFINE_DRAW_POINTS_OP(Lines, kLines_PointMode); +DEFINE_DRAW_POINTS_OP(Polygon, kPolygon_PointMode); +#undef DEFINE_DRAW_POINTS_OP + +// 4 byte header + 12 byte payload packs efficiently into 16 bytes +struct DrawVerticesOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawVertices; + + DrawVerticesOp(sk_sp vertices, SkBlendMode mode) + : mode(mode), vertices(std::move(vertices)) {} + + const SkBlendMode mode; + const sk_sp vertices; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawVertices(vertices, mode); + } +}; + +// 4 byte header + 36 byte payload packs efficiently into 40 bytes +struct DrawImageOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawImage; + + DrawImageOp(const sk_sp image, + const SkPoint& point, + const SkSamplingOptions& sampling) + : point(point), sampling(sampling), image(std::move(image)) {} + + const SkPoint point; + const SkSamplingOptions sampling; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawImage(image, point, sampling); + } +}; + +// 4 byte header + 60 byte payload packs efficiently into 64 bytes +// +// The constraint could be stored in the struct, but it would not pack +// efficiently so 2 variants are defined instead. +#define DEFINE_DRAW_IMAGE_RECT_OP(name, constraint) \ + struct Draw##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##name; \ + \ + Draw##name##Op(const sk_sp image, \ + const SkRect& src, \ + const SkRect& dst, \ + const SkSamplingOptions& sampling) \ + : src(src), dst(dst), sampling(sampling), image(std::move(image)) {} \ + \ + const SkRect src; \ + const SkRect dst; \ + const SkSamplingOptions sampling; \ + const sk_sp image; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.drawImageRect(image, src, dst, sampling, constraint); \ + } \ + }; +DEFINE_DRAW_IMAGE_RECT_OP(ImageRectStrict, SkCanvas::kStrict_SrcRectConstraint) +DEFINE_DRAW_IMAGE_RECT_OP(ImageRectFast, SkCanvas::kFast_SrcRectConstraint) +#undef DEFINE_DRAW_IMAGE_RECT_OP + +// 4 byte header + 44 byte payload packs efficiently into 48 bytes +struct DrawImageNineOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawImageNine; + + DrawImageNineOp(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) + : center(center), dst(dst), filter(filter), image(std::move(image)) {} + + const SkIRect center; + const SkRect dst; + const SkFilterMode filter; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawImageNine(image, center, dst, filter); + } +}; + +// 4 byte header + 60 byte payload packs evenly into 64 bytes +struct DrawImageLatticeOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawImageLattice; + + DrawImageLatticeOp(const sk_sp image, + int x_count, + int y_count, + int cell_count, + const SkIRect& src, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) + : with_paint(with_paint), + x_count(x_count), + y_count(y_count), + cell_count(cell_count), + filter(filter), + src(src), + dst(dst), + image(std::move(image)) {} + + const bool with_paint; + const int x_count; + const int y_count; + const int cell_count; + const SkFilterMode filter; + const SkIRect src; + const SkRect dst; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + const int* xDivs = reinterpret_cast(this + 1); + const int* yDivs = reinterpret_cast(xDivs + x_count); + const SkColor* colors = + (cell_count == 0) ? nullptr + : reinterpret_cast(yDivs + y_count); + const SkCanvas::Lattice::RectType* types = + (cell_count == 0) + ? nullptr + : reinterpret_cast(colors + + cell_count); + dispatcher.drawImageLattice( + image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, + filter, with_paint); + } +}; + +#define DRAW_ATLAS_NO_COLORS_ARRAY(tex, count) nullptr +#define DRAW_ATLAS_HAS_COLORS_ARRAY(tex, count) \ + reinterpret_cast(tex + count) + +#define DRAW_ATLAS_NO_CULLING_ARGS \ + const sk_sp atlas, int count, SkBlendMode mode, \ + const SkSamplingOptions &sampling +#define DRAW_ATLAS_NO_CULLING_INIT \ + count(count), mode(mode), sampling(sampling), atlas(std::move(atlas)) +#define DRAW_ATLAS_NO_CULLING_FIELDS \ + const int count; \ + const SkBlendMode mode; \ + const SkSamplingOptions sampling; \ + const sk_sp atlas +#define DRAW_ATLAS_NO_CULLING_P_ARG nullptr + +#define DRAW_ATLAS_HAS_CULLING_ARGS \ + DRAW_ATLAS_NO_CULLING_ARGS, const SkRect& cull +#define DRAW_ATLAS_HAS_CULLING_INIT DRAW_ATLAS_NO_CULLING_INIT, cull(cull) +#define DRAW_ATLAS_HAS_CULLING_FIELDS \ + DRAW_ATLAS_NO_CULLING_FIELDS; \ + const SkRect cull +#define DRAW_ATLAS_HAS_CULLING_P_ARG &cull + +// 4 byte header + 36 byte common payload packs efficiently into 40 bytes +// Culling version has an additional 16 bytes of payload for 56 bytes +// So all 4 versions of the base structure pack well. +// Each of these is then followed by a number of lists. +// SkRSXform list is a multiple of 16 bytes so it is always packed well +// SkRect list is also a multiple of 16 bytes so it also packs well +// SkColor list only packs well if the count is even, otherwise there +// can be 4 unusued bytes at the end. +#define DEFINE_DRAW_ATLAS_OP(name, colors, cull) \ + struct Draw##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##name; \ + \ + Draw##name##Op(DRAW_ATLAS_##cull##_ARGS) : DRAW_ATLAS_##cull##_INIT {} \ + \ + DRAW_ATLAS_##cull##_FIELDS; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + const SkRSXform* xform = reinterpret_cast(this + 1); \ + const SkRect* tex = reinterpret_cast(xform + count); \ + const SkColor* colors = DRAW_ATLAS_##colors##_ARRAY(tex, count); \ + dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, \ + DRAW_ATLAS_##cull##_P_ARG); \ + } \ + }; +DEFINE_DRAW_ATLAS_OP(Atlas, NO_COLORS, NO_CULLING) +DEFINE_DRAW_ATLAS_OP(AtlasColored, HAS_COLORS, NO_CULLING) +DEFINE_DRAW_ATLAS_OP(AtlasCulled, NO_COLORS, HAS_CULLING) +DEFINE_DRAW_ATLAS_OP(AtlasColoredCulled, HAS_COLORS, HAS_CULLING) +#undef DEFINE_DRAW_ATLAS_OP +#undef DRAW_ATLAS_NO_COLORS_ARRAY +#undef DRAW_ATLAS_HAS_COLORS_ARRAY +#undef DRAW_ATLAS_NO_CULLING_ARGS +#undef DRAW_ATLAS_NO_CULLING_INIT +#undef DRAW_ATLAS_NO_CULLING_FIELDS +#undef DRAW_ATLAS_NO_CULLING_P_ARG +#undef DRAW_ATLAS_HAS_CULLING_ARGS +#undef DRAW_ATLAS_HAS_CULLING_INIT +#undef DRAW_ATLAS_HAS_CULLING_FIELDS +#undef DRAW_ATLAS_HAS_CULLING_P_ARG + +// 4 byte header + 12 byte payload packs evenly into 16 bytes +struct DrawSkPictureOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawSkPicture; + + DrawSkPictureOp(sk_sp picture, bool with_layer) + : with_layer(with_layer), picture(std::move(picture)) {} + + const bool with_layer; + const sk_sp picture; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawPicture(picture, nullptr, with_layer); + } +}; + +// 4 byte header + 52 byte payload packs evenly into 56 bytes +struct DrawSkPictureMatrixOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawSkPictureMatrix; + + DrawSkPictureMatrixOp(sk_sp picture, + const SkMatrix matrix, + bool with_layer) + : with_layer(with_layer), picture(std::move(picture)), matrix(matrix) {} + + const bool with_layer; + const sk_sp picture; + const SkMatrix matrix; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawPicture(picture, &matrix, with_layer); + } +}; + +// 4 byte header + ptr aligned payload uses 12 bytes rounde up to 16 +// (4 bytes unused) +struct DrawDisplayListOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawDisplayList; + + DrawDisplayListOp(const sk_sp display_list) + : display_list(std::move(display_list)) {} + + sk_sp display_list; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawDisplayList(display_list); + } +}; + +// 4 byte header + 8 payload bytes + an aligned pointer take 24 bytes +// (4 unused to align the pointer) +struct DrawTextBlobOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawTextBlob; + + DrawTextBlobOp(const sk_sp blob, SkScalar x, SkScalar y) + : x(x), y(y), blob(std::move(blob)) {} + + const SkScalar x; + const SkScalar y; + const sk_sp blob; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawTextBlob(blob, x, y); + } +}; + +// 4 byte header + 28 byte payload packs evenly into 32 bytes +#define DEFINE_DRAW_SHADOW_OP(name, occludes) \ + struct Draw##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##name; \ + \ + Draw##name##Op(const SkPath& path, \ + SkColor color, \ + SkScalar elevation, \ + SkScalar dpr) \ + : color(color), elevation(elevation), dpr(dpr), path(path) {} \ + \ + const SkColor color; \ + const SkScalar elevation; \ + const SkScalar dpr; \ + const SkPath path; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.drawShadow(path, color, elevation, occludes, dpr); \ + } \ + }; +DEFINE_DRAW_SHADOW_OP(Shadow, false) +DEFINE_DRAW_SHADOW_OP(ShadowOccludes, true) +#undef DEFINE_DRAW_SHADOW_OP + +#pragma pack(pop, DLOp_Alignment) + +void DisplayList::ComputeBounds() { + DisplayListBoundsCalculator calculator(bounds_cull_); + Dispatch(calculator); + bounds_ = calculator.getBounds(); +} + +void DisplayList::Dispatch(Dispatcher& dispatcher, + uint8_t* ptr, + uint8_t* end) const { + while (ptr < end) { + auto op = (const DLOp*)ptr; + ptr += op->size; + FML_DCHECK(ptr <= end); + switch (op->type) { +#define DL_OP_DISPATCH(name) \ + case DisplayListOpType::k##name: \ + static_cast(op)->dispatch(dispatcher); \ + break; + + FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) + +#undef DL_OP_DISPATCH + + default: + FML_DCHECK(false); + return; + } + } +} + +static void DisposeOps(uint8_t* ptr, uint8_t* end) { + while (ptr < end) { + auto op = (const DLOp*)ptr; + ptr += op->size; + FML_DCHECK(ptr <= end); + switch (op->type) { +#define DL_OP_DISPOSE(name) \ + case DisplayListOpType::k##name: \ + if (!std::is_trivially_destructible_v) { \ + static_cast(op)->~name##Op(); \ + } \ + break; + + FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) + +#undef DL_OP_DISPOSE + + default: + FML_DCHECK(false); + return; + } + } +} + +static bool CompareOps(uint8_t* ptrA, + uint8_t* endA, + uint8_t* ptrB, + uint8_t* endB) { + // These conditions are checked by the caller... + FML_DCHECK((endA - ptrA) == (endB - ptrB)); + FML_DCHECK(ptrA != ptrB); + uint8_t* bulkStartA = ptrA; + uint8_t* bulkStartB = ptrB; + while (ptrA < endA && ptrB < endB) { + auto opA = (const DLOp*)ptrA; + auto opB = (const DLOp*)ptrB; + if (opA->type != opB->type || opA->size != opB->size) { + return false; + } + ptrA += opA->size; + ptrB += opB->size; + FML_DCHECK(ptrA <= endA); + FML_DCHECK(ptrB <= endB); + DisplayListCompare result; + switch (opA->type) { +#define DL_OP_EQUALS(name) \ + case DisplayListOpType::k##name: \ + result = static_cast(opA)->equals( \ + static_cast(opB)); \ + break; + + FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) + +#undef DL_OP_EQUALS + + default: + FML_DCHECK(false); + return false; + } + switch (result) { + case DisplayListCompare::kNotEqual: + return false; + case DisplayListCompare::kUseBulkCompare: + break; + case DisplayListCompare::kEqual: + // Check if we have a backlog of bytes to bulk compare and then + // reset the bulk compare pointers to the address following this op + auto bulkBytes = reinterpret_cast(opA) - bulkStartA; + if (bulkBytes > 0) { + if (memcmp(bulkStartA, bulkStartB, bulkBytes) != 0) { + return false; + } + } + bulkStartA = ptrA; + bulkStartB = ptrB; + break; + } + } + if (ptrA != endA || ptrB != endB) { + return false; + } + if (bulkStartA < ptrA) { + // Perform a final bulk compare if we have remaining bytes waiting + if (memcmp(bulkStartA, bulkStartB, ptrA - bulkStartA) != 0) { + return false; + } + } + return true; +} + +void DisplayList::RenderTo(SkCanvas* canvas) const { + DisplayListCanvasDispatcher dispatcher(canvas); + Dispatch(dispatcher); +} + +bool DisplayList::Equals(const DisplayList& other) const { + if (used_ != other.used_ || op_count_ != other.op_count_) { + return false; + } + if (ptr_ == other.ptr_) { + return true; + } + return CompareOps(ptr_, ptr_ + used_, other.ptr_, other.ptr_ + other.used_); +} + +DisplayList::DisplayList(uint8_t* ptr, + size_t used, + int op_count, + const SkRect& cull) + : ptr_(ptr), + used_(used), + op_count_(op_count), + bounds_({0, 0, -1, -1}), + bounds_cull_(cull) { + static std::atomic nextID{1}; + do { + unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed); + } while (unique_id_ == 0); +} + +DisplayList::~DisplayList() { + DisposeOps(ptr_, ptr_ + used_); +} + +#define DL_BUILDER_PAGE 4096 + +// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. +static void CopyV(void* dst) {} + +template +static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { + FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) + << "Expected " << dst << " to be aligned for at least " << alignof(S) + << " bytes."; + sk_careful_memcpy(dst, src, n * sizeof(S)); + CopyV(SkTAddOffset(dst, n * sizeof(S)), std::forward(rest)...); +} + +template +void* DisplayListBuilder::Push(size_t pod, Args&&... args) { + size_t size = SkAlignPtr(sizeof(T) + pod); + FML_DCHECK(size < (1 << 24)); + if (used_ + size > allocated_) { + static_assert(SkIsPow2(DL_BUILDER_PAGE), + "This math needs updating for non-pow2."); + // Next greater multiple of DL_BUILDER_PAGE. + allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); + storage_.realloc(allocated_); + FML_DCHECK(storage_.get()); + memset(storage_.get() + used_, 0, allocated_ - used_); + } + FML_DCHECK(used_ + size <= allocated_); + auto op = (T*)(storage_.get() + used_); + used_ += size; + new (op) T{std::forward(args)...}; + op->type = T::kType; + op->size = size; + op_count_++; + return op + 1; +} + +sk_sp DisplayListBuilder::Build() { + while (save_level_ > 0) { + restore(); + } + size_t used = used_; + int count = op_count_; + used_ = allocated_ = op_count_ = 0; + storage_.realloc(used); + return sk_sp( + new DisplayList(storage_.release(), used, count, cull_)); +} + +DisplayListBuilder::DisplayListBuilder(const SkRect& cull) : cull_(cull) {} + +DisplayListBuilder::~DisplayListBuilder() { + uint8_t* ptr = storage_.get(); + if (ptr) { + DisposeOps(ptr, ptr + used_); + } +} + +void DisplayListBuilder::setAA(bool aa) { + Push(0, aa); +} +void DisplayListBuilder::setDither(bool dither) { + Push(0, dither); +} +void DisplayListBuilder::setInvertColors(bool invert) { + Push(0, invert); +} +void DisplayListBuilder::setCaps(SkPaint::Cap cap) { + Push(0, cap); +} +void DisplayListBuilder::setJoins(SkPaint::Join join) { + Push(0, join); +} +void DisplayListBuilder::setDrawStyle(SkPaint::Style style) { + Push(0, style); +} +void DisplayListBuilder::setStrokeWidth(SkScalar width) { + Push(0, width); +} +void DisplayListBuilder::setMiterLimit(SkScalar limit) { + Push(0, limit); +} +void DisplayListBuilder::setColor(SkColor color) { + Push(0, color); +} +void DisplayListBuilder::setBlendMode(SkBlendMode mode) { + Push(0, mode); +} +void DisplayListBuilder::setBlender(sk_sp blender) { + blender // + ? Push(0, std::move(blender)) + : Push(0); +} +void DisplayListBuilder::setShader(sk_sp shader) { + shader // + ? Push(0, std::move(shader)) + : Push(0); +} +void DisplayListBuilder::setImageFilter(sk_sp filter) { + filter // + ? Push(0, std::move(filter)) + : Push(0); +} +void DisplayListBuilder::setColorFilter(sk_sp filter) { + filter // + ? Push(0, std::move(filter)) + : Push(0); +} +void DisplayListBuilder::setPathEffect(sk_sp effect) { + effect // + ? Push(0, std::move(effect)) + : Push(0); +} +void DisplayListBuilder::setMaskFilter(sk_sp filter) { + Push(0, std::move(filter)); +} +void DisplayListBuilder::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { + switch (style) { + case kNormal_SkBlurStyle: + Push(0, sigma); + break; + case kSolid_SkBlurStyle: + Push(0, sigma); + break; + case kOuter_SkBlurStyle: + Push(0, sigma); + break; + case kInner_SkBlurStyle: + Push(0, sigma); + break; + } +} + +void DisplayListBuilder::save() { + save_level_++; + Push(0); +} +void DisplayListBuilder::restore() { + if (save_level_ > 0) { + Push(0); + save_level_--; + } +} +void DisplayListBuilder::saveLayer(const SkRect* bounds, bool with_paint) { + save_level_++; + bounds // + ? Push(0, *bounds, with_paint) + : Push(0, with_paint); +} + +void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { + Push(0, tx, ty); +} +void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { + Push(0, sx, sy); +} +void DisplayListBuilder::rotate(SkScalar degrees) { + Push(0, degrees); +} +void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { + Push(0, sx, sy); +} +void DisplayListBuilder::transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + Push(0, mxx, mxy, mxt, myx, myy, myt); +} +void DisplayListBuilder::transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) { + Push(0, mxx, mxy, mxt, myx, myy, myt, px, py, pt); +} + +void DisplayListBuilder::clipRect(const SkRect& rect, + bool is_aa, + SkClipOp clip_op) { + clip_op == SkClipOp::kIntersect // + ? Push(0, rect, is_aa) + : Push(0, rect, is_aa); +} +void DisplayListBuilder::clipRRect(const SkRRect& rrect, + bool is_aa, + SkClipOp clip_op) { + if (rrect.isRect()) { + clipRect(rrect.rect(), is_aa, clip_op); + } else { + clip_op == SkClipOp::kIntersect // + ? Push(0, rrect, is_aa) + : Push(0, rrect, is_aa); + } +} +void DisplayListBuilder::clipPath(const SkPath& path, + bool is_aa, + SkClipOp clip_op) { + if (!path.isInverseFillType()) { + SkRect rect; + if (path.isRect(&rect)) { + this->clipRect(rect, is_aa, clip_op); + return; + } + SkRRect rrect; + if (path.isOval(&rect)) { + rrect.setOval(rect); + this->clipRRect(rrect, is_aa, clip_op); + return; + } + if (path.isRRect(&rrect)) { + this->clipRRect(rrect, is_aa, clip_op); + return; + } + } + clip_op == SkClipOp::kIntersect // + ? Push(0, path, is_aa) + : Push(0, path, is_aa); +} + +void DisplayListBuilder::drawPaint() { + Push(0); +} +void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { + Push(0, color, mode); +} +void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { + Push(0, p0, p1); +} +void DisplayListBuilder::drawRect(const SkRect& rect) { + Push(0, rect); +} +void DisplayListBuilder::drawOval(const SkRect& bounds) { + Push(0, bounds); +} +void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { + Push(0, center, radius); +} +void DisplayListBuilder::drawRRect(const SkRRect& rrect) { + if (rrect.isRect()) { + drawRect(rrect.rect()); + } else if (rrect.isOval()) { + drawOval(rrect.rect()); + } else { + Push(0, rrect); + } +} +void DisplayListBuilder::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + Push(0, outer, inner); +} +void DisplayListBuilder::drawPath(const SkPath& path) { + Push(0, path); +} + +void DisplayListBuilder::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + Push(0, bounds, start, sweep, useCenter); +} +void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + void* data_ptr; + FML_DCHECK(count < kMaxDrawPointsCount); + int bytes = count * sizeof(SkPoint); + switch (mode) { + case SkCanvas::PointMode::kPoints_PointMode: + data_ptr = Push(bytes, count); + break; + case SkCanvas::PointMode::kLines_PointMode: + data_ptr = Push(bytes, count); + break; + case SkCanvas::PointMode::kPolygon_PointMode: + data_ptr = Push(bytes, count); + break; + default: + FML_DCHECK(false); + return; + } + CopyV(data_ptr, pts, count); +} +void DisplayListBuilder::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + Push(0, std::move(vertices), mode); +} + +void DisplayListBuilder::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) { + Push(0, std::move(image), point, sampling); +} +void DisplayListBuilder::drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) { + constraint == SkCanvas::kFast_SrcRectConstraint // + ? Push(0, std::move(image), src, dst, sampling) + : Push(0, std::move(image), src, dst, sampling); +} +void DisplayListBuilder::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) { + Push(0, std::move(image), center, dst, filter); +} +void DisplayListBuilder::drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) { + int xDivCount = lattice.fXCount; + int yDivCount = lattice.fYCount; + FML_DCHECK((lattice.fRectTypes == nullptr) || (lattice.fColors != nullptr)); + int cellCount = lattice.fRectTypes && lattice.fColors + ? (xDivCount + 1) * (yDivCount + 1) + : 0; + size_t bytes = + (xDivCount + yDivCount) * sizeof(int) + + cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); + SkIRect src = lattice.fBounds ? *lattice.fBounds : image->bounds(); + void* pod = this->Push(bytes, std::move(image), xDivCount, + yDivCount, cellCount, src, dst, + filter, with_paint); + CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, + lattice.fColors, cellCount, lattice.fRectTypes, cellCount); +} +void DisplayListBuilder::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) { + int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); + void* data_ptr; + if (colors) { + bytes += count * sizeof(SkColor); + if (cullRect) { + data_ptr = Push(bytes, std::move(atlas), count, + mode, sampling, *cullRect); + } else { + data_ptr = Push(bytes, std::move(atlas), count, mode, + sampling); + } + CopyV(data_ptr, xform, count, tex, count, colors, count); + } else { + if (cullRect) { + data_ptr = Push(bytes, std::move(atlas), count, mode, + sampling, *cullRect); + } else { + data_ptr = + Push(bytes, std::move(atlas), count, mode, sampling); + } + CopyV(data_ptr, xform, count, tex, count); + } +} + +void DisplayListBuilder::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool with_layer) { + matrix // + ? Push(0, std::move(picture), *matrix, with_layer) + : Push(0, std::move(picture), with_layer); +} +void DisplayListBuilder::drawDisplayList( + const sk_sp display_list) { + Push(0, std::move(display_list)); +} +void DisplayListBuilder::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + Push(0, std::move(blob), x, y); +} +void DisplayListBuilder::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) { + occludes // + ? Push(0, path, color, elevation, dpr) + : Push(0, path, color, elevation, dpr); +} + +} // namespace flutter diff --git a/flow/display_list.h b/flow/display_list.h new file mode 100644 index 0000000000000..a765cfbf6ce6a --- /dev/null +++ b/flow/display_list.h @@ -0,0 +1,463 @@ +// 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_FLOW_DISPLAY_LIST_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_H_ + +#include "third_party/skia/include/core/SkBlender.h" +#include "third_party/skia/include/core/SkBlurTypes.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkPathEffect.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkShader.h" +#include "third_party/skia/include/core/SkVertices.h" + +// The Flutter DisplayList mechanism encapsulates a persistent sequence of +// rendering operations. +// +// This file contains the definitions for: +// DisplayList: the base class that holds the information about the +// sequence of operations and can dispatch them to a Dispatcher +// Dispatcher: a pure virtual interface which can be implemented to field +// the requests for purposes such as sending them to an SkCanvas +// or detecting various rendering optimization scenarios +// DisplayListBuilder: a class for constructing a DisplayList from the same +// calls defined in the Dispatcher +// +// Other files include various class definitions for dealing with display +// lists, such as: +// display_list_canvas.h: classes to interact between SkCanvas and DisplayList +// (SkCanvas->DisplayList adapter and vice versa) +// +// display_list_utils.h: various utility classes to ease implementing +// a Dispatcher, including NOP implementations of +// the attribute, clip, and transform methods, +// classes to track attributes, clips, and transforms +// and a class to compute the bounds of a DisplayList +// Any class implementing Dispatcher can inherit from +// these utility classes to simplify its creation +// +// The Flutter DisplayList mechanism can be used in place of the Skia +// SkPicture mechanism. The primary means of communication into and out +// of the DisplayList is through the Dispatcher virtual class which +// provides a nearly 1:1 translation between the records of the DisplayList +// to method calls. +// +// A DisplayList can be created directly using a DisplayListBuilder and +// the Dispatcher methods that it implements, or it can be created from +// a sequence of SkCanvas calls using the DisplayListCanvasRecorder class. +// +// A DisplayList can be read back by implementing the Dispatcher virtual +// methods (with help from some of the classes in the utils file) and +// passing an instance to the dispatch() method, or it can be rendered +// to Skia using a DisplayListCanvasDispatcher or simply by passing an +// SkCanvas pointer to its renderTo() method. +// +// The mechanism is inspired by the SkLiteDL class that is not directly +// supported by Skia, but has been recommended as a basis for custom +// display lists for a number of their customers. + +namespace flutter { + +#define FOR_EACH_DISPLAY_LIST_OP(V) \ + V(SetAA) \ + V(SetDither) \ + V(SetInvertColors) \ + \ + V(SetCaps) \ + V(SetJoins) \ + \ + V(SetDrawStyle) \ + V(SetStrokeWidth) \ + V(SetMiterLimit) \ + \ + V(SetColor) \ + V(SetBlendMode) \ + \ + V(SetBlender) \ + V(ClearBlender) \ + V(SetShader) \ + V(ClearShader) \ + V(SetColorFilter) \ + V(ClearColorFilter) \ + V(SetImageFilter) \ + V(ClearImageFilter) \ + V(SetPathEffect) \ + V(ClearPathEffect) \ + \ + V(ClearMaskFilter) \ + V(SetMaskFilter) \ + V(SetMaskBlurFilterNormal) \ + V(SetMaskBlurFilterSolid) \ + V(SetMaskBlurFilterOuter) \ + V(SetMaskBlurFilterInner) \ + \ + V(Save) \ + V(SaveLayer) \ + V(SaveLayerBounds) \ + V(Restore) \ + \ + V(Translate) \ + V(Scale) \ + V(Rotate) \ + V(Skew) \ + V(Transform2x3) \ + V(Transform3x3) \ + \ + V(ClipIntersectRect) \ + V(ClipIntersectRRect) \ + V(ClipIntersectPath) \ + V(ClipDifferenceRect) \ + V(ClipDifferenceRRect) \ + V(ClipDifferencePath) \ + \ + V(DrawPaint) \ + V(DrawColor) \ + \ + V(DrawLine) \ + V(DrawRect) \ + V(DrawOval) \ + V(DrawCircle) \ + V(DrawRRect) \ + V(DrawDRRect) \ + V(DrawArc) \ + V(DrawPath) \ + \ + V(DrawPoints) \ + V(DrawLines) \ + V(DrawPolygon) \ + V(DrawVertices) \ + \ + V(DrawImage) \ + V(DrawImageRectStrict) \ + V(DrawImageRectFast) \ + V(DrawImageNine) \ + V(DrawImageLattice) \ + V(DrawAtlas) \ + V(DrawAtlasColored) \ + V(DrawAtlasCulled) \ + V(DrawAtlasColoredCulled) \ + \ + V(DrawSkPicture) \ + V(DrawSkPictureMatrix) \ + V(DrawDisplayList) \ + V(DrawTextBlob) \ + \ + V(DrawShadow) \ + V(DrawShadowOccludes) + +#define DL_OP_TO_ENUM_VALUE(name) k##name, +enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; +#undef DL_OP_TO_ENUM_VALUE + +class Dispatcher; +class DisplayListBuilder; + +// The base class that contains a sequence of rendering operations +// for dispatch to a Dispatcher. These objects must be instantiated +// through an instance of DisplayListBuilder::build(). +class DisplayList : public SkRefCnt { + public: + static const SkSamplingOptions NearestSampling; + static const SkSamplingOptions LinearSampling; + static const SkSamplingOptions MipmapSampling; + static const SkSamplingOptions CubicSampling; + + DisplayList() + : ptr_(nullptr), + used_(0), + op_count_(0), + unique_id_(0), + bounds_({0, 0, 0, 0}), + bounds_cull_({0, 0, 0, 0}) {} + + ~DisplayList(); + + void Dispatch(Dispatcher& ctx) const { Dispatch(ctx, ptr_, ptr_ + used_); } + + void RenderTo(SkCanvas* canvas) const; + + size_t bytes() const { return used_; } + int op_count() const { return op_count_; } + uint32_t unique_id() const { return unique_id_; } + + const SkRect& bounds() { + if (bounds_.width() < 0.0) { + // ComputeBounds() will leave the variable with a + // non-negative width and height + ComputeBounds(); + } + return bounds_; + } + + bool Equals(const DisplayList& other) const; + + private: + DisplayList(uint8_t* ptr, size_t used, int op_count, const SkRect& cull_rect); + + uint8_t* ptr_; + size_t used_; + int op_count_; + + uint32_t unique_id_; + SkRect bounds_; + + // Only used for drawPaint() and drawColor() + SkRect bounds_cull_; + + void ComputeBounds(); + void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const; + + friend class DisplayListBuilder; +}; + +// The pure virtual interface for interacting with a display list. +// This interface represents the methods used to build a list +// through the DisplayListBuilder and also the methods that will +// be invoked through the DisplayList::dispatch() method. +class Dispatcher { + public: + // MaxDrawPointsCount * sizeof(SkPoint) must be less than 1 << 32 + static constexpr int kMaxDrawPointsCount = ((1 << 29) - 1); + + virtual void setAA(bool aa) = 0; + virtual void setDither(bool dither) = 0; + virtual void setInvertColors(bool invert) = 0; + virtual void setCaps(SkPaint::Cap cap) = 0; + virtual void setJoins(SkPaint::Join join) = 0; + virtual void setDrawStyle(SkPaint::Style style) = 0; + virtual void setStrokeWidth(SkScalar width) = 0; + virtual void setMiterLimit(SkScalar limit) = 0; + virtual void setColor(SkColor color) = 0; + virtual void setBlendMode(SkBlendMode mode) = 0; + virtual void setBlender(sk_sp blender) = 0; + virtual void setShader(sk_sp shader) = 0; + virtual void setImageFilter(sk_sp filter) = 0; + virtual void setColorFilter(sk_sp filter) = 0; + virtual void setPathEffect(sk_sp effect) = 0; + virtual void setMaskFilter(sk_sp filter) = 0; + virtual void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) = 0; + + virtual void save() = 0; + virtual void restore() = 0; + virtual void saveLayer(const SkRect* bounds, bool restoreWithPaint) = 0; + + virtual void translate(SkScalar tx, SkScalar ty) = 0; + virtual void scale(SkScalar sx, SkScalar sy) = 0; + virtual void rotate(SkScalar degrees) = 0; + virtual void skew(SkScalar sx, SkScalar sy) = 0; + virtual void transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) = 0; + virtual void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) = 0; + + virtual void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) = 0; + virtual void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) = 0; + virtual void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) = 0; + + virtual void drawPaint() = 0; + virtual void drawColor(SkColor color, SkBlendMode mode) = 0; + virtual void drawLine(const SkPoint& p0, const SkPoint& p1) = 0; + virtual void drawRect(const SkRect& rect) = 0; + virtual void drawOval(const SkRect& bounds) = 0; + virtual void drawCircle(const SkPoint& center, SkScalar radius) = 0; + virtual void drawRRect(const SkRRect& rrect) = 0; + virtual void drawDRRect(const SkRRect& outer, const SkRRect& inner) = 0; + virtual void drawPath(const SkPath& path) = 0; + virtual void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) = 0; + virtual void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) = 0; + virtual void drawVertices(const sk_sp vertices, + SkBlendMode mode) = 0; + virtual void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) = 0; + virtual void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) = 0; + virtual void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) = 0; + virtual void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) = 0; + virtual void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) = 0; + virtual void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool with_save_layer) = 0; + virtual void drawDisplayList(const sk_sp display_list) = 0; + virtual void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) = 0; + virtual void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) = 0; +}; + +// The primary class used to build a display list. The list of methods +// here matches the list of methods invoked during dispatch(). +// If there is some code that already renders to an SkCanvas object, +// those rendering commands can be captured into a DisplayList using +// the DisplayListCanvasRecorder class. +class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { + public: + DisplayListBuilder(const SkRect& cull = kMaxCull_); + ~DisplayListBuilder(); + + void setAA(bool aa) override; + void setDither(bool dither) override; + void setInvertColors(bool invert) override; + void setCaps(SkPaint::Cap cap) override; + void setJoins(SkPaint::Join join) override; + void setDrawStyle(SkPaint::Style style) override; + void setStrokeWidth(SkScalar width) override; + void setMiterLimit(SkScalar limit) override; + void setColor(SkColor color) override; + void setBlendMode(SkBlendMode mode) override; + void setBlender(sk_sp blender) override; + void setShader(sk_sp shader) override; + void setImageFilter(sk_sp filter) override; + void setColorFilter(sk_sp filter) override; + void setPathEffect(sk_sp effect) override; + void setMaskFilter(sk_sp filter) override; + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + + void save() override; + void restore() override; + void saveLayer(const SkRect* bounds, bool restoreWithPaint) override; + + void translate(SkScalar tx, SkScalar ty) override; + void scale(SkScalar sx, SkScalar sy) override; + void rotate(SkScalar degrees) override; + void skew(SkScalar sx, SkScalar sy) override; + void transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override; + + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override; + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override; + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override; + + void drawPaint() override; + void drawColor(SkColor color, SkBlendMode mode) override; + void drawLine(const SkPoint& p0, const SkPoint& p1) override; + void drawRect(const SkRect& rect) override; + void drawOval(const SkRect& bounds) override; + void drawCircle(const SkPoint& center, SkScalar radius) override; + void drawRRect(const SkRRect& rrect) override; + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawPath(const SkPath& path) override; + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) override; + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) override; + void drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) override; + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) override; + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool with_save_layer) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) override; + + sk_sp Build(); + + private: + SkAutoTMalloc storage_; + size_t used_ = 0; + size_t allocated_ = 0; + int op_count_ = 0; + int save_level_ = 0; + + SkRect cull_; + static constexpr SkRect kMaxCull_ = + SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); + + template + void* Push(size_t extra, Args&&... args); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_H_ diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc new file mode 100644 index 0000000000000..2da08e5406c0f --- /dev/null +++ b/flow/display_list_canvas.cc @@ -0,0 +1,493 @@ +// 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/flow/display_list_canvas.h" + +#include "flutter/flow/layers/physical_shape_layer.h" + +#include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +namespace flutter { + +void DisplayListCanvasDispatcher::save() { + canvas_->save(); +} +void DisplayListCanvasDispatcher::restore() { + canvas_->restore(); +} +void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, + bool restore_with_paint) { + canvas_->saveLayer(bounds, restore_with_paint ? &paint() : nullptr); +} + +void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { + canvas_->translate(tx, ty); +} +void DisplayListCanvasDispatcher::scale(SkScalar sx, SkScalar sy) { + canvas_->scale(sx, sy); +} +void DisplayListCanvasDispatcher::rotate(SkScalar degrees) { + canvas_->rotate(degrees); +} +void DisplayListCanvasDispatcher::skew(SkScalar sx, SkScalar sy) { + canvas_->skew(sx, sy); +} +void DisplayListCanvasDispatcher::transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1)); +} +void DisplayListCanvasDispatcher::transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) { + canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt)); +} + +void DisplayListCanvasDispatcher::clipRect(const SkRect& rect, + bool isAA, + SkClipOp clip_op) { + canvas_->clipRect(rect, clip_op, isAA); +} +void DisplayListCanvasDispatcher::clipRRect(const SkRRect& rrect, + bool isAA, + SkClipOp clip_op) { + canvas_->clipRRect(rrect, clip_op, isAA); +} +void DisplayListCanvasDispatcher::clipPath(const SkPath& path, + bool isAA, + SkClipOp clip_op) { + canvas_->clipPath(path, clip_op, isAA); +} + +void DisplayListCanvasDispatcher::drawPaint() { + canvas_->drawPaint(paint()); +} +void DisplayListCanvasDispatcher::drawColor(SkColor color, SkBlendMode mode) { + canvas_->drawColor(color, mode); +} +void DisplayListCanvasDispatcher::drawLine(const SkPoint& p0, + const SkPoint& p1) { + canvas_->drawLine(p0, p1, paint()); +} +void DisplayListCanvasDispatcher::drawRect(const SkRect& rect) { + canvas_->drawRect(rect, paint()); +} +void DisplayListCanvasDispatcher::drawOval(const SkRect& bounds) { + canvas_->drawOval(bounds, paint()); +} +void DisplayListCanvasDispatcher::drawCircle(const SkPoint& center, + SkScalar radius) { + canvas_->drawCircle(center, radius, paint()); +} +void DisplayListCanvasDispatcher::drawRRect(const SkRRect& rrect) { + canvas_->drawRRect(rrect, paint()); +} +void DisplayListCanvasDispatcher::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + canvas_->drawDRRect(outer, inner, paint()); +} +void DisplayListCanvasDispatcher::drawPath(const SkPath& path) { + canvas_->drawPath(path, paint()); +} +void DisplayListCanvasDispatcher::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + canvas_->drawArc(bounds, start, sweep, useCenter, paint()); +} +void DisplayListCanvasDispatcher::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + canvas_->drawPoints(mode, count, pts, paint()); +} +void DisplayListCanvasDispatcher::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + canvas_->drawVertices(vertices, mode, paint()); +} +void DisplayListCanvasDispatcher::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) { + canvas_->drawImage(image, point.fX, point.fY, sampling, &paint()); +} +void DisplayListCanvasDispatcher::drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) { + canvas_->drawImageRect(image, src, dst, sampling, &paint(), constraint); +} +void DisplayListCanvasDispatcher::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) { + canvas_->drawImageNine(image.get(), center, dst, filter, &paint()); +} +void DisplayListCanvasDispatcher::drawImageLattice( + const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) { + canvas_->drawImageLattice(image.get(), lattice, dst, filter, + with_paint ? &paint() : nullptr); +} +void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) { + canvas_->drawAtlas(atlas.get(), xform, tex, colors, count, mode, sampling, + cullRect, &paint()); +} +void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool with_save_layer) { + canvas_->drawPicture(picture, matrix, with_save_layer ? &paint() : nullptr); +} +void DisplayListCanvasDispatcher::drawDisplayList( + const sk_sp display_list) { + int save_count = canvas_->save(); + { + DisplayListCanvasDispatcher dispatcher(canvas_); + display_list->Dispatch(dispatcher); + } + canvas_->restoreToCount(save_count); +} +void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + canvas_->drawTextBlob(blob, x, y, paint()); +} +void DisplayListCanvasDispatcher::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) { + flutter::PhysicalShapeLayer::DrawShadow(canvas_, path, color, elevation, + occludes, dpr); +} + +DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) + : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), + builder_(sk_make_sp(bounds)) {} + +sk_sp DisplayListCanvasRecorder::Build() { + sk_sp display_list = builder_->Build(); + builder_.reset(); + return display_list; +} + +void DisplayListCanvasRecorder::didConcat44(const SkM44& m44) { + SkMatrix m = m44.asM33(); + if (m.hasPerspective()) { + builder_->transform3x3(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], + m[8]); + } else { + builder_->transform2x3(m[0], m[1], m[2], m[3], m[4], m[5]); + } +} +void DisplayListCanvasRecorder::didTranslate(SkScalar tx, SkScalar ty) { + builder_->translate(tx, ty); +} +void DisplayListCanvasRecorder::didScale(SkScalar sx, SkScalar sy) { + builder_->scale(sx, sy); +} + +void DisplayListCanvasRecorder::onClipRect(const SkRect& rect, + SkClipOp clip_op, + ClipEdgeStyle edgeStyle) { + builder_->clipRect(rect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, + clip_op); +} +void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, + SkClipOp clip_op, + ClipEdgeStyle edgeStyle) { + builder_->clipRRect(rrect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, + clip_op); +} +void DisplayListCanvasRecorder::onClipPath(const SkPath& path, + SkClipOp clip_op, + ClipEdgeStyle edgeStyle) { + builder_->clipPath(path, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, + clip_op); +} + +void DisplayListCanvasRecorder::willSave() { + builder_->save(); +} +SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( + const SaveLayerRec& rec) { + if (rec.fPaint) { + RecordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType); + builder_->saveLayer(rec.fBounds, true); + } else { + builder_->saveLayer(rec.fBounds, false); + } + return SaveLayerStrategy::kNoLayer_SaveLayerStrategy; +} +void DisplayListCanvasRecorder::didRestore() { + builder_->restore(); +} + +void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kFillOpType); + builder_->drawPaint(); +} +void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawRect(rect); +} +void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawRRect(rrect); +} +void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawDRRect(outer, inner); +} +void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawOval(rect); +} +void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawArc(rect, startAngle, sweepAngle, useCenter); +} +void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawPath(path); +} + +void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kStrokeOpType); + if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { + builder_->drawLine(pts[0], pts[1]); + } else { + uint32_t count32 = static_cast(count); + // TODO(flar): depending on the mode we could break it down into + // multiple calls to drawPoints, but how much do we really want + // to support more than a couple billion points? + FML_DCHECK(count32 == count); + builder_->drawPoints(mode, count32, pts); + } +} +void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, + SkBlendMode mode, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawVertices(sk_ref_sp(vertices), mode); +} + +void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, + SkScalar dx, + SkScalar dy, + const SkSamplingOptions& sampling, + const SkPaint* paint) { + RecordPaintAttributes(paint, DrawType::kImageOpType); + builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling); +} +void DisplayListCanvasRecorder::onDrawImageRect2( + const SkImage* image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + const SkPaint* paint, + SrcRectConstraint constraint) { + RecordPaintAttributes(paint, DrawType::kImageRectOpType); + builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, constraint); +} +void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, + const Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + const SkPaint* paint) { + if (paint != nullptr) { + // SkCanvas will always construct a paint, + // though it is a default paint most of the time + SkPaint default_paint; + if (*paint == default_paint) { + paint = nullptr; + } else { + RecordPaintAttributes(paint, DrawType::kImageOpType); + } + } + builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, + paint != nullptr); +} +void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, + const SkRSXform xform[], + const SkRect src[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cull, + const SkPaint* paint) { + RecordPaintAttributes(paint, DrawType::kImageOpType); + builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, + sampling, cull); +} + +void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) { + RecordPaintAttributes(&paint, DrawType::kDrawOpType); + builder_->drawTextBlob(sk_ref_sp(blob), x, y); +} +void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, + const SkDrawShadowRec& rec) { + // Skia does not expose the SkDrawShadowRec structure in a public + // header file so we cannot record this operation. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + FML_DCHECK(false); +} + +void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) { + if (paint) { + RecordPaintAttributes(paint, DrawType::kSaveLayerOpType); + } + builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); +} + +void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint, + DrawType type) { + int dataNeeded; + switch (type) { + case DrawType::kDrawOpType: + dataNeeded = kDrawMask_; + break; + case DrawType::kFillOpType: + dataNeeded = kPaintMask_; + break; + case DrawType::kStrokeOpType: + dataNeeded = kStrokeMask_; + break; + case DrawType::kImageOpType: + dataNeeded = kImageMask_; + break; + case DrawType::kImageRectOpType: + dataNeeded = kImageRectMask_; + break; + case DrawType::kSaveLayerOpType: + dataNeeded = kSaveLayerMask_; + break; + default: + FML_DCHECK(false); + return; + } + if (paint == nullptr) { + paint = new SkPaint(); + } + if ((dataNeeded & kAaNeeded_) != 0 && current_aa_ != paint->isAntiAlias()) { + builder_->setAA(current_aa_ = paint->isAntiAlias()); + } + if ((dataNeeded & kDitherNeeded_) != 0 && + current_dither_ != paint->isDither()) { + builder_->setDither(current_dither_ = paint->isDither()); + } + if ((dataNeeded & kColorNeeded_) != 0 && + current_color_ != paint->getColor()) { + builder_->setColor(current_color_ = paint->getColor()); + } + if ((dataNeeded & kBlendNeeded_)) { + skstd::optional mode_optional = paint->asBlendMode(); + if (mode_optional) { + SkBlendMode mode = mode_optional.value(); + if (current_blender_ || current_blend_ != mode) { + builder_->setBlendMode(current_blend_ = mode); + current_blender_ = nullptr; + } + } else { + if (current_blender_.get() != paint->getBlender()) { + builder_->setBlender(current_blender_ = sk_ref_sp(paint->getBlender())); + } + } + } + // invert colors is a Flutter::Paint thing, not an SkPaint thing + // if ((dataNeeded & invertColorsNeeded_) != 0 && + // currentInvertColors_ != paint->???) { + // currentInvertColors_ = paint->invertColors; + // addOp_(currentInvertColors_ + // ? _CanvasOp.setInvertColors + // : _CanvasOp.clearInvertColors, 0); + // } + if ((dataNeeded & kPaintStyleNeeded_) != 0) { + if (current_style_ != paint->getStyle()) { + builder_->setDrawStyle(current_style_ = paint->getStyle()); + } + if (current_style_ == SkPaint::Style::kStroke_Style) { + dataNeeded |= kStrokeStyleNeeded_; + } + } + if ((dataNeeded & kStrokeStyleNeeded_) != 0) { + if (current_stroke_width_ != paint->getStrokeWidth()) { + builder_->setStrokeWidth(current_stroke_width_ = paint->getStrokeWidth()); + } + if (current_cap_ != paint->getStrokeCap()) { + builder_->setCaps(current_cap_ = paint->getStrokeCap()); + } + if (current_join_ != paint->getStrokeJoin()) { + builder_->setJoins(current_join_ = paint->getStrokeJoin()); + } + if (current_miter_limit_ != paint->getStrokeMiter()) { + builder_->setMiterLimit(current_miter_limit_ = paint->getStrokeMiter()); + } + } + if ((dataNeeded & kShaderNeeded_) != 0 && + current_shader_.get() != paint->getShader()) { + builder_->setShader(current_shader_ = sk_ref_sp(paint->getShader())); + } + if ((dataNeeded & kColorFilterNeeded_) != 0 && + current_color_filter_.get() != paint->getColorFilter()) { + builder_->setColorFilter(current_color_filter_ = + sk_ref_sp(paint->getColorFilter())); + } + if ((dataNeeded & kImageFilterNeeded_) != 0 && + current_image_filter_.get() != paint->getImageFilter()) { + builder_->setImageFilter(current_image_filter_ = + sk_ref_sp(paint->getImageFilter())); + } + if ((dataNeeded & kPathEffectNeeded_) != 0 && + current_path_effect_.get() != paint->getPathEffect()) { + builder_->setPathEffect(current_path_effect_ = + sk_ref_sp(paint->getPathEffect())); + } + if ((dataNeeded & kMaskFilterNeeded_) != 0 && + current_mask_filter_.get() != paint->getMaskFilter()) { + builder_->setMaskFilter(current_mask_filter_ = + sk_ref_sp(paint->getMaskFilter())); + } +} + +} // namespace flutter diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h new file mode 100644 index 0000000000000..aa99ac8dc1238 --- /dev/null +++ b/flow/display_list_canvas.h @@ -0,0 +1,314 @@ +// 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_FLOW_DISPLAY_LIST_CANVAS_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ + +#include "flutter/flow/display_list.h" +#include "flutter/flow/display_list_utils.h" +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" +#include "third_party/skia/include/utils/SkNoDrawCanvas.h" + +// Classes to interact between SkCanvas and DisplayList, including: +// DisplayListCanvasDispatcher: +// Can be fed to the dispatch() method of a DisplayList to feed +// the resulting rendering operations to an SkCanvas instance. +// DisplayListCanvasRecorder +// An adapter that implements an SkCanvas interface which can +// then be handed to code that outputs to an SkCanvas to capture +// the output into a Flutter DisplayList. + +namespace flutter { + +// Receives all methods on Dispatcher and sends them to an SkCanvas +class DisplayListCanvasDispatcher : public virtual Dispatcher, + public SkPaintDispatchHelper { + public: + DisplayListCanvasDispatcher(SkCanvas* canvas) : canvas_(canvas) {} + + void save() override; + void restore() override; + void saveLayer(const SkRect* bounds, bool restore_with_paint) override; + + void translate(SkScalar tx, SkScalar ty) override; + void scale(SkScalar sx, SkScalar sy) override; + void rotate(SkScalar degrees) override; + void skew(SkScalar sx, SkScalar sy) override; + void transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override; + + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override; + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override; + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override; + + void drawPaint() override; + void drawColor(SkColor color, SkBlendMode mode) override; + void drawLine(const SkPoint& p0, const SkPoint& p1) override; + void drawRect(const SkRect& rect) override; + void drawOval(const SkRect& bounds) override; + void drawCircle(const SkPoint& center, SkScalar radius) override; + void drawRRect(const SkRRect& rrect) override; + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawPath(const SkPath& path) override; + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) override; + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) override; + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) override; + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) override; + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool with_save_layer) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) override; + + private: + SkCanvas* canvas_; +}; + +// Receives all methods on SkCanvas and sends them to a DisplayListBuilder +class DisplayListCanvasRecorder + : public SkCanvasVirtualEnforcer, + public SkRefCnt { + public: + DisplayListCanvasRecorder(const SkRect& bounds); + + const sk_sp builder() { return builder_; } + + sk_sp Build(); + + void didConcat44(const SkM44&) override; + void didSetM44(const SkM44&) override { FML_DCHECK(false); } + void didTranslate(SkScalar, SkScalar) override; + void didScale(SkScalar, SkScalar) override; + + void onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + void onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + void onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; + void didRestore() override; + + void onDrawPaint(const SkPaint& paint) override; + void onDrawBehind(const SkPaint&) override { FML_DCHECK(false); } + void onDrawRect(const SkRect& rect, const SkPaint& paint) override; + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override; + void onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint& paint) override; + void onDrawOval(const SkRect& rect, const SkPaint& paint) override; + void onDrawArc(const SkRect& rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const SkPaint& paint) override; + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + void onDrawRegion(const SkRegion& region, const SkPaint& paint) override { + FML_DCHECK(false); + } + + void onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + + void onDrawPatch(const SkPoint cubics[12], + const SkColor colors[4], + const SkPoint texCoords[4], + SkBlendMode mode, + const SkPaint& paint) override { + FML_DCHECK(false); + } + void onDrawPoints(SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) override; + void onDrawVerticesObject(const SkVertices* vertices, + SkBlendMode mode, + const SkPaint& paint) override; + + void onDrawImage2(const SkImage*, + SkScalar dx, + SkScalar dy, + const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, + const Lattice&, + const SkRect& dst, + SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, + const SkRSXform[], + const SkRect src[], + const SkColor[], + int count, + SkBlendMode, + const SkSamplingOptions&, + const SkRect* cull, + const SkPaint*) override; + + void onDrawEdgeAAQuad(const SkRect& rect, + const SkPoint clip[4], + SkCanvas::QuadAAFlags aaFlags, + const SkColor4f& color, + SkBlendMode mode) override { + FML_DCHECK(0); + } + + void onDrawAnnotation(const SkRect& rect, + const char key[], + SkData* value) override { + FML_DCHECK(false); + } + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + FML_DCHECK(false); + } + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override; + + enum class DrawType { + // The operation will be an image operation + kImageOpType, + // The operation will be an imageRect operation + kImageRectOpType, + // The operation will be a fill or stroke depending on the paint.style + kDrawOpType, + // The operation will be a fill (ignoring paint.style) + kFillOpType, + // The operation will be a stroke (ignoring paint.style) + kStrokeOpType, + // The operation will be a saveLayer with a paint object + kSaveLayerOpType, + }; + + void RecordPaintAttributes(const SkPaint* paint, DrawType type); + + private: + sk_sp builder_; + + // Mask bits for the various attributes that might be needed for a given + // operation. + // clang-format off + static constexpr int kAaNeeded_ = 1 << 0; + static constexpr int kColorNeeded_ = 1 << 1; + static constexpr int kBlendNeeded_ = 1 << 2; + static constexpr int kInvertColorsNeeded_ = 1 << 3; + static constexpr int kPaintStyleNeeded_ = 1 << 4; + static constexpr int kStrokeStyleNeeded_ = 1 << 5; + static constexpr int kShaderNeeded_ = 1 << 6; + static constexpr int kColorFilterNeeded_ = 1 << 7; + static constexpr int kImageFilterNeeded_ = 1 << 8; + static constexpr int kPathEffectNeeded_ = 1 << 9; + static constexpr int kMaskFilterNeeded_ = 1 << 10; + static constexpr int kDitherNeeded_ = 1 << 11; + // clang-format on + + // Combinations of the above mask bits that are common to typical "draw" + // calls. + // Note that the strokeStyle_ is handled conditionally depending on whether + // the paintStyle_ attribute value is synchronized. It can also be manually + // specified for operations that will be always stroking, like [drawLine]. + static constexpr int kPaintMask_ = kAaNeeded_ | kColorNeeded_ | + kBlendNeeded_ | kInvertColorsNeeded_ | + kColorFilterNeeded_ | kShaderNeeded_ | + kDitherNeeded_ | kImageFilterNeeded_; + static constexpr int kDrawMask_ = kPaintMask_ | kPaintStyleNeeded_ | + kMaskFilterNeeded_ | kPathEffectNeeded_; + static constexpr int kStrokeMask_ = kPaintMask_ | kStrokeStyleNeeded_ | + kMaskFilterNeeded_ | kPathEffectNeeded_; + static constexpr int kImageMask_ = kColorNeeded_ | kBlendNeeded_ | + kInvertColorsNeeded_ | + kColorFilterNeeded_ | kDitherNeeded_ | + kImageFilterNeeded_ | kMaskFilterNeeded_; + static constexpr int kImageRectMask_ = kImageMask_ | kAaNeeded_; + static constexpr int kSaveLayerMask_ = + kColorNeeded_ | kBlendNeeded_ | kInvertColorsNeeded_ | + kColorFilterNeeded_ | kImageFilterNeeded_; + + bool current_aa_ = false; + bool current_dither_ = false; + SkColor current_color_ = 0xFF000000; + SkBlendMode current_blend_ = SkBlendMode::kSrcOver; + SkPaint::Style current_style_ = SkPaint::Style::kFill_Style; + SkScalar current_stroke_width_ = 0.0; + SkScalar current_miter_limit_ = 4.0; + SkPaint::Cap current_cap_ = SkPaint::Cap::kButt_Cap; + SkPaint::Join current_join_ = SkPaint::Join::kMiter_Join; + sk_sp current_blender_; + sk_sp current_shader_; + sk_sp current_color_filter_; + sk_sp current_image_filter_; + sk_sp current_path_effect_; + sk_sp current_mask_filter_; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc new file mode 100644 index 0000000000000..8084bc0f8973d --- /dev/null +++ b/flow/display_list_canvas_unittests.cc @@ -0,0 +1,1515 @@ +// 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/flow/display_list_canvas.h" +#include "flutter/flow/layers/physical_shape_layer.h" + +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" +#include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkBlenders.h" +#include "third_party/skia/include/effects/SkDashPathEffect.h" +#include "third_party/skia/include/effects/SkDiscretePathEffect.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "third_party/skia/include/effects/SkImageFilters.h" + +#include + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +constexpr int TestWidth = 200; +constexpr int TestHeight = 200; +constexpr int RenderWidth = 100; +constexpr int RenderHeight = 100; +constexpr int RenderLeft = (TestWidth - RenderWidth) / 2; +constexpr int RenderTop = (TestHeight - RenderHeight) / 2; +constexpr int RenderRight = RenderLeft + RenderWidth; +constexpr int RenderBottom = RenderTop + RenderHeight; +constexpr int RenderCenterX = (RenderLeft + RenderRight) / 2; +constexpr int RenderCenterY = (RenderTop + RenderBottom) / 2; +constexpr SkScalar RenderRadius = std::min(RenderWidth, RenderHeight) / 2.0; +constexpr SkScalar RenderCornerRadius = RenderRadius / 5.0; + +constexpr SkPoint TestCenter = SkPoint::Make(TestWidth / 2, TestHeight / 2); +constexpr SkRect TestBounds = SkRect::MakeWH(TestWidth, TestHeight); +constexpr SkRect RenderBounds = + SkRect::MakeLTRB(RenderLeft, RenderTop, RenderRight, RenderBottom); + +class CanvasCompareTester { + private: + // If a test is using any shadow operations then we cannot currently + // record those in an SkCanvas and play it back into a DisplayList + // because internally the operation gets encapsulated in a Skia + // ShadowRec which is not exposed by their headers. For operations + // that use shadows, we can perform a lot of tests, but not the tests + // that require SkCanvas->DisplayList transfers. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + static bool TestingDrawShadows; + // The CPU renders nothing for drawVertices with a Blender. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 + static bool TestingDrawVertices; + // The CPU renders nothing for drawAtlas with a Blender. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 + static bool TestingDrawAtlas; + + public: + typedef const std::function CvRenderer; + typedef const std::function DlRenderer; + + // All of the tests should eventually use this method except for the + // tests that call |RenderNoAttributes| because they do not use the + // SkPaint object. + // But there are a couple of conditions beyond our control which require + // the use of one of the variant methods below (|RenderShadows|, + // |RenderVertices|, |RenderAtlas|). + static void RenderAll(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + RenderWithAttributes(cv_renderer, dl_renderer); + RenderWithTransforms(cv_renderer, dl_renderer); + RenderWithClips(cv_renderer, dl_renderer); + } + + // Used by the tests that render shadows to deal with a condition where + // we cannot recapture the shadow information from an SkCanvas stream + // due to the DrawShadowRec used by Skia is not properly exported. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + static void RenderShadows(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + TestingDrawShadows = true; + RenderNoAttributes(cv_renderer, dl_renderer); + TestingDrawShadows = false; + } + + // Used by the tests that call drawVertices to avoid using an SkBlender + // during testing because the CPU renderer appears not to render anything. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12200 + static void RenderVertices(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + TestingDrawVertices = true; + RenderAll(cv_renderer, dl_renderer); + TestingDrawVertices = false; + } + + // Used by the tests that call drawAtlas to avoid using an SkBlender + // during testing because the CPU renderer appears not to render anything. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12199 + static void RenderAtlas(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + TestingDrawAtlas = true; + RenderAll(cv_renderer, dl_renderer); + TestingDrawAtlas = false; + } + + // Used by the tests that call a draw method that does not take a paint + // call. Those tests could use |RenderAll| but there would be a lot of + // wasted test runs that prepare an SkPaint that is never used. + static void RenderNoAttributes(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + RenderWith([=](SkCanvas*, SkPaint& p) {}, // + [=](DisplayListBuilder& d) {}, // + cv_renderer, dl_renderer, "Base Test"); + RenderWithTransforms(cv_renderer, dl_renderer); + RenderWithClips(cv_renderer, dl_renderer); + } + + static void RenderWithSaveRestore(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + SkRect clip = SkRect::MakeLTRB(0, 0, 10, 10); + SkRect rect = SkRect::MakeLTRB(5, 5, 15, 15); + SkColor save_layer_color = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); + RenderWith( + [=](SkCanvas* cv, SkPaint& p) { + cv->save(); + cv->clipRect(clip, SkClipOp::kIntersect, false); + cv->drawRect(rect, p); + cv->restore(); + }, + [=](DisplayListBuilder& b) { + b.save(); + b.clipRect(clip, false, SkClipOp::kIntersect); + b.drawRect(rect); + b.restore(); + }, + cv_renderer, dl_renderer, "With prior save/clip/restore"); + RenderWith( + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColor(save_layer_color); + cv->saveLayer(RenderBounds, &save_p); + cv->drawRect(rect, p); + }, + [=](DisplayListBuilder& b) { + b.setColor(save_layer_color); + b.saveLayer(&RenderBounds, true); + b.setColor(SkPaint().getColor()); + b.drawRect(rect); + }, + cv_renderer, dl_renderer, "With saveLayer"); + } + + static void RenderWithAttributes(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + RenderWith([=](SkCanvas*, SkPaint& p) {}, // + [=](DisplayListBuilder& d) {}, // + cv_renderer, dl_renderer, "Base Test"); + + RenderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(true); }, // + [=](DisplayListBuilder& b) { b.setAA(true); }, // + cv_renderer, dl_renderer, "AA == True"); + RenderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(false); }, // + [=](DisplayListBuilder& b) { b.setAA(false); }, // + cv_renderer, dl_renderer, "AA == False"); + + RenderWith([=](SkCanvas*, SkPaint& p) { p.setDither(true); }, // + [=](DisplayListBuilder& b) { b.setDither(true); }, // + cv_renderer, dl_renderer, "Dither == True"); + RenderWith([=](SkCanvas*, SkPaint& p) { p.setDither(false); }, // + [=](DisplayListBuilder& b) { b.setDither(false); }, // + cv_renderer, dl_renderer, "Dither = False"); + + RenderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, // + [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); }, // + cv_renderer, dl_renderer, "Color == Blue"); + RenderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, // + [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); }, // + cv_renderer, dl_renderer, "Color == Green"); + + RenderWithStrokes(cv_renderer, dl_renderer); + + { + // half opaque cyan + SkColor blendableColor = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); + SkColor bg = SK_ColorWHITE; + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kSrcIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kSrcIn); + b.setColor(blendableColor); + }, + cv_renderer, dl_renderer, "Blend == SrcIn", &bg); + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kDstIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kDstIn); + b.setColor(blendableColor); + }, + cv_renderer, dl_renderer, "Blend == DstIn", &bg); + } + + if (!(TestingDrawAtlas || TestingDrawVertices)) { + sk_sp blender = + SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, false); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); }, + cv_renderer, dl_renderer, + "ImageFilter == Blender Arithmetic 0.25-false"); + } + ASSERT_TRUE(blender->unique()) << "Blender Cleanup"; + blender = SkBlenders::Arithmetic(0.25, 0.25, 0.25, 0.25, true); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setBlender(blender); }, + [=](DisplayListBuilder& b) { b.setBlender(blender); }, + cv_renderer, dl_renderer, + "ImageFilter == Blender Arithmetic 0.25-true"); + } + ASSERT_TRUE(blender->unique()) << "Blender Cleanup"; + } + + { + sk_sp filter = + SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setImageFilter(filter); }, + [=](DisplayListBuilder& b) { b.setImageFilter(filter); }, + cv_renderer, dl_renderer, "ImageFilter == Decal Blur 5"); + } + ASSERT_TRUE(filter->unique()) << "ImageFilter Cleanup"; + filter = + SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr, nullptr); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setImageFilter(filter); }, + [=](DisplayListBuilder& b) { b.setImageFilter(filter); }, + cv_renderer, dl_renderer, "ImageFilter == Clamp Blur 5"); + } + ASSERT_TRUE(filter->unique()) << "ImageFilter Cleanup"; + } + + { + // clang-format off + constexpr float rotate_color_matrix[20] = { + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, + }; + constexpr float invert_color_matrix[20] = { + -1.0, 0, 0, 1.0, 0, + 0, -1.0, 0, 1.0, 0, + 0, 0, -1.0, 1.0, 0, + 1.0, 1.0, 1.0, 1.0, 0, + }; + // clang-format on + sk_sp filter = SkColorFilters::Matrix(rotate_color_matrix); + { + SkColor bg = SK_ColorWHITE; + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setColorFilter(filter); + }, + cv_renderer, dl_renderer, "ColorFilter == RotateRGB", &bg); + } + ASSERT_TRUE(filter->unique()) << "ColorFilter Cleanup"; + filter = SkColorFilters::Matrix(invert_color_matrix); + { + SkColor bg = SK_ColorWHITE; + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setInvertColors(true); + }, + cv_renderer, dl_renderer, "ColorFilter == Invert", &bg); + } + ASSERT_TRUE(filter->unique()) << "ColorFilter Cleanup"; + } + + { + // Discrete path effects need a stroke width for drawPointsAsPoints + // to do something realistic + sk_sp effect = SkDiscretePathEffect::Make(3, 5); + { + // Discrete path effects need a stroke width for drawPointsAsPoints + // to do something realistic + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + }, + cv_renderer, dl_renderer, "PathEffect == Discrete-3-5"); + } + ASSERT_TRUE(effect->unique()) << "PathEffect Cleanup"; + effect = SkDiscretePathEffect::Make(2, 3); + { + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + }, + cv_renderer, dl_renderer, "PathEffect == Discrete-2-3"); + } + ASSERT_TRUE(effect->unique()) << "PathEffect Cleanup"; + } + + { + sk_sp filter = + SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, + [=](DisplayListBuilder& b) { b.setMaskFilter(filter); }, + cv_renderer, dl_renderer, "MaskFilter == Blur 5"); + } + ASSERT_TRUE(filter->unique()) << "MaskFilter Cleanup"; + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, + [=](DisplayListBuilder& b) { + b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); + }, + cv_renderer, dl_renderer, "MaskFilter == Blur(Normal, 5.0)"); + } + ASSERT_TRUE(filter->unique()) << "MaskFilter Cleanup"; + } + + { + SkPoint end_points[] = { + SkPoint::Make(RenderBounds.fLeft, RenderBounds.fTop), + SkPoint::Make(RenderBounds.fRight, RenderBounds.fBottom), + }; + SkColor colors[] = { + SK_ColorGREEN, + SK_ColorYELLOW, + SK_ColorBLUE, + }; + float stops[] = { + 0.0, + 0.5, + 1.0, + }; + sk_sp shader = SkGradientShader::MakeLinear( + end_points, colors, stops, 3, SkTileMode::kMirror, 0, nullptr); + { + RenderWith([=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, + [=](DisplayListBuilder& b) { b.setShader(shader); }, + cv_renderer, dl_renderer, "LinearGradient GYB"); + } + ASSERT_TRUE(shader->unique()) << "Shader Cleanup"; + } + } + + static void RenderWithStrokes(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + RenderWith( + [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); }, + [=](DisplayListBuilder& b) { b.setDrawStyle(SkPaint::kFill_Style); }, + cv_renderer, dl_renderer, "Fill"); + RenderWith( + [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); }, + [=](DisplayListBuilder& b) { b.setDrawStyle(SkPaint::kStroke_Style); }, + cv_renderer, dl_renderer, "Stroke + defaults"); + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kFill_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kFill_Style); + b.setStrokeWidth(10.0); + }, + cv_renderer, dl_renderer, "Fill + unnecessary StrokeWidth 10"); + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(10.0); + }, + cv_renderer, dl_renderer, "Stroke Width 10"); + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + }, + cv_renderer, dl_renderer, "Stroke Width 5"); + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kButt_Cap); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setCaps(SkPaint::kButt_Cap); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Butt Cap"); + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kRound_Cap); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setCaps(SkPaint::kRound_Cap); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Round Cap"); + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kBevel_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setJoins(SkPaint::kBevel_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Bevel Join"); + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kRound_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setJoins(SkPaint::kRound_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Round Join"); + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(100.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setMiterLimit(100.0); + b.setJoins(SkPaint::kMiter_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Miter 100"); + + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(0.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setMiterLimit(0.0); + b.setJoins(SkPaint::kMiter_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Miter 0"); + + { + const SkScalar TestDashes1[] = {4.0, 2.0}; + const SkScalar TestDashes2[] = {1.0, 1.5}; + sk_sp effect = SkDashPathEffect::Make(TestDashes1, 2, 0.0f); + { + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + }, + cv_renderer, dl_renderer, "PathEffect == Dash-4-2"); + } + ASSERT_TRUE(effect->unique()) << "PathEffect Cleanup"; + effect = SkDashPathEffect::Make(TestDashes2, 2, 0.0f); + { + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setPathEffect(effect); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setPathEffect(effect); + }, + cv_renderer, dl_renderer, "PathEffect == Dash-1-1.5"); + } + ASSERT_TRUE(effect->unique()) << "PathEffect Cleanup"; + } + } + + static void RenderWithTransforms(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + RenderWith([=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, // + [=](DisplayListBuilder& b) { b.translate(5, 10); }, // + cv_renderer, dl_renderer, "Translate 5, 10"); + RenderWith([=](SkCanvas* c, SkPaint&) { c->scale(1.05, 1.05); }, // + [=](DisplayListBuilder& b) { b.scale(1.05, 1.05); }, // + cv_renderer, dl_renderer, "Scale +5%"); + RenderWith([=](SkCanvas* c, SkPaint&) { c->rotate(5); }, // + [=](DisplayListBuilder& b) { b.rotate(5); }, // + cv_renderer, dl_renderer, "Rotate 5 degrees"); + RenderWith([=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, // + [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); }, // + cv_renderer, dl_renderer, "Skew 5%"); + { + SkMatrix tx = SkMatrix::MakeAll(1.1, 0.1, 1.05, 0.05, 1, 1, 0, 0, 1); + RenderWith([=](SkCanvas* c, SkPaint&) { c->concat(tx); }, // + [=](DisplayListBuilder& b) { + b.transform2x3(tx[0], tx[1], tx[2], // + tx[3], tx[4], tx[5]); + }, // + cv_renderer, dl_renderer, "Transform 2x3"); + } + { + SkMatrix tx = SkMatrix::MakeAll(1.1, 0.1, 1.05, 0.05, 1, 1, 0, 0, 1.01); + RenderWith([=](SkCanvas* c, SkPaint&) { c->concat(tx); }, // + [=](DisplayListBuilder& b) { + b.transform3x3(tx[0], tx[1], tx[2], // + tx[3], tx[4], tx[5], // + tx[6], tx[7], tx[8]); + }, // + cv_renderer, dl_renderer, "Transform 3x3"); + } + } + + static void RenderWithClips(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + SkRect r_clip = RenderBounds.makeInset(15.5, 15.5); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, false, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "Hard ClipRect inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, true, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "AA ClipRect inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, false, SkClipOp::kDifference); + }, + cv_renderer, dl_renderer, "Hard ClipRect Diff, inset by 15.5"); + SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 1.8, 2.7); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, false, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "Hard ClipRRect inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, true, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "AA ClipRRect inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, false, SkClipOp::kDifference); + }, + cv_renderer, dl_renderer, "Hard ClipRRect Diff, inset by 15.5"); + SkPath path_clip = SkPath(); + path_clip.setFillType(SkPathFillType::kEvenOdd); + path_clip.addRect(r_clip); + path_clip.addCircle(RenderCenterX, RenderCenterY, 1.0); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, false, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "Hard ClipPath inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, true, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "AA ClipPath inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, false, SkClipOp::kDifference); + }, + cv_renderer, dl_renderer, "Hard ClipPath Diff, inset by 15.5"); + } + + static SkRect getSkBounds(CvRenderer& cv_setup, CvRenderer& cv_render) { + SkPictureRecorder recorder; + SkRTreeFactory rtree_factory; + SkCanvas* cv = recorder.beginRecording(TestBounds, &rtree_factory); + SkPaint p; + cv_setup(cv, p); + cv_render(cv, p); + return recorder.finishRecordingAsPicture()->cullRect(); + } + + static void RenderWith(CvRenderer& cv_setup, + DlRenderer& dl_setup, + CvRenderer& cv_render, + DlRenderer& dl_render, + const std::string info, + const SkColor* bg = nullptr) { + // surface1 is direct rendering via SkCanvas to SkSurface + // DisplayList mechanisms are not involved in this operation + sk_sp ref_surface = makeSurface(bg); + SkPaint paint1; + cv_setup(ref_surface->getCanvas(), paint1); + cv_render(ref_surface->getCanvas(), paint1); + SkRect ref_bounds = getSkBounds(cv_setup, cv_render); + SkPixmap ref_pixels; + ASSERT_TRUE(ref_surface->peekPixels(&ref_pixels)) << info; + ASSERT_EQ(ref_pixels.width(), TestWidth) << info; + ASSERT_EQ(ref_pixels.height(), TestHeight) << info; + ASSERT_EQ(ref_pixels.info().bytesPerPixel(), 4) << info; + checkPixels(&ref_pixels, ref_bounds, info, bg); + + { + // This sequence plays the provided equivalently constructed + // DisplayList onto the SkCanvas of the surface + // DisplayList => direct rendering + sk_sp test_surface = makeSurface(bg); + DisplayListBuilder builder(TestBounds); + dl_setup(builder); + dl_render(builder); + sk_sp display_list = builder.Build(); + SkRect dl_bounds = display_list->bounds(); +#ifdef DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + if (dl_bounds != ref_bounds) { + FML_LOG(ERROR) << "For " << info; + FML_LOG(ERROR) << "ref: " << ref_bounds.fLeft << ", " << ref_bounds.fTop + << " => " << ref_bounds.fRight << ", " + << ref_bounds.fBottom; + FML_LOG(ERROR) << "dl: " << dl_bounds.fLeft << ", " << dl_bounds.fTop + << " => " << dl_bounds.fRight << ", " + << dl_bounds.fBottom; + if (!dl_bounds.contains(ref_bounds)) { + FML_LOG(ERROR) << "DisplayList bounds are too small!"; + } + } +#endif // DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + // This sometimes triggers, but when it triggers and I examine + // the ref_bounds, they are always unnecessarily large and + // since the pixel OOB tests in the compare method do not + // trigger, we will trust the DL bounds. + // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info; + display_list->RenderTo(test_surface->getCanvas()); + compareToReference(test_surface.get(), &ref_pixels, info + " (DL render)", + &dl_bounds, bg); + } + + // This test cannot work if the rendering is using shadows until + // we can access the Skia ShadowRec via public headers. + if (!TestingDrawShadows) { + // This sequence renders SkCanvas calls to a DisplayList and then + // plays them back on SkCanvas to SkSurface + // SkCanvas calls => DisplayList => rendering + sk_sp test_surface = makeSurface(bg); + DisplayListCanvasRecorder dl_recorder(TestBounds); + SkPaint test_paint; + cv_setup(&dl_recorder, test_paint); + cv_render(&dl_recorder, test_paint); + dl_recorder.builder()->Build()->RenderTo(test_surface->getCanvas()); + compareToReference(test_surface.get(), &ref_pixels, + info + " (Sk->DL render)", nullptr, nullptr); + } + + { + // This sequence renders the SkCanvas calls to an SkPictureRecorder and + // renders the DisplayList calls to a DisplayListBuilder and then + // renders both back under a transform (scale(2x)) to see if their + // rendering is affected differently by a change of matrix between + // recording time and rendering time. + const int TestWidth2 = TestWidth * 2; + const int TestHeight2 = TestHeight * 2; + const SkScalar TestScale = 2.0; + + SkPictureRecorder sk_recorder; + SkCanvas* ref_canvas = sk_recorder.beginRecording(TestBounds); + SkPaint ref_paint; + cv_setup(ref_canvas, ref_paint); + cv_render(ref_canvas, ref_paint); + sk_sp ref_picture = sk_recorder.finishRecordingAsPicture(); + sk_sp ref_surface2 = makeSurface(bg, TestWidth2, TestHeight2); + SkCanvas* ref_canvas2 = ref_surface2->getCanvas(); + ref_canvas2->scale(TestScale, TestScale); + ref_picture->playback(ref_canvas2); + SkPixmap ref_pixels2; + ASSERT_TRUE(ref_surface2->peekPixels(&ref_pixels2)) << info; + ASSERT_EQ(ref_pixels2.width(), TestWidth2) << info; + ASSERT_EQ(ref_pixels2.height(), TestHeight2) << info; + ASSERT_EQ(ref_pixels2.info().bytesPerPixel(), 4) << info; + + DisplayListBuilder builder(TestBounds); + dl_setup(builder); + dl_render(builder); + sk_sp display_list = builder.Build(); + sk_sp test_surface = makeSurface(bg, TestWidth2, TestHeight2); + SkCanvas* test_canvas = test_surface->getCanvas(); + test_canvas->scale(TestScale, TestScale); + display_list->RenderTo(test_canvas); + compareToReference(test_surface.get(), &ref_pixels2, + info + " (SKP/DL render scaled 2x)", nullptr, nullptr, + TestWidth2, TestHeight2, false); + } + } + + static void checkPixels(SkPixmap* ref_pixels, + SkRect ref_bounds, + const std::string info, + const SkColor* bg) { + SkPMColor untouched = (bg) ? SkPreMultiplyColor(*bg) : 0; + int pixels_touched = 0; + int pixels_oob = 0; + for (int y = 0; y < TestHeight; y++) { + const uint32_t* ref_row = ref_pixels->addr32(0, y); + for (int x = 0; x < TestWidth; x++) { + if (ref_row[x] != untouched) { + pixels_touched++; + if (!ref_bounds.intersects(SkRect::MakeXYWH(x, y, 1, 1))) { + pixels_oob++; + } + } + } + } + ASSERT_EQ(pixels_oob, 0) << info; + ASSERT_GT(pixels_touched, 0) << info; + } + + static void compareToReference(SkSurface* test_surface, + SkPixmap* reference, + const std::string info, + SkRect* bounds, + const SkColor* bg, + int width = TestWidth, + int height = TestHeight, + bool printMismatches = false) { + SkPMColor untouched = (bg) ? SkPreMultiplyColor(*bg) : 0; + SkPixmap test_pixels; + ASSERT_TRUE(test_surface->peekPixels(&test_pixels)) << info; + ASSERT_EQ(test_pixels.width(), width) << info; + ASSERT_EQ(test_pixels.height(), height) << info; + ASSERT_EQ(test_pixels.info().bytesPerPixel(), 4) << info; + + int pixels_different = 0; + int pixels_oob = 0; + int minX = width; + int minY = height; + int maxX = 0; + int maxY = 0; + for (int y = 0; y < height; y++) { + const uint32_t* ref_row = reference->addr32(0, y); + const uint32_t* test_row = test_pixels.addr32(0, y); + for (int x = 0; x < width; x++) { + if (bounds && test_row[x] != untouched) { + if (minX > x) + minX = x; + if (minY > y) + minY = y; + if (maxX < x) + maxX = x; + if (maxY < y) + maxY = y; + if (!bounds->intersects(SkRect::MakeXYWH(x, y, 1, 1))) { + pixels_oob++; + } + } + if (test_row[x] != ref_row[x]) { + if (printMismatches) { + FML_LOG(ERROR) << "pix[" << x << ", " << y + << "] mismatch: " << std::hex << test_row[x] + << "(test) != (ref)" << ref_row[x] << std::dec; + } + pixels_different++; + } + } + } +#ifdef DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + if (bounds && *bounds != SkRect::MakeLTRB(minX, minY, maxX + 1, maxY + 1)) { + FML_LOG(ERROR) << "inaccurate bounds for " << info; + FML_LOG(ERROR) << "dl: " << bounds->fLeft << ", " << bounds->fTop + << " => " << bounds->fRight << ", " << bounds->fBottom; + FML_LOG(ERROR) << "pixels: " << minX << ", " << minY << " => " + << (maxX + 1) << ", " << (maxY + 1); + } +#endif // DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + ASSERT_EQ(pixels_oob, 0) << info; + ASSERT_EQ(pixels_different, 0) << info; + } + + static sk_sp makeSurface(const SkColor* bg, + int width = TestWidth, + int height = TestHeight) { + sk_sp surface = SkSurface::MakeRasterN32Premul(width, height); + if (bg) { + surface->getCanvas()->drawColor(*bg); + } + return surface; + } + + static const sk_sp testImage; + static const sk_sp makeTestImage() { + sk_sp surface = + SkSurface::MakeRasterN32Premul(RenderWidth, RenderHeight); + SkCanvas* canvas = surface->getCanvas(); + SkPaint p0, p1; + p0.setStyle(SkPaint::kFill_Style); + p0.setColor(SK_ColorGREEN); + p1.setStyle(SkPaint::kFill_Style); + p1.setColor(SK_ColorBLUE); + // Some pixels need some transparency for DstIn testing + p1.setAlpha(128); + int cbdim = 5; + for (int y = 0; y < RenderHeight; y += cbdim) { + for (int x = 0; x < RenderWidth; x += cbdim) { + SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1; + canvas->drawRect(SkRect::MakeXYWH(x, y, cbdim, cbdim), cellp); + } + } + return surface->makeImageSnapshot(); + } + + static sk_sp MakeTextBlob(std::string string, + SkScalar height = RenderHeight) { + SkFont font(SkTypeface::MakeDefault(), height); + return SkTextBlob::MakeFromText(string.c_str(), string.size(), font, + SkTextEncoding::kUTF8); + } +}; + +bool CanvasCompareTester::TestingDrawShadows = false; +bool CanvasCompareTester::TestingDrawVertices = false; +bool CanvasCompareTester::TestingDrawAtlas = false; + +const sk_sp CanvasCompareTester::testImage = + CanvasCompareTester::makeTestImage(); + +TEST(DisplayListCanvas, DrawPaint) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPaint(paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPaint(); + }); +} + +TEST(DisplayListCanvas, DrawColor) { + CanvasCompareTester::RenderNoAttributes( // + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawColor(SK_ColorMAGENTA); + }, + [=](DisplayListBuilder& builder) { // + builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + }); +} + +TEST(DisplayListCanvas, DrawLine) { + SkRect rect = RenderBounds; + SkPoint p1 = SkPoint::Make(rect.fLeft, rect.fTop); + SkPoint p2 = SkPoint::Make(rect.fRight, rect.fBottom); + + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawLine(p1, p2, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawLine(p1, p2); + }); +} + +TEST(DisplayListCanvas, DrawRect) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawRect(RenderBounds, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRect(RenderBounds); + }); +} + +TEST(DisplayListCanvas, DrawOval) { + SkRect rect = RenderBounds.makeInset(0, 10); + + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawOval(rect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawOval(rect); + }); +} + +TEST(DisplayListCanvas, DrawCircle) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawCircle(TestCenter, RenderRadius, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawCircle(TestCenter, RenderRadius); + }); +} + +TEST(DisplayListCanvas, DrawRRect) { + SkRRect rrect = + SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawRRect(rrect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRRect(rrect); + }); +} + +TEST(DisplayListCanvas, DrawDRRect) { + SkRRect outer = + SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); + SkRect innerBounds = RenderBounds.makeInset(30.0, 30.0); + SkRRect inner = + SkRRect::MakeRectXY(innerBounds, RenderCornerRadius, RenderCornerRadius); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawDRRect(outer, inner, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDRRect(outer, inner); + }); +} + +TEST(DisplayListCanvas, DrawPath) { + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPath(path, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPath(path); + }); +} + +TEST(DisplayListCanvas, DrawArc) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawArc(RenderBounds, 30, 270, false, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 30, 270, false); + }); +} + +TEST(DisplayListCanvas, DrawArcCenter) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawArc(RenderBounds, 30, 270, true, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 30, 270, true); + }); +} + +TEST(DisplayListCanvas, DrawPointsAsPoints) { + const SkScalar x0 = RenderLeft; + const SkScalar x1 = (RenderLeft + RenderCenterX) * 0.5; + const SkScalar x2 = RenderCenterX; + const SkScalar x3 = (RenderRight + RenderCenterX) * 0.5; + const SkScalar x4 = RenderRight; + + const SkScalar y0 = RenderTop; + const SkScalar y1 = (RenderTop + RenderCenterY) * 0.5; + const SkScalar y2 = RenderCenterY; + const SkScalar y3 = (RenderBottom + RenderCenterY) * 0.5; + const SkScalar y4 = RenderBottom; + + // clang-format off + const SkPoint points[] = { + {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, + {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, + {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, + {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, + {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, + }; + // clang-format on + + const int count = sizeof(points) / sizeof(points[0]); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kPoints_PointMode, count, points); + }); +} + +TEST(DisplayListCanvas, DrawPointsAsLines) { + const SkScalar x0 = RenderLeft; + const SkScalar x1 = (RenderLeft + RenderCenterX) * 0.5; + const SkScalar x2 = RenderCenterX; + const SkScalar x3 = (RenderRight + RenderCenterX) * 0.5; + const SkScalar x4 = RenderRight; + + const SkScalar y0 = RenderTop; + const SkScalar y1 = (RenderTop + RenderCenterY) * 0.5; + const SkScalar y2 = RenderCenterY; + const SkScalar y3 = (RenderBottom + RenderCenterY) * 0.5; + const SkScalar y4 = RenderBottom; + + // clang-format off + const SkPoint points[] = { + // Diagonals + {x0, y0}, {x4, y4}, {x4, y0}, {x0, y4}, + // Inner box + {x1, y1}, {x3, y1}, + {x3, y1}, {x3, y3}, + {x3, y3}, {x1, y3}, + {x1, y3}, {x1, y1}, + // Middle crosshair + {x2, y1}, {x2, y3}, + {x1, y2}, {x3, y3}, + }; + // clang-format on + + const int count = sizeof(points) / sizeof(points[0]); + ASSERT_TRUE((count & 1) == 0); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kLines_PointMode, count, points); + }); +} + +TEST(DisplayListCanvas, DrawPointsAsPolygon) { + const SkPoint points[] = { + SkPoint::Make(RenderLeft, RenderTop), + SkPoint::Make(RenderRight, RenderBottom), + SkPoint::Make(RenderRight, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderLeft, RenderTop), + }; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + SkPaint p = paint; + p.setStyle(SkPaint::kStroke_Style); + canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, points, p); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kPolygon_PointMode, 4, points); + }); +} + +TEST(DisplayListCanvas, DrawVerticesWithColors) { + const SkPoint pts[3] = { + SkPoint::Make(RenderCenterX, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderRight, RenderBottom), + }; + const SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN}; + const sk_sp vertices = SkVertices::MakeCopy( + SkVertices::kTriangles_VertexMode, 3, pts, nullptr, colors); + CanvasCompareTester::RenderVertices( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawVertices(vertices, SkBlendMode::kSrcOver); + }); + ASSERT_TRUE(vertices->unique()); +} + +TEST(DisplayListCanvas, DrawVerticesWithImage) { + const SkPoint pts[3] = { + SkPoint::Make(RenderCenterX, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderRight, RenderBottom), + }; + const SkPoint tex[3] = { + SkPoint::Make(RenderWidth / 2.0, 0), + SkPoint::Make(0, RenderHeight), + SkPoint::Make(RenderWidth, RenderHeight), + }; + const sk_sp vertices = SkVertices::MakeCopy( + SkVertices::kTriangles_VertexMode, 3, pts, tex, nullptr); + const sk_sp shader = CanvasCompareTester::testImage->makeShader( + SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions()); + CanvasCompareTester::RenderVertices( + [=](SkCanvas* canvas, SkPaint& paint) { // + paint.setShader(shader); + canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.setShader(shader); + builder.drawVertices(vertices, SkBlendMode::kSrcOver); + }); + ASSERT_TRUE(vertices->unique()); + ASSERT_TRUE(shader->unique()); +} + +TEST(DisplayListCanvas, DrawImageNearest) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::NearestSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::NearestSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageLinear) { + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::LinearSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::LinearSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageRectNearest) { + SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageRectLinear) { + SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageNineNearest) { + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kNearest); + }); +} + +TEST(DisplayListCanvas, DrawImageNineLinear) { + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kLinear); + }); +} + +TEST(DisplayListCanvas, DrawImageLatticeNearest) { + const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const int divX[] = { + (RenderLeft + RenderCenterX) / 2, + RenderCenterX, + (RenderRight + RenderCenterX) / 2, + }; + const int divY[] = { + (RenderTop + RenderCenterY) / 2, + RenderCenterY, + (RenderBottom + RenderCenterY) / 2, + }; + SkCanvas::Lattice lattice = { + divX, divY, nullptr, 3, 3, nullptr, nullptr, + }; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kNearest, true); + }); +} + +TEST(DisplayListCanvas, DrawImageLatticeLinear) { + const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const int divX[] = { + (RenderLeft + RenderCenterX) / 2, + RenderCenterX, + (RenderRight + RenderCenterX) / 2, + }; + const int divY[] = { + (RenderTop + RenderCenterY) / 2, + RenderCenterY, + (RenderBottom + RenderCenterY) / 2, + }; + SkCanvas::Lattice lattice = { + divX, divY, nullptr, 3, 3, nullptr, nullptr, + }; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kLinear, true); + }); +} + +TEST(DisplayListCanvas, DrawAtlasNearest) { + const SkRSXform xform[] = { + {0.5, 0, RenderLeft, RenderRight}, + {0, 0.5, RenderCenterX, RenderCenterY}, + }; + const SkRect tex[] = { + {0, 0, RenderWidth * 0.5, RenderHeight * 0.5}, + {RenderWidth * 0.5, RenderHeight * 0.5, RenderWidth, RenderHeight}, + }; + const SkColor colors[] = { + SK_ColorBLUE, + SK_ColorGREEN, + }; + const sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAtlas( + [=](SkCanvas* canvas, SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 2, + SkBlendMode::kSrcOver, DisplayList::NearestSampling, + nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 2, // + SkBlendMode::kSrcOver, DisplayList::NearestSampling, + nullptr); + }); +} + +TEST(DisplayListCanvas, DrawAtlasLinear) { + const SkRSXform xform[] = { + {0.5, 0, RenderLeft, RenderRight}, + {0, 0.5, RenderCenterX, RenderCenterY}, + }; + const SkRect tex[] = { + {0, 0, RenderWidth * 0.5, RenderHeight * 0.5}, + {RenderWidth * 0.5, RenderHeight * 0.5, RenderWidth, RenderHeight}, + }; + const SkColor colors[] = { + SK_ColorBLUE, + SK_ColorGREEN, + }; + const sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAtlas( + [=](SkCanvas* canvas, SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 2, // + SkBlendMode::kSrcOver, DisplayList::LinearSampling, + nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 2, // + SkBlendMode::kSrcOver, DisplayList::LinearSampling, + nullptr); + }); +} + +TEST(DisplayListCanvas, DrawPicture) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(RenderBounds); + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorBLUE); + cv->drawOval(RenderBounds, p); + sk_sp picture = recorder.finishRecordingAsPicture(); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, false); + }); +} + +TEST(DisplayListCanvas, DrawPictureWithMatrix) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(RenderBounds); + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorBLUE); + cv->drawOval(RenderBounds, p); + sk_sp picture = recorder.finishRecordingAsPicture(); + SkMatrix matrix = SkMatrix::Scale(0.95, 0.95); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, &matrix, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, &matrix, false); + }); +} + +TEST(DisplayListCanvas, DrawPictureWithPaint) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(RenderBounds); + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorBLUE); + cv->drawOval(RenderBounds, p); + sk_sp picture = recorder.finishRecordingAsPicture(); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, true); + }); +} + +TEST(DisplayListCanvas, DrawDisplayList) { + DisplayListBuilder builder; + builder.setDrawStyle(SkPaint::kFill_Style); + builder.setColor(SK_ColorBLUE); + builder.drawOval(RenderBounds); + sk_sp display_list = builder.Build(); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + display_list->RenderTo(canvas); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDisplayList(display_list); + }); +} + +TEST(DisplayListCanvas, DrawTextBlob) { + // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the + // performance overlay can use Fuchsia's font manager instead of the empty + // default. +#if defined(OS_FUCHSIA) + GTEST_SKIP() << "Rendering comparisons require a valid default font manager"; +#endif // OS_FUCHSIA + sk_sp blob = CanvasCompareTester::MakeTextBlob("Test Blob"); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawTextBlob(blob, RenderLeft, RenderBottom, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawTextBlob(blob, RenderLeft, RenderBottom); + }); +} + +TEST(DisplayListCanvas, DrawShadow) { + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + path.close(); + const SkColor color = SK_ColorDKGRAY; + const SkScalar elevation = 10; + + CanvasCompareTester::RenderShadows( + [=](SkCanvas* canvas, SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, + 1.0); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, false, 1.0); + }); +} + +TEST(DisplayListCanvas, DrawOccludingShadow) { + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + path.close(); + const SkColor color = SK_ColorDKGRAY; + const SkScalar elevation = 10; + + CanvasCompareTester::RenderShadows( + [=](SkCanvas* canvas, SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, true, + 1.0); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, true, 1.0); + }); +} + +TEST(DisplayListCanvas, DrawShadowDpr) { + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + path.close(); + const SkColor color = SK_ColorDKGRAY; + const SkScalar elevation = 10; + + CanvasCompareTester::RenderShadows( + [=](SkCanvas* canvas, SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, + 2.5); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, false, 2.5); + }); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc new file mode 100644 index 0000000000000..17447b7bd29e5 --- /dev/null +++ b/flow/display_list_unittests.cc @@ -0,0 +1,992 @@ +// 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/flow/display_list_canvas.h" + +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" +#include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkBlenders.h" +#include "third_party/skia/include/effects/SkDashPathEffect.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "third_party/skia/include/effects/SkImageFilters.h" + +#include + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +constexpr SkPoint end_points[] = { + {0, 0}, + {100, 100}, +}; +constexpr SkColor colors[] = { + SK_ColorGREEN, + SK_ColorYELLOW, + SK_ColorBLUE, +}; +constexpr float stops[] = { + 0.0, + 0.5, + 1.0, +}; + +// clang-format off +constexpr float rotate_color_matrix[20] = { + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, +}; +constexpr float invert_color_matrix[20] = { + -1.0, 0, 0, 1.0, 0, + 0, -1.0, 0, 1.0, 0, + 0, 0, -1.0, 1.0, 0, + 1.0, 1.0, 1.0, 1.0, 0, +}; +// clang-format on + +const SkScalar TestDashes1[] = {4.0, 2.0}; +const SkScalar TestDashes2[] = {1.0, 1.5}; + +constexpr SkPoint TestPoints[] = { + {10, 10}, + {20, 20}, + {10, 20}, + {20, 10}, +}; +#define TestPointCount sizeof(TestPoints) / (sizeof(TestPoints[0])) + +static const sk_sp TestBlender1 = + SkBlenders::Arithmetic(0.2, 0.2, 0.2, 0.2, false); +static const sk_sp TestBlender2 = + SkBlenders::Arithmetic(0.2, 0.2, 0.2, 0.2, true); +static const sk_sp TestBlender3 = + SkBlenders::Arithmetic(0.3, 0.3, 0.3, 0.3, true); +static const sk_sp TestShader1 = + SkGradientShader::MakeLinear(end_points, + colors, + stops, + 3, + SkTileMode::kMirror, + 0, + nullptr); +// TestShader2 is identical to TestShader1 and points out that we cannot +// perform a deep compare over our various sk_sp objects because the +// DisplayLists constructed with the two do not compare == below. +static const sk_sp TestShader2 = + SkGradientShader::MakeLinear(end_points, + colors, + stops, + 3, + SkTileMode::kMirror, + 0, + nullptr); +static const sk_sp TestShader3 = + SkGradientShader::MakeLinear(end_points, + colors, + stops, + 3, + SkTileMode::kDecal, + 0, + nullptr); +static const sk_sp TestImageFilter1 = + SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); +static const sk_sp TestImageFilter2 = + SkImageFilters::Blur(5.0, 5.0, SkTileMode::kClamp, nullptr, nullptr); +static const sk_sp TestColorFilter1 = + SkColorFilters::Matrix(rotate_color_matrix); +static const sk_sp TestColorFilter2 = + SkColorFilters::Matrix(invert_color_matrix); +static const sk_sp TestPathEffect1 = + SkDashPathEffect::Make(TestDashes1, 2, 0.0f); +static const sk_sp TestPathEffect2 = + SkDashPathEffect::Make(TestDashes2, 2, 0.0f); +static const sk_sp TestMaskFilter = + SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); +constexpr SkRect TestBounds = SkRect::MakeLTRB(10, 10, 50, 60); +static const SkRRect TestRRect = SkRRect::MakeRectXY(TestBounds, 5, 5); +static const SkRRect TestRRectRect = SkRRect::MakeRect(TestBounds); +static const SkRRect TestInnerRRect = + SkRRect::MakeRectXY(TestBounds.makeInset(5, 5), 2, 2); +static const SkPath TestPathRect = SkPath::Rect(TestBounds); +static const SkPath TestPathOval = SkPath::Oval(TestBounds); +static const SkPath TestPath1 = + SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, true); +static const SkPath TestPath2 = + SkPath::Polygon({{0, 0}, {10, 10}, {0, 10}, {10, 0}}, true); +static const SkPath TestPath3 = + SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, false); +static const SkMatrix TestMatrix1 = SkMatrix::Scale(2, 2); +static const SkMatrix TestMatrix2 = SkMatrix::RotateDeg(45); + +static sk_sp MakeTestImage(int w, int h, int checker_size) { + sk_sp surface = SkSurface::MakeRasterN32Premul(w, h); + SkCanvas* canvas = surface->getCanvas(); + SkPaint p0, p1; + p0.setStyle(SkPaint::kFill_Style); + p0.setColor(SK_ColorGREEN); + p1.setStyle(SkPaint::kFill_Style); + p1.setColor(SK_ColorBLUE); + p1.setAlpha(128); + for (int y = 0; y < w; y += checker_size) { + for (int x = 0; x < h; x += checker_size) { + SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1; + canvas->drawRect(SkRect::MakeXYWH(x, y, checker_size, checker_size), + cellp); + } + } + return surface->makeImageSnapshot(); +} +static sk_sp TestImage1 = MakeTestImage(40, 40, 5); +static sk_sp TestImage2 = MakeTestImage(50, 50, 5); + +static sk_sp TestVertices1 = + SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, + 3, + TestPoints, + nullptr, + colors); +static sk_sp TestVertices2 = + SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, + 3, + TestPoints, + nullptr, + colors); + +static constexpr int TestDivs1[] = {10, 20, 30}; +static constexpr int TestDivs2[] = {15, 20, 25}; +static constexpr int TestDivs3[] = {15, 25}; +static constexpr SkCanvas::Lattice::RectType TestRTypes[] = { + SkCanvas::Lattice::RectType::kDefault, + SkCanvas::Lattice::RectType::kTransparent, + SkCanvas::Lattice::RectType::kFixedColor, + SkCanvas::Lattice::RectType::kDefault, + SkCanvas::Lattice::RectType::kTransparent, + SkCanvas::Lattice::RectType::kFixedColor, + SkCanvas::Lattice::RectType::kDefault, + SkCanvas::Lattice::RectType::kTransparent, + SkCanvas::Lattice::RectType::kFixedColor, +}; +static constexpr SkColor TestLatticeColors[] = { + SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW, + SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW, + SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW, +}; +static constexpr SkIRect TestLatticeSrcRect = {1, 1, 39, 39}; + +static sk_sp MakeTestPicture(int w, int h, SkColor color) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(TestBounds); + SkPaint paint; + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + cv->drawRect(SkRect::MakeWH(w, h), paint); + return recorder.finishRecordingAsPicture(); +} +static sk_sp TestPicture1 = MakeTestPicture(20, 20, SK_ColorGREEN); +static sk_sp TestPicture2 = MakeTestPicture(25, 25, SK_ColorBLUE); + +static sk_sp MakeTestDisplayList(int w, int h, SkColor color) { + DisplayListBuilder builder; + builder.setColor(color); + builder.drawRect(SkRect::MakeWH(w, h)); + return builder.Build(); +} +static sk_sp TestDisplayList1 = + MakeTestDisplayList(20, 20, SK_ColorGREEN); +static sk_sp TestDisplayList2 = + MakeTestDisplayList(25, 25, SK_ColorBLUE); + +static sk_sp MakeTextBlob(std::string string) { + return SkTextBlob::MakeFromText(string.c_str(), string.size(), SkFont(), + SkTextEncoding::kUTF8); +} +static sk_sp TestBlob1 = MakeTextBlob("TestBlob1"); +static sk_sp TestBlob2 = MakeTextBlob("TestBlob2"); + +// --------------- +// Test Suite data +// --------------- + +typedef const std::function DlInvoker; + +struct DisplayListInvocation { + int op_count; + size_t byte_count; + + // in some cases, running the sequence through an SkCanvas will result + // in fewer ops/bytes. Attribute invocations are recorded in an SkPaint + // and not forwarded on, and SkCanvas culls unused save/restore/transforms. + int sk_op_count; + size_t sk_byte_count; + + DlInvoker invoker; + + bool sk_version_matches() { + return (op_count == sk_op_count && byte_count == sk_byte_count); + } + + sk_sp Build() { + DisplayListBuilder builder; + invoker(builder); + return builder.Build(); + } +}; + +struct DisplayListInvocationGroup { + std::string op_name; + std::vector variants; +}; + +std::vector allGroups = { + { "SetAA", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setAA(false);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setAA(true);}}, + } + }, + { "SetDither", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(false);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(true);}}, + } + }, + { "SetInvertColors", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(false);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(true);}}, + } + }, + { "SetStrokeCap", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kButt_Cap);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kRound_Cap);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kSquare_Cap);}}, + } + }, + { "SetStrokeJoin", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kBevel_Join);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kRound_Join);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kMiter_Join);}}, + } + }, + { "SetDrawStyle", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kFill_Style);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kStroke_Style);}}, + } + }, + { "SetStrokeWidth", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(5.0);}}, + } + }, + { "SetMiterLimit", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMiterLimit(0.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMiterLimit(5.0);}}, + } + }, + { "SetColor", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorGREEN);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLUE);}}, + } + }, + { "SetBlendMode", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcIn);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}}, + } + }, + { "SetBlender", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlender(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender2);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setBlender(TestBlender3);}}, + } + }, + { "SetShader", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader2);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader3);}}, + } + }, + { "SetImageFilter", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter2);}}, + } + }, + { "SetColorFilter", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter2);}}, + } + }, + { "SetPathEffect", { + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setPathEffect(TestPathEffect2);}}, + } + }, + { "SetMaskFilter", { + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(TestMaskFilter);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 3.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kSolid_SkBlurStyle, 3.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kInner_SkBlurStyle, 3.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kOuter_SkBlurStyle, 3.0);}}, + } + }, + { "Save(Layer)+Restore", { + // cv.save/restore are ignored if there are no draw calls between them + {2, 16, 0, 0, [](DisplayListBuilder& b) {b.save(); b.restore();}}, + {2, 16, 2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, false); b.restore(); }}, + {2, 16, 2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, true); b.restore(); }}, + {2, 32, 2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, false); b.restore(); }}, + {2, 32, 2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, true); b.restore(); }}, + } + }, + { "Translate", { + // cv.translate(0, 0) is ignored + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.translate(0, 0);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 10);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 15);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(15, 10);}}, + } + }, + { "Scale", { + // cv.scale(1, 1) is ignored + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.scale(1, 1);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 2);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 3);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(3, 2);}}, + } + }, + { "Rotate", { + // cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix) + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.rotate(0);}}, + {1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(30);}}, + {1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(45);}}, + } + }, + { "Skew", { + // cv.skew(0, 0) is ignored, otherwise expressed as concat(skewmatrix) + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.skew(0, 0);}}, + {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.1);}}, + {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.2);}}, + {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.2, 0.1);}}, + } + }, + { "Transform2x3", { + // cv.transform(identity) is ignored + {1, 32, 0, 0, [](DisplayListBuilder& b) {b.transform2x3(1, 0, 0, 0, 1, 0);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.transform2x3(0, 1, 12, 1, 0, 33);}}, + } + }, + { "Transform3x3", { + // cv.transform(identity) is ignored + {1, 40, 0, 0, [](DisplayListBuilder& b) {b.transform3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.transform3x3(0, 1, 12, 1, 0, 33, 0, 0, 12);}}, + } + }, + { "ClipRect", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds.makeOffset(1, 1), + true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kDifference);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kDifference);}}, + } + }, + { "ClipRRect", { + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kIntersect);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect.makeOffset(1, 1), + true, SkClipOp::kIntersect);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kIntersect);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kDifference);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kDifference);}}, + } + }, + { "ClipPath", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath2, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath3, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kDifference);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kDifference);}}, + // clipPath(rect) becomes clipRect + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPathRect, true, SkClipOp::kIntersect);}}, + // clipPath(oval) becomes clipRRect + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipPath(TestPathOval, true, SkClipOp::kIntersect);}}, + } + }, + { "DrawPaint", { + {1, 8, 1, 8, [](DisplayListBuilder& b) {b.drawPaint();}}, + } + }, + { "DrawColor", { + // cv.drawColor becomes cv.drawPaint(paint) + {1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kSrcIn);}}, + {1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kDstIn);}}, + {1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorCYAN, SkBlendMode::kSrcIn);}}, + } + }, + { "DrawLine", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 1}, {10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {20, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 20});}}, + } + }, + { "DrawRect", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 1, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 20, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 20});}}, + } + }, + { "DrawOval", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 1, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 20, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 20});}}, + } + }, + { "DrawCircle", { + // cv.drawCircle becomes cv.drawOval + {1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 10);}}, + {1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 5}, 10);}}, + {1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 20);}}, + } + }, + { "DrawRRect", { + {1, 56, 1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect);}}, + {1, 56, 1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect.makeOffset(5, 5));}}, + } + }, + { "DrawDRRect", { + {1, 112, 1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect, TestInnerRRect);}}, + {1, 112, 1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect.makeOffset(5, 5), + TestInnerRRect.makeOffset(4, 4));}}, + } + }, + { "DrawPath", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath1);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath2);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath3);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPathRect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPathOval);}}, + } + }, + { "DrawArc", { + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds.makeOffset(1, 1), + 45, 270, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 30, 270, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 260, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, true);}}, + } + }, + { "DrawPoints", { + {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode, + TestPointCount, + TestPoints);}}, + {1, 8 + (TestPointCount - 1) * 8, 1, 8 + (TestPointCount - 1) * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode, + TestPointCount - 1, + TestPoints);}}, + {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kLines_PointMode, + TestPointCount, + TestPoints);}}, + {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPolygon_PointMode, + TestPointCount, + TestPoints);}}, + } + }, + { "DrawVertices", { + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kSrcIn);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kDstIn);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices2, SkBlendMode::kSrcIn);}}, + } + }, + { "DrawImage", { + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {20, 10}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 20}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::LinearSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage2, {10, 10}, DisplayList::NearestSampling);}}, + } + }, + { "DrawImageRect", { + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + DisplayList::NearestSampling, + SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80}, + DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80}, + DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + DisplayList::LinearSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80}, + DisplayList::NearestSampling);}}, + } + }, + { "DrawImageNine", { + // SkVanvas::drawImageNine is immediately converted to drawImageLattice + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + SkFilterMode::kNearest);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80}, + SkFilterMode::kNearest);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80}, + SkFilterMode::kNearest);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + SkFilterMode::kLinear);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80}, + SkFilterMode::kNearest);}}, + } + }, + { "DrawImageLattice", { + // Lattice: + // const int* fXDivs; //!< x-axis values dividing bitmap + // const int* fYDivs; //!< y-axis values dividing bitmap + // const RectType* fRectTypes; //!< array of fill types + // int fXCount; //!< number of x-coordinates + // int fYCount; //!< number of y-coordinates + // const SkIRect* fBounds; //!< source bounds to draw from + // const SkColor* fColors; //!< array of colors + // size = 64 + fXCount * 4 + fYCount * 4 + // if fColors and fRectTypes are not null, add (fXCount + 1) * (fYCount + 1) * 5 + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 45}, SkFilterMode::kNearest, false);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs2, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + // One less yDiv does not change the allocation due to 8-byte alignment + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 2, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kLinear, false);}}, + {2, 96, 2, 96, [](DisplayListBuilder& b) {b.setColor(SK_ColorMAGENTA); + b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, true);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage2, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + // Supplying fBounds does not change size because the Op record always includes it + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + {1, 128, 1, 128, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs3, TestDivs3, TestRTypes, 2, 2, nullptr, TestLatticeColors}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + } + }, + { "DrawAtlas", { + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {0, 1, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 25, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::LinearSampling, nullptr);}}, + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kDstIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 56 + 32 + 32, 1, 56 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + static SkRect cullRect = { 0, 0, 200, 200 }; + b.drawAtlas(TestImage2, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, &cullRect);}}, + {1, 40 + 32 + 32 + 8, 1, 40 + 32 + 32 + 8, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN }; + b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 56 + 32 + 32 + 8, 1, 56 + 32 + 32 + 8, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN }; + static SkRect cullRect = { 0, 0, 200, 200 }; + b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, &cullRect);}}, + } + }, + { "DrawPicture", { + // cv.drawPicture cannot be compared as SkCanvas may inline it + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, false);}}, + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2, nullptr, false);}}, + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, true);}}, + {1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, false);}}, + {1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix2, false);}}, + {1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, true);}}, + } + }, + { "DrawDisplayList", { + // cv.drawDL does not exist + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList1);}}, + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList2);}}, + } + }, + { "DrawTextBlob", { + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 10);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 20, 10);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 20);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob2, 10, 10);}}, + } + }, + // The -1 op counts below are to indicate to the framework not to test + // SkCanvas conversion of these ops as it converts the operation into a + // format that is not exposed publicly and so we cannot recapture the + // operation. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + { "DrawShadow", { + // cv shadows are turned into an opaque ShadowRec which is not exposed + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false, 1.0);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath2, SK_ColorGREEN, 1.0, false, 1.0);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorBLUE, 1.0, false, 1.0);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 2.0, false, 1.0);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, true, 1.0);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false, 2.5);}}, + } + }, +}; + +TEST(DisplayList, SingleOpSizes) { + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { + auto& invocation = group.variants[i]; + sk_sp dl = invocation.Build(); + auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")"; + ASSERT_EQ(dl->op_count(), invocation.op_count) << desc; + EXPECT_EQ(dl->bytes(), invocation.byte_count) << desc; + } + } +} + +TEST(DisplayList, SingleOpDisplayListsNotEqualEmpty) { + sk_sp empty = DisplayListBuilder().Build(); + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { + sk_sp dl = group.variants[i].Build(); + auto desc = + group.op_name + "(variant " + std::to_string(i + 1) + " != empty)"; + ASSERT_FALSE(dl->Equals(*empty)) << desc; + ASSERT_FALSE(empty->Equals(*dl)) << desc; + } + } +} + +TEST(DisplayList, SingleOpDisplayListsRecapturedAreEqual) { + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { + sk_sp dl = group.variants[i].Build(); + // Verify recapturing the replay of the display list is Equals() + // when dispatching directly from the DL to another builder + DisplayListBuilder builder; + dl->Dispatch(builder); + sk_sp copy = builder.Build(); + auto desc = + group.op_name + "(variant " + std::to_string(i + 1) + " == copy)"; + ASSERT_EQ(copy->op_count(), dl->op_count()) << desc; + ASSERT_EQ(copy->bytes(), dl->bytes()) << desc; + ASSERT_EQ(copy->bounds(), dl->bounds()) << desc; + ASSERT_TRUE(copy->Equals(*dl)) << desc; + ASSERT_TRUE(dl->Equals(*copy)) << desc; + } + } +} + +TEST(DisplayList, SingleOpDisplayListsRecapturedViaSkCanvasAreEqual) { + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { + if (group.variants[i].sk_op_count < 0) { + // A negative sk_op_count means "do not test this op". + // Used mainly for these cases: + // - we cannot encode a DrawShadowRec (Skia private header) + // - SkCanvas cannot receive a DisplayList + // - SkCanvas may or may not inline an SkPicture + continue; + } + // Verify a DisplayList (re)built by "rendering" it to an + // [SkCanvas->DisplayList] recorder recaptures an equivalent + // sequence. + // Note that sometimes the rendering ops can be optimized out by + // SkCanvas so the transfer is not always 1:1. We control for + // this by having separate op counts and sizes for the sk results + // and changing our expectation of Equals() results accordingly. + sk_sp dl = group.variants[i].Build(); + + DisplayListCanvasRecorder recorder(dl->bounds()); + dl->RenderTo(&recorder); + sk_sp sk_copy = recorder.Build(); + auto desc = group.op_name + "[variant " + std::to_string(i + 1) + "]"; + EXPECT_EQ(sk_copy->op_count(), group.variants[i].sk_op_count) << desc; + EXPECT_EQ(sk_copy->bytes(), group.variants[i].sk_byte_count) << desc; + if (group.variants[i].sk_version_matches()) { + EXPECT_EQ(sk_copy->bounds(), dl->bounds()) << desc; + EXPECT_TRUE(dl->Equals(*sk_copy)) << desc << " == sk_copy"; + EXPECT_TRUE(sk_copy->Equals(*dl)) << "sk_copy == " << desc; + } else { + // No assertion on bounds + // they could be equal, hard to tell + EXPECT_FALSE(dl->Equals(*sk_copy)) << desc << " != sk_copy"; + EXPECT_FALSE(sk_copy->Equals(*dl)) << "sk_copy != " << desc; + } + } + } +} + +TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) { + for (auto& group : allGroups) { + std::vector> listsA; + std::vector> listsB; + for (size_t i = 0; i < group.variants.size(); i++) { + listsA.push_back(group.variants[i].Build()); + listsB.push_back(group.variants[i].Build()); + } + + for (size_t i = 0; i < listsA.size(); i++) { + sk_sp listA = listsA[i]; + for (size_t j = 0; j < listsB.size(); j++) { + sk_sp listB = listsB[j]; + auto desc = group.op_name + "(variant " + std::to_string(i + 1) + + " ==? variant " + std::to_string(j + 1) + ")"; + if (i == j) { + ASSERT_EQ(listA->op_count(), listB->op_count()) << desc; + ASSERT_EQ(listA->bytes(), listB->bytes()) << desc; + ASSERT_EQ(listA->bounds(), listB->bounds()) << desc; + ASSERT_TRUE(listA->Equals(*listB)) << desc; + ASSERT_TRUE(listB->Equals(*listA)) << desc; + } else { + // No assertion on op/byte counts or bounds + // they may or may not be equal between variants + ASSERT_FALSE(listA->Equals(*listB)) << desc; + ASSERT_FALSE(listB->Equals(*listA)) << desc; + } + } + } + } +} + +static sk_sp Build(size_t g_index, size_t v_index) { + DisplayListBuilder builder; + int op_count = 0; + size_t byte_count = 0; + for (size_t i = 0; i < allGroups.size(); i++) { + DisplayListInvocationGroup& group = allGroups[i]; + size_t j = (i == g_index ? v_index : 0); + if (j >= group.variants.size()) + continue; + DisplayListInvocation& invocation = group.variants[j]; + op_count += invocation.op_count; + byte_count += invocation.byte_count; + invocation.invoker(builder); + } + sk_sp dl = builder.Build(); + std::string name; + if (g_index >= allGroups.size()) { + name = "Default"; + } else { + name = allGroups[g_index].op_name; + if (v_index < 0) { + name += " skipped"; + } else { + name += " variant " + std::to_string(v_index + 1); + } + } + EXPECT_EQ(dl->op_count(), op_count) << name; + EXPECT_EQ(dl->bytes(), byte_count) << name; + return dl; +} + +TEST(DisplayList, DisplayListsWithVaryingOpComparisons) { + sk_sp default_dl = Build(allGroups.size(), 0); + ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself"; + for (size_t gi = 0; gi < allGroups.size(); gi++) { + DisplayListInvocationGroup& group = allGroups[gi]; + sk_sp missing_dl = Build(gi, group.variants.size()); + auto desc = "[Group " + std::to_string(gi + 1) + " omitted]"; + ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself"; + ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default"; + ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc; + for (size_t vi = 0; vi < group.variants.size(); vi++) { + auto desc = "[Group " + std::to_string(gi + 1) + " variant " + + std::to_string(vi + 1) + "]"; + sk_sp variant_dl = Build(gi, vi); + ASSERT_TRUE(variant_dl->Equals(*variant_dl)) << desc << " == itself"; + if (vi == 0) { + ASSERT_TRUE(variant_dl->Equals(*default_dl)) << desc << " == Default"; + ASSERT_TRUE(default_dl->Equals(*variant_dl)) << "Default == " << desc; + } else { + ASSERT_FALSE(variant_dl->Equals(*default_dl)) << desc << " != Default"; + ASSERT_FALSE(default_dl->Equals(*variant_dl)) << "Default != " << desc; + } + ASSERT_FALSE(variant_dl->Equals(*missing_dl)) << desc << " != omitted"; + ASSERT_FALSE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc; + } + } +} + +TEST(DisplayList, DisplayListSaveLayerBoundsWithAlphaFilter) { + SkRect build_bounds = SkRect::MakeLTRB(-100, -100, 200, 200); + SkRect save_bounds = SkRect::MakeWH(100, 100); + SkRect rect = SkRect::MakeLTRB(30, 30, 70, 70); + // clang-format off + const float color_matrix[] = { + 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, + }; + // clang-format on + sk_sp base_color_filter = SkColorFilters::Matrix(color_matrix); + // clang-format off + const float alpha_matrix[] = { + 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, + }; + // clang-format on + sk_sp alpha_color_filter = + SkColorFilters::Matrix(alpha_matrix); + + { + DisplayListBuilder builder(build_bounds); + builder.saveLayer(&save_bounds, true); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), rect); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setColorFilter(base_color_filter); + builder.saveLayer(&save_bounds, true); + builder.setColorFilter(nullptr); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), rect); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setColorFilter(alpha_color_filter); + builder.saveLayer(&save_bounds, true); + builder.setColorFilter(nullptr); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), save_bounds); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setColorFilter(alpha_color_filter); + builder.saveLayer(nullptr, true); + builder.setColorFilter(nullptr); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), build_bounds); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setImageFilter( + SkImageFilters::ColorFilter(base_color_filter, nullptr)); + builder.saveLayer(&save_bounds, true); + builder.setImageFilter(nullptr); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), rect); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setImageFilter( + SkImageFilters::ColorFilter(alpha_color_filter, nullptr)); + builder.saveLayer(&save_bounds, true); + builder.setImageFilter(nullptr); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), save_bounds); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setImageFilter( + SkImageFilters::ColorFilter(alpha_color_filter, nullptr)); + builder.saveLayer(nullptr, true); + builder.setImageFilter(nullptr); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), build_bounds); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setBlendMode(SkBlendMode::kClear); + builder.saveLayer(&save_bounds, true); + builder.setBlendMode(SkBlendMode::kSrcOver); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), save_bounds); + } + + { + DisplayListBuilder builder(build_bounds); + builder.setBlendMode(SkBlendMode::kClear); + builder.saveLayer(nullptr, true); + builder.setBlendMode(SkBlendMode::kSrcOver); + builder.drawRect(rect); + builder.restore(); + sk_sp display_list = builder.Build(); + ASSERT_EQ(display_list->bounds(), build_bounds); + } +} + +} // namespace testing +} // namespace flutter diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc new file mode 100644 index 0000000000000..be2d47cff1820 --- /dev/null +++ b/flow/display_list_utils.cc @@ -0,0 +1,514 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/flow/display_list_utils.h" +#include "flutter/flow/layers/physical_shape_layer.h" +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkTextBlob.h" +#include "third_party/skia/include/utils/SkShadowUtils.h" + +namespace flutter { + +// clang-format off +constexpr float invert_color_matrix[20] = { + -1.0, 0, 0, 1.0, 0, + 0, -1.0, 0, 1.0, 0, + 0, 0, -1.0, 1.0, 0, + 1.0, 1.0, 1.0, 1.0, 0 +}; +// clang-format on + +void SkPaintDispatchHelper::setAA(bool aa) { + paint_.setAntiAlias(aa); +} +void SkPaintDispatchHelper::setDither(bool dither) { + paint_.setDither(dither); +} +void SkPaintDispatchHelper::setInvertColors(bool invert) { + invert_colors_ = invert; + paint_.setColorFilter(makeColorFilter()); +} +void SkPaintDispatchHelper::setCaps(SkPaint::Cap cap) { + paint_.setStrokeCap(cap); +} +void SkPaintDispatchHelper::setJoins(SkPaint::Join join) { + paint_.setStrokeJoin(join); +} +void SkPaintDispatchHelper::setDrawStyle(SkPaint::Style style) { + paint_.setStyle(style); +} +void SkPaintDispatchHelper::setStrokeWidth(SkScalar width) { + paint_.setStrokeWidth(width); +} +void SkPaintDispatchHelper::setMiterLimit(SkScalar limit) { + paint_.setStrokeMiter(limit); +} +void SkPaintDispatchHelper::setColor(SkColor color) { + paint_.setColor(color); +} +void SkPaintDispatchHelper::setBlendMode(SkBlendMode mode) { + paint_.setBlendMode(mode); +} +void SkPaintDispatchHelper::setBlender(sk_sp blender) { + paint_.setBlender(blender); +} +void SkPaintDispatchHelper::setShader(sk_sp shader) { + paint_.setShader(shader); +} +void SkPaintDispatchHelper::setImageFilter(sk_sp filter) { + paint_.setImageFilter(filter); +} +void SkPaintDispatchHelper::setColorFilter(sk_sp filter) { + color_filter_ = filter; + paint_.setColorFilter(makeColorFilter()); +} +void SkPaintDispatchHelper::setPathEffect(sk_sp effect) { + paint_.setPathEffect(effect); +} +void SkPaintDispatchHelper::setMaskFilter(sk_sp filter) { + paint_.setMaskFilter(filter); +} +void SkPaintDispatchHelper::setMaskBlurFilter(SkBlurStyle style, + SkScalar sigma) { + paint_.setMaskFilter(SkMaskFilter::MakeBlur(style, sigma)); +} + +sk_sp SkPaintDispatchHelper::makeColorFilter() { + if (!invert_colors_) { + return color_filter_; + } + sk_sp invert_filter = + SkColorFilters::Matrix(invert_color_matrix); + if (color_filter_) { + invert_filter = invert_filter->makeComposed(color_filter_); + } + return invert_filter; +} + +void SkMatrixDispatchHelper::translate(SkScalar tx, SkScalar ty) { + matrix_.preTranslate(tx, ty); +} +void SkMatrixDispatchHelper::scale(SkScalar sx, SkScalar sy) { + matrix_.preScale(sx, sy); +} +void SkMatrixDispatchHelper::rotate(SkScalar degrees) { + matrix_.preRotate(degrees); +} +void SkMatrixDispatchHelper::skew(SkScalar sx, SkScalar sy) { + matrix_.preSkew(sx, sy); +} +void SkMatrixDispatchHelper::transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + matrix_.preConcat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1)); +} +void SkMatrixDispatchHelper::transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) { + matrix_.preConcat( + SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt)); +} +void SkMatrixDispatchHelper::save() { + saved_.push_back(matrix_); +} +void SkMatrixDispatchHelper::restore() { + matrix_ = saved_.back(); + saved_.pop_back(); +} +void SkMatrixDispatchHelper::reset() { + matrix_.reset(); +} + +void ClipBoundsDispatchHelper::clipRect(const SkRect& rect, + bool isAA, + SkClipOp clip_op) { + if (clip_op == SkClipOp::kIntersect) { + intersect(rect); + } +} +void ClipBoundsDispatchHelper::clipRRect(const SkRRect& rrect, + bool isAA, + SkClipOp clip_op) { + if (clip_op == SkClipOp::kIntersect) { + intersect(rrect.getBounds()); + } +} +void ClipBoundsDispatchHelper::clipPath(const SkPath& path, + bool isAA, + SkClipOp clip_op) { + if (clip_op == SkClipOp::kIntersect) { + intersect(path.getBounds()); + } +} +void ClipBoundsDispatchHelper::intersect(const SkRect& rect) { + SkRect devClipBounds = matrix().mapRect(rect); + if (!bounds_.intersect(devClipBounds)) { + bounds_.setEmpty(); + } +} +void ClipBoundsDispatchHelper::save() { + saved_.push_back(bounds_); +} +void ClipBoundsDispatchHelper::restore() { + bounds_ = saved_.back(); + saved_.pop_back(); +} + +void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, + bool with_paint) { + SkMatrixDispatchHelper::save(); + ClipBoundsDispatchHelper::save(); + saved_infos_.emplace_back( + with_paint ? std::make_unique( + this, accumulator_, matrix(), bounds, paint()) + : std::make_unique(accumulator_, matrix())); + accumulator_ = saved_infos_.back()->save(); + SkMatrixDispatchHelper::reset(); +} +void DisplayListBoundsCalculator::save() { + SkMatrixDispatchHelper::save(); + ClipBoundsDispatchHelper::save(); + saved_infos_.emplace_back(std::make_unique(accumulator_)); + accumulator_ = saved_infos_.back()->save(); +} +void DisplayListBoundsCalculator::restore() { + if (!saved_infos_.empty()) { + SkMatrixDispatchHelper::restore(); + ClipBoundsDispatchHelper::restore(); + accumulator_ = saved_infos_.back()->restore(); + saved_infos_.pop_back(); + } +} + +void DisplayListBoundsCalculator::drawPaint() { + if (!bounds_cull_.isEmpty()) { + root_accumulator_.accumulate(bounds_cull_); + } +} +void DisplayListBoundsCalculator::drawColor(SkColor color, SkBlendMode mode) { + if (!bounds_cull_.isEmpty()) { + root_accumulator_.accumulate(bounds_cull_); + } +} +void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, + const SkPoint& p1) { + SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); + accumulateRect(bounds, true); +} +void DisplayListBoundsCalculator::drawRect(const SkRect& rect) { + accumulateRect(rect); +} +void DisplayListBoundsCalculator::drawOval(const SkRect& bounds) { + accumulateRect(bounds); +} +void DisplayListBoundsCalculator::drawCircle(const SkPoint& center, + SkScalar radius) { + accumulateRect(SkRect::MakeLTRB(center.fX - radius, center.fY - radius, + center.fX + radius, center.fY + radius)); +} +void DisplayListBoundsCalculator::drawRRect(const SkRRect& rrect) { + accumulateRect(rrect.getBounds()); +} +void DisplayListBoundsCalculator::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + accumulateRect(outer.getBounds()); +} +void DisplayListBoundsCalculator::drawPath(const SkPath& path) { + accumulateRect(path.getBounds()); +} +void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + // This could be tighter if we compute where the start and end + // angles are and then also consider the quadrants swept and + // the center if specified. + accumulateRect(bounds); +} +void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + if (count > 0) { + BoundsAccumulator ptBounds; + for (size_t i = 0; i < count; i++) { + ptBounds.accumulate(pts[i]); + } + accumulateRect(ptBounds.getBounds(), true); + } +} +void DisplayListBoundsCalculator::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + accumulateRect(vertices->bounds()); +} +void DisplayListBoundsCalculator::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) { + SkRect bounds = SkRect::Make(image->bounds()); + bounds.offset(point); + accumulateRect(bounds); +} +void DisplayListBoundsCalculator::drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) { + accumulateRect(dst); +} +void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) { + accumulateRect(dst); +} +void DisplayListBoundsCalculator::drawImageLattice( + const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) { + accumulateRect(dst); +} +void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) { + SkPoint quad[4]; + BoundsAccumulator atlasBounds; + for (int i = 0; i < count; i++) { + const SkRect& src = tex[i]; + xform[i].toQuad(src.width(), src.height(), quad); + for (int j = 0; j < 4; j++) { + atlasBounds.accumulate(quad[j]); + } + } + if (atlasBounds.isNotEmpty()) { + accumulateRect(atlasBounds.getBounds()); + } +} +void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, + const SkMatrix* pic_matrix, + bool with_save_layer) { + // TODO(flar) cull rect really cannot be trusted in general, but it will + // work for SkPictures generated from our own PictureRecorder or any + // picture captured with an SkRTreeFactory or accurate bounds estimate. + SkRect bounds = picture->cullRect(); + if (pic_matrix) { + pic_matrix->mapRect(&bounds); + } + if (with_save_layer) { + accumulateRect(bounds); + } else { + matrix().mapRect(&bounds); + accumulator_->accumulate(bounds); + } +} +void DisplayListBoundsCalculator::drawDisplayList( + const sk_sp display_list) { + accumulateRect(display_list->bounds()); +} +void DisplayListBoundsCalculator::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + accumulateRect(blob->bounds().makeOffset(x, y)); +} +void DisplayListBoundsCalculator::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) { + accumulateRect( + PhysicalShapeLayer::ComputeShadowBounds(path, elevation, dpr, matrix())); +} + +void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect, + bool forceStroke) { + SkRect dstRect = rect; + const SkPaint& p = paint(); + if (forceStroke) { + if (p.getStyle() == SkPaint::kFill_Style) { + setDrawStyle(SkPaint::kStroke_Style); + } else { + forceStroke = false; + } + } + if (p.canComputeFastBounds()) { + dstRect = p.computeFastBounds(rect, &dstRect); + matrix().mapRect(&dstRect); + accumulator_->accumulate(dstRect); + } else { + root_accumulator_.accumulate(bounds_cull_); + } + if (forceStroke) { + setDrawStyle(SkPaint::kFill_Style); + } +} + +DisplayListBoundsCalculator::SaveInfo::SaveInfo(BoundsAccumulator* accumulator) + : saved_accumulator_(accumulator) {} +BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::save() { + // No need to swap out the accumulator for a normal save + return saved_accumulator_; +} +BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::restore() { + return saved_accumulator_; +} + +DisplayListBoundsCalculator::SaveLayerInfo::SaveLayerInfo( + BoundsAccumulator* accumulator, + const SkMatrix& matrix) + : SaveInfo(accumulator), matrix_(matrix) {} +BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::save() { + // Use the local layerAccumulator until restore is called and + // then transform (and adjust with paint if necessary) on restore() + return &layer_accumulator_; +} +BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::restore() { + SkRect layer_bounds = layer_accumulator_.getBounds(); + layer_bounds.roundOut(&layer_bounds); + matrix_.mapRect(&layer_bounds); + saved_accumulator_->accumulate(layer_bounds); + return saved_accumulator_; +} + +DisplayListBoundsCalculator::SaveLayerWithPaintInfo::SaveLayerWithPaintInfo( + DisplayListBoundsCalculator* calculator, + BoundsAccumulator* accumulator, + const SkMatrix& saveMatrix, + const SkRect* saveBounds, + const SkPaint& savePaint) + : SaveLayerInfo(accumulator, saveMatrix), + calculator_(calculator), + paint_(savePaint) { + if (saveBounds) { + bounds_.emplace(*saveBounds); + } +} + +static bool PaintNopsOnTransparenBlack(const SkPaint& paint) { + SkImageFilter* image_filter = paint.getImageFilter(); + // SkImageFilter::canComputeFastBounds tests for transparency behavior + // This test assumes that the blend mode checked down below will + // NOP on transparent black. + if (image_filter && !image_filter->canComputeFastBounds()) { + return false; + } + + SkColorFilter* color_filter = paint.getColorFilter(); + // We filter the transparent black that is used for the background of a + // saveLayer and make sure it returns transparent black. If it does, then + // the color filter will leave all area surrounding the contents of the + // save layer untouched out to the edge of the output surface. + // This test assumes that the blend mode checked down below will + // NOP on transparent black. + if (color_filter && + color_filter->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT) { + return false; + } + + const auto blend_mode = paint.asBlendMode(); + if (!blend_mode) { + return false; // can we query other blenders for this? + } + // Unusual blendmodes require us to process a saved layer + // even with operations outisde the clip. + // For example, DstIn is used by masking layers. + // https://code.google.com/p/skia/issues/detail?id=1291 + // https://crbug.com/401593 + switch (blend_mode.value()) { + // For each of the following transfer modes, if the source + // alpha is zero (our transparent black), the resulting + // blended pixel is not necessarily equal to the original + // destination pixel. + // Mathematically, any time in the following equations where + // the result is not d assuming source is 0 + case SkBlendMode::kClear: // r = 0 + case SkBlendMode::kSrc: // r = s + case SkBlendMode::kSrcIn: // r = s * da + case SkBlendMode::kDstIn: // r = d * sa + case SkBlendMode::kSrcOut: // r = s * (1-da) + case SkBlendMode::kDstATop: // r = d*sa + s*(1-da) + case SkBlendMode::kModulate: // r = s*d + return false; + break; + + // And in these equations, the result must be d if the + // source is 0 + case SkBlendMode::kDst: // r = d + case SkBlendMode::kSrcOver: // r = s + (1-sa)*d + case SkBlendMode::kDstOver: // r = d + (1-da)*s + case SkBlendMode::kDstOut: // r = d * (1-sa) + case SkBlendMode::kSrcATop: // r = s*da + d*(1-sa) + case SkBlendMode::kXor: // r = s*(1-da) + d*(1-sa) + case SkBlendMode::kPlus: // r = min(s + d, 1) + case SkBlendMode::kScreen: // r = s + d - s*d + case SkBlendMode::kOverlay: // multiply or screen, depending on dest + case SkBlendMode::kDarken: // rc = s + d - max(s*da, d*sa), + // ra = kSrcOver + case SkBlendMode::kLighten: // rc = s + d - min(s*da, d*sa), + // ra = kSrcOver + case SkBlendMode::kColorDodge: // brighten destination to reflect source + case SkBlendMode::kColorBurn: // darken destination to reflect source + case SkBlendMode::kHardLight: // multiply or screen, depending on source + case SkBlendMode::kSoftLight: // lighten or darken, depending on source + case SkBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)), + // ra = kSrcOver + case SkBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver + case SkBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d + case SkBlendMode::kHue: // ra = kSrcOver + case SkBlendMode::kSaturation: // ra = kSrcOver + case SkBlendMode::kColor: // ra = kSrcOver + case SkBlendMode::kLuminosity: // ra = kSrcOver + return true; + break; + } +} + +BoundsAccumulator* +DisplayListBoundsCalculator::SaveLayerWithPaintInfo::restore() { + SkRect layer_bounds; + if (paint_.canComputeFastBounds() && PaintNopsOnTransparenBlack(paint_)) { + // The ideal situation. The paint can compute the bounds AND the + // surrounding transparent pixels will not affect the destination. + layer_bounds = layer_accumulator_.getBounds(); + layer_bounds = paint_.computeFastBounds(layer_bounds, &layer_bounds); + } else if (bounds_.has_value()) { + // Bounds were provided by the save layer, the operation will affect + // all of those bounds. + layer_bounds = bounds_.value(); + } else { + // Bounds were not provided for the save layer. We will fill to the + // cull bounds provided to the original DisplayList. + calculator_->root_accumulator_.accumulate(calculator_->bounds_cull_); + // There is no need to process the layer bounds further as we just + // expanded bounds to the cull rect of the DisplayList. + return saved_accumulator_; + } + layer_bounds.roundOut(&layer_bounds); + matrix_.mapRect(&layer_bounds); + saved_accumulator_->accumulate(layer_bounds); + return saved_accumulator_; +} + +} // namespace flutter diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h new file mode 100644 index 0000000000000..af169604b2f14 --- /dev/null +++ b/flow/display_list_utils.h @@ -0,0 +1,395 @@ +// 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_FLOW_DISPLAY_LIST_UTILS_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ + +#include + +#include "flutter/flow/display_list.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" + +#include "third_party/skia/include/core/SkMaskFilter.h" + +// This file contains various utility classes to ease implementing +// a Flutter DisplayList Dispatcher, including: +// +// IgnoreAttributeDispatchHelper: +// IgnoreClipDispatchHelper: +// IgnoreTransformDispatchHelper +// Empty overrides of all of the associated methods of Dispatcher +// for dispatchers that only track some of the rendering operations +// +// SkPaintAttributeDispatchHelper: +// Tracks the attribute methods and maintains their state in an +// SkPaint object. +// SkMatrixTransformDispatchHelper: +// Tracks the transform methods and maintains their state in a +// (save/restore stack of) SkMatrix object. +// ClipBoundsDispatchHelper: +// Tracks the clip methods and maintains a culling box in a +// (save/restore stack of) SkRect culling rectangle. +// +// DisplayListBoundsCalculator: +// A class that can traverse an entire display list and compute +// a conservative estimate of the bounds of all of the rendering +// operations. + +namespace flutter { + +// A utility class that will ignore all Dispatcher methods relating +// to the setting of attributes. +class IgnoreAttributeDispatchHelper : public virtual Dispatcher { + public: + void setAA(bool aa) override {} + void setDither(bool dither) override {} + void setInvertColors(bool invert) override {} + void setCaps(SkPaint::Cap cap) override {} + void setJoins(SkPaint::Join join) override {} + void setDrawStyle(SkPaint::Style style) override {} + void setStrokeWidth(SkScalar width) override {} + void setMiterLimit(SkScalar limit) override {} + void setColor(SkColor color) override {} + void setBlendMode(SkBlendMode mode) override {} + void setBlender(sk_sp blender) override {} + void setShader(sk_sp shader) override {} + void setImageFilter(sk_sp filter) override {} + void setColorFilter(sk_sp filter) override {} + void setPathEffect(sk_sp effect) override {} + void setMaskFilter(sk_sp filter) override {} + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override {} +}; + +// A utility class that will ignore all Dispatcher methods relating +// to setting a clip. +class IgnoreClipDispatchHelper : public virtual Dispatcher { + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override {} + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override {} + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override {} +}; + +// A utility class that will ignore all Dispatcher methods relating +// to modifying the transform. +class IgnoreTransformDispatchHelper : public virtual Dispatcher { + public: + void translate(SkScalar tx, SkScalar ty) override {} + void scale(SkScalar sx, SkScalar sy) override {} + void rotate(SkScalar degrees) override {} + void skew(SkScalar sx, SkScalar sy) override {} + void transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override {} + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override {} +}; + +// A utility class that will monitor the Dispatcher methods relating +// to the rendering attributes and accumulate them into an SkPaint +// which can be accessed at any time via paint(). +class SkPaintDispatchHelper : public virtual Dispatcher { + public: + void setAA(bool aa) override; + void setDither(bool dither) override; + void setInvertColors(bool invert) override; + void setCaps(SkPaint::Cap cap) override; + void setJoins(SkPaint::Join join) override; + void setDrawStyle(SkPaint::Style style) override; + void setStrokeWidth(SkScalar width) override; + void setMiterLimit(SkScalar limit) override; + void setColor(SkColor color) override; + void setBlendMode(SkBlendMode mode) override; + void setBlender(sk_sp blender) override; + void setShader(sk_sp shader) override; + void setImageFilter(sk_sp filter) override; + void setColorFilter(sk_sp filter) override; + void setPathEffect(sk_sp effect) override; + void setMaskFilter(sk_sp filter) override; + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + + const SkPaint& paint() { return paint_; } + + private: + SkPaint paint_; + bool invert_colors_ = false; + sk_sp color_filter_; + + sk_sp makeColorFilter(); +}; + +class SkMatrixSource { + public: + virtual const SkMatrix& matrix() const = 0; +}; + +// A utility class that will monitor the Dispatcher methods relating +// to the transform and accumulate them into an SkMatrix which can +// be accessed at any time via getMatrix(). +// +// This class also implements an appropriate stack of transforms via +// its save() and restore() methods so those methods will need to be +// forwarded if overridden in more than one super class. +class SkMatrixDispatchHelper : public virtual Dispatcher, + public virtual SkMatrixSource { + public: + void translate(SkScalar tx, SkScalar ty) override; + void scale(SkScalar sx, SkScalar sy) override; + void rotate(SkScalar degrees) override; + void skew(SkScalar sx, SkScalar sy) override; + void transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override; + + void save() override; + void restore() override; + + const SkMatrix& matrix() const override { return matrix_; } + + protected: + void reset(); + + private: + SkMatrix matrix_; + std::vector saved_; +}; + +// A utility class that will monitor the Dispatcher methods relating +// to the clip and accumulate a conservative bounds into an SkRect +// which can be accessed at any time via getCullingBounds(). +// +// The subclass must implement a single virtual method matrix() +// which will happen automatically if the subclass also inherits +// from SkMatrixTransformDispatchHelper. +// +// This class also implements an appropriate stack of transforms via +// its save() and restore() methods so those methods will need to be +// forwarded if overridden in more than one super class. +class ClipBoundsDispatchHelper : public virtual Dispatcher, + private virtual SkMatrixSource { + public: + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override; + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override; + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override; + + void save() override; + void restore() override; + + const SkRect& getCullingBounds() const { return bounds_; } + + private: + SkRect bounds_; + std::vector saved_; + + void intersect(const SkRect& clipBounds); +}; + +class BoundsAccumulator { + public: + void accumulate(const SkPoint& p) { accumulate(p.fX, p.fY); } + void accumulate(SkScalar x, SkScalar y) { + if (min_x_ > x) + min_x_ = x; + if (min_y_ > y) + min_y_ = y; + if (max_x_ < x) + max_x_ = x; + if (max_y_ < y) + max_y_ = y; + } + void accumulate(const SkRect& r) { + if (r.fLeft <= r.fRight && r.fTop <= r.fBottom) { + accumulate(r.fLeft, r.fTop); + accumulate(r.fRight, r.fBottom); + } + } + + bool isEmpty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; } + bool isNotEmpty() const { return min_x_ < max_x_ && min_y_ < max_y_; } + + SkRect getBounds() const { + return (max_x_ > min_x_ && max_y_ > min_y_) + ? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_) + : SkRect::MakeEmpty(); + } + + private: + SkScalar min_x_ = std::numeric_limits::infinity(); + SkScalar min_y_ = std::numeric_limits::infinity(); + SkScalar max_x_ = -std::numeric_limits::infinity(); + SkScalar max_y_ = -std::numeric_limits::infinity(); +}; + +// This class implements all rendering methods and computes a liberal +// bounds of the rendering operations. +class DisplayListBoundsCalculator final + : public virtual Dispatcher, + public virtual SkPaintDispatchHelper, + public virtual SkMatrixDispatchHelper, + public virtual ClipBoundsDispatchHelper { + public: + // Construct a Calculator to determine the bounds of a list of + // DisplayList dispatcher method calls. Since 2 of the method calls + // have no intrinsic size because they render to the entire available, + // the |cullRect| provides a bounds for them to include. + DisplayListBoundsCalculator(const SkRect& cull_rect = SkRect::MakeEmpty()) + : accumulator_(&root_accumulator_), bounds_cull_(cull_rect) {} + + void saveLayer(const SkRect* bounds, bool with_paint) override; + void save() override; + void restore() override; + + void drawPaint() override; + void drawColor(SkColor color, SkBlendMode mode) override; + void drawLine(const SkPoint& p0, const SkPoint& p1) override; + void drawRect(const SkRect& rect) override; + void drawOval(const SkRect& bounds) override; + void drawCircle(const SkPoint& center, SkScalar radius) override; + void drawRRect(const SkRRect& rrect) override; + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + void drawPath(const SkPath& path) override; + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) override; + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) override; + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool with_paint) override; + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) override; + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool with_save_layer) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes, + SkScalar dpr) override; + + SkRect getBounds() { + FML_DCHECK(accumulator_ == &root_accumulator_); + return root_accumulator_.getBounds(); + } + + private: + // current accumulator based on saveLayer history + BoundsAccumulator* accumulator_; + + // Only used for drawColor and drawPaint and paint objects that + // cannot support fast bounds. + SkRect bounds_cull_; + BoundsAccumulator root_accumulator_; + + class SaveInfo { + public: + SaveInfo(BoundsAccumulator* accumulator); + virtual ~SaveInfo() = default; + + virtual BoundsAccumulator* save(); + virtual BoundsAccumulator* restore(); + + protected: + BoundsAccumulator* saved_accumulator_; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(SaveInfo); + }; + + class SaveLayerInfo : public SaveInfo { + public: + SaveLayerInfo(BoundsAccumulator* accumulator, const SkMatrix& matrix); + virtual ~SaveLayerInfo() = default; + + BoundsAccumulator* save() override; + BoundsAccumulator* restore() override; + + protected: + BoundsAccumulator layer_accumulator_; + const SkMatrix matrix_; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(SaveLayerInfo); + }; + + class SaveLayerWithPaintInfo : public SaveLayerInfo { + public: + SaveLayerWithPaintInfo(DisplayListBoundsCalculator* calculator, + BoundsAccumulator* accumulator, + const SkMatrix& save_matrix, + const SkRect* bounds, + const SkPaint& save_paint); + virtual ~SaveLayerWithPaintInfo() = default; + + BoundsAccumulator* restore() override; + + protected: + DisplayListBoundsCalculator* calculator_; + + std::optional bounds_; + SkPaint paint_; + + private: + static constexpr SkRect kMissingBounds = SkRect::MakeWH(-1, -1); + + FML_DISALLOW_COPY_AND_ASSIGN(SaveLayerWithPaintInfo); + }; + + std::vector> saved_infos_; + + void accumulateRect(const SkRect& rect, bool force_stroke = false); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ diff --git a/flow/embedded_view_params_unittests.cc b/flow/embedded_view_params_unittests.cc index cb94936e17ddf..aa9604cae9922 100644 --- a/flow/embedded_view_params_unittests.cc +++ b/flow/embedded_view_params_unittests.cc @@ -36,7 +36,7 @@ TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithScale) { TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithTranslate) { MutatorsStack stack; - SkMatrix matrix = SkMatrix::MakeTrans(1, 1); + SkMatrix matrix = SkMatrix::Translate(1, 1); stack.PushTransform(matrix); EmbeddedViewParams params(matrix, SkSize::Make(1, 1), stack); @@ -79,7 +79,7 @@ TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithRotation45) { TEST(EmbeddedViewParams, GetBoundingRectAfterMutationsWithTranslateScaleAndRotation) { - SkMatrix matrix = SkMatrix::MakeTrans(2, 2); + SkMatrix matrix = SkMatrix::Translate(2, 2); matrix.preScale(3, 3); matrix.preRotate(90); diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 9441c8dc9470c..bf4ccaad319fb 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,8 +6,10 @@ namespace flutter { -void ExternalViewEmbedder::SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) { +void ExternalViewEmbedder::SubmitFrame( + GrDirectContext* context, + std::unique_ptr frame, + const std::shared_ptr& gpu_disable_sync_switch) { frame->Submit(); }; diff --git a/flow/embedded_views.h b/flow/embedded_views.h index e82d9e82c0471..8f8e228636169 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -10,6 +10,7 @@ #include "flutter/flow/surface_frame.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/raster_thread_merger.h" +#include "flutter/fml/synchronization/sync_switch.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint.h" @@ -123,7 +124,7 @@ class Mutator { // // For example consider the following stack: [T1, T2, T3], where T1 is the top // of the stack and T3 is the bottom of the stack. Applying this mutators stack -// to a platform view P1 will result in T1(T2(T2(P1))). +// to a platform view P1 will result in T1(T2(T3(P1))). class MutatorsStack { public: MutatorsStack() = default; @@ -147,7 +148,7 @@ class MutatorsStack { const std::vector>::const_reverse_iterator Bottom() const; - // Returns an iterator pointing to the begining of the mutator vector, which + // Returns an iterator pointing to the beginning of the mutator vector, which // is the mutator that is furtherest from the leaf node. const std::vector>::const_iterator Begin() const; @@ -217,6 +218,9 @@ class EmbeddedViewParams { final_bounding_rect_ = other.final_bounding_rect_; }; + // The transformation Matrix corresponding to the sum of all the + // transformations in the platform view's mutator stack. + const SkMatrix& transformMatrix() const { return matrix_; }; // The original size of the platform view before any mutation matrix is // applied. const SkSize& sizePoints() const { return size_points_; }; @@ -276,7 +280,7 @@ class ExternalViewEmbedder { // sets the stage for the next pre-roll. virtual void CancelFrame() = 0; - // Indicates the begining of a frame. + // Indicates the beginning of a frame. // // The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging| // returns false. @@ -309,8 +313,10 @@ class ExternalViewEmbedder { // This method can mutate the root Skia canvas before submitting the frame. // // It can also allocate frames for overlay surfaces to compose hybrid views. - virtual void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame); + virtual void SubmitFrame( + GrDirectContext* context, + std::unique_ptr frame, + const std::shared_ptr& gpu_disable_sync_switch); // This method provides the embedder a way to do additional tasks after // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame` diff --git a/flow/frame_timings.cc b/flow/frame_timings.cc new file mode 100644 index 0000000000000..b5169c1621c85 --- /dev/null +++ b/flow/frame_timings.cc @@ -0,0 +1,175 @@ +// 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/flow/frame_timings.h" + +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/time/time_point.h" + +namespace flutter { + +std::atomic FrameTimingsRecorder::frame_number_gen_ = {1}; + +static std::string ToString(uint64_t val) { + std::stringstream stream; + stream << val; + return stream.str(); +} + +FrameTimingsRecorder::FrameTimingsRecorder() + : frame_number_(frame_number_gen_++), + frame_number_trace_arg_val_(ToString(frame_number_)) {} + +FrameTimingsRecorder::FrameTimingsRecorder(uint64_t frame_number) + : frame_number_(frame_number), + frame_number_trace_arg_val_(ToString(frame_number_)) {} + +FrameTimingsRecorder::~FrameTimingsRecorder() = default; + +fml::TimePoint FrameTimingsRecorder::GetVsyncStartTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kVsync); + return vsync_start_; +} + +fml::TimePoint FrameTimingsRecorder::GetVsyncTargetTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kVsync); + return vsync_target_; +} + +fml::TimePoint FrameTimingsRecorder::GetBuildStartTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kBuildStart); + return build_start_; +} + +fml::TimePoint FrameTimingsRecorder::GetBuildEndTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kBuildEnd); + return build_end_; +} + +fml::TimePoint FrameTimingsRecorder::GetRasterStartTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kRasterStart); + return raster_start_; +} + +fml::TimePoint FrameTimingsRecorder::GetRasterEndTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kRasterEnd); + return raster_end_; +} + +fml::TimePoint FrameTimingsRecorder::GetRasterEndWallTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kRasterEnd); + return raster_end_wall_time_; +} + +fml::TimeDelta FrameTimingsRecorder::GetBuildDuration() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ >= State::kBuildEnd); + return build_end_ - build_start_; +} + +void FrameTimingsRecorder::RecordVsync(fml::TimePoint vsync_start, + fml::TimePoint vsync_target) { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ == State::kUninitialized); + state_ = State::kVsync; + vsync_start_ = vsync_start; + vsync_target_ = vsync_target; +} + +void FrameTimingsRecorder::RecordBuildStart(fml::TimePoint build_start) { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ == State::kVsync); + state_ = State::kBuildStart; + build_start_ = build_start; +} + +void FrameTimingsRecorder::RecordBuildEnd(fml::TimePoint build_end) { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ == State::kBuildStart); + state_ = State::kBuildEnd; + build_end_ = build_end; +} + +void FrameTimingsRecorder::RecordRasterStart(fml::TimePoint raster_start) { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ == State::kBuildEnd); + state_ = State::kRasterStart; + raster_start_ = raster_start; +} + +FrameTiming FrameTimingsRecorder::RecordRasterEnd() { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ == State::kRasterStart); + state_ = State::kRasterEnd; + raster_end_ = fml::TimePoint::Now(); + raster_end_wall_time_ = fml::TimePoint::CurrentWallTime(); + timing_.Set(FrameTiming::kVsyncStart, vsync_start_); + timing_.Set(FrameTiming::kBuildStart, build_start_); + timing_.Set(FrameTiming::kBuildFinish, build_end_); + timing_.Set(FrameTiming::kRasterStart, raster_start_); + timing_.Set(FrameTiming::kRasterFinish, raster_end_); + timing_.Set(FrameTiming::kRasterFinishWallTime, raster_end_wall_time_); + timing_.SetFrameNumber(GetFrameNumber()); + return timing_; +} + +FrameTiming FrameTimingsRecorder::GetRecordedTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_DCHECK(state_ == State::kRasterEnd); + return timing_; +} + +std::unique_ptr FrameTimingsRecorder::CloneUntil( + State state) { + std::scoped_lock state_lock(state_mutex_); + std::unique_ptr recorder = + std::make_unique(frame_number_); + FML_DCHECK(state_ >= state); + recorder->state_ = state; + + if (state >= State::kVsync) { + recorder->vsync_start_ = vsync_start_; + recorder->vsync_target_ = vsync_target_; + } + + if (state >= State::kBuildStart) { + recorder->build_start_ = build_start_; + } + + if (state >= State::kBuildEnd) { + recorder->build_end_ = build_end_; + } + + if (state >= State::kRasterStart) { + recorder->raster_start_ = raster_start_; + } + + if (state >= State::kRasterEnd) { + recorder->raster_end_ = raster_end_; + recorder->raster_end_wall_time_ = raster_end_wall_time_; + } + + return recorder; +} + +uint64_t FrameTimingsRecorder::GetFrameNumber() const { + return frame_number_; +} + +const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const { + return frame_number_trace_arg_val_.c_str(); +} + +} // namespace flutter diff --git a/flow/frame_timings.h b/flow/frame_timings.h new file mode 100644 index 0000000000000..a882a45db98fd --- /dev/null +++ b/flow/frame_timings.h @@ -0,0 +1,128 @@ +// 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_FLOW_FRAME_TIMINGS_H_ +#define FLUTTER_FLOW_FRAME_TIMINGS_H_ + +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" + +#define TRACE_EVENT_WITH_FRAME_NUMBER(recorder, category_group, name) \ + TRACE_EVENT1(category_group, name, "frame_number", \ + recorder->GetFrameNumberTraceArg()) + +namespace flutter { + +/// Records timestamps for various phases of a frame rendering process. +/// +/// Recorder is created on vsync and destroyed after the rasterization of the +/// frame. This class is thread safe and doesn't require additional +/// synchronization. +class FrameTimingsRecorder { + public: + /// Various states that the recorder can be in. When created the recorder is + /// in an unitialized state and transtions in sequential order of the states. + enum class State : uint32_t { + kUninitialized, + kVsync, + kBuildStart, + kBuildEnd, + kRasterStart, + kRasterEnd, + }; + + /// Default constructor, initializes the recorder with State::kUninitialized. + FrameTimingsRecorder(); + + /// Constructor with a pre-populated frame number. + FrameTimingsRecorder(uint64_t frame_number); + + ~FrameTimingsRecorder(); + + /// Timestamp of the vsync signal. + fml::TimePoint GetVsyncStartTime() const; + + /// Timestamp of when the frame was targeted to be presented. + /// + /// This is typically the next vsync signal timestamp. + fml::TimePoint GetVsyncTargetTime() const; + + /// Timestamp of when the frame building started. + fml::TimePoint GetBuildStartTime() const; + + /// Timestamp of when the frame was finished building. + fml::TimePoint GetBuildEndTime() const; + + /// Timestamp of when the frame rasterization started. + fml::TimePoint GetRasterStartTime() const; + + /// Timestamp of when the frame rasterization finished. + fml::TimePoint GetRasterEndTime() const; + + /// Timestamp of when the frame rasterization is complete in wall-time. + fml::TimePoint GetRasterEndWallTime() const; + + /// Duration of the frame build time. + fml::TimeDelta GetBuildDuration() const; + + /// Records a vsync event. + void RecordVsync(fml::TimePoint vsync_start, fml::TimePoint vsync_target); + + /// Records a build start event. + void RecordBuildStart(fml::TimePoint build_start); + + /// Records a build end event. + void RecordBuildEnd(fml::TimePoint build_end); + + /// Records a raster start event. + void RecordRasterStart(fml::TimePoint raster_start); + + /// Clones the recorder until (and including) the specified state. + std::unique_ptr CloneUntil(State state); + + /// Records a raster end event, and builds a `FrameTiming` that summarizes all + /// the events. This summary is sent to the framework. + FrameTiming RecordRasterEnd(); + + /// Returns the frame number. Frame number is unique per frame and a frame + /// built earlier will have a frame number less than a frame that has been + /// built at a later point of time. + uint64_t GetFrameNumber() const; + + /// Returns the frame number in a fml tracing friendly format. + const char* GetFrameNumberTraceArg() const; + + /// Returns the recorded time from when `RecordRasterEnd` is called. + FrameTiming GetRecordedTime() const; + + private: + static std::atomic frame_number_gen_; + + mutable std::mutex state_mutex_; + State state_ = State::kUninitialized; + + const uint64_t frame_number_; + const std::string frame_number_trace_arg_val_; + + fml::TimePoint vsync_start_; + fml::TimePoint vsync_target_; + fml::TimePoint build_start_; + fml::TimePoint build_end_; + fml::TimePoint raster_start_; + fml::TimePoint raster_end_; + fml::TimePoint raster_end_wall_time_; + + // Set when `RecordRasterEnd` is called. Cannot be reset once set. + FrameTiming timing_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(FrameTimingsRecorder); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_FRAME_TIMINGS_H_ diff --git a/flow/frame_timings_recorder_unittests.cc b/flow/frame_timings_recorder_unittests.cc new file mode 100644 index 0000000000000..3cbb33ca9751f --- /dev/null +++ b/flow/frame_timings_recorder_unittests.cc @@ -0,0 +1,207 @@ +// 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/flow/frame_timings.h" + +#include + +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(FrameTimingsRecorderTest, RecordVsync) { + auto recorder = std::make_unique(); + const auto st = fml::TimePoint::Now(); + const auto en = st + fml::TimeDelta::FromMillisecondsF(16); + recorder->RecordVsync(st, en); + + ASSERT_EQ(st, recorder->GetVsyncStartTime()); + ASSERT_EQ(en, recorder->GetVsyncTargetTime()); +} + +TEST(FrameTimingsRecorderTest, RecordBuildTimes) { + auto recorder = std::make_unique(); + + const auto st = fml::TimePoint::Now(); + const auto en = st + fml::TimeDelta::FromMillisecondsF(16); + recorder->RecordVsync(st, en); + + const auto build_start = fml::TimePoint::Now(); + const auto build_end = build_start + fml::TimeDelta::FromMillisecondsF(16); + recorder->RecordBuildStart(build_start); + recorder->RecordBuildEnd(build_end); + + ASSERT_EQ(build_start, recorder->GetBuildStartTime()); + ASSERT_EQ(build_end, recorder->GetBuildEndTime()); +} + +TEST(FrameTimingsRecorderTest, RecordRasterTimes) { + auto recorder = std::make_unique(); + + const auto st = fml::TimePoint::Now(); + const auto en = st + fml::TimeDelta::FromMillisecondsF(16); + recorder->RecordVsync(st, en); + + const auto build_start = fml::TimePoint::Now(); + const auto build_end = build_start + fml::TimeDelta::FromMillisecondsF(16); + recorder->RecordBuildStart(build_start); + recorder->RecordBuildEnd(build_end); + + using namespace std::chrono_literals; + + const auto raster_start = fml::TimePoint::Now(); + recorder->RecordRasterStart(raster_start); + const auto before_raster_end_wall_time = fml::TimePoint::CurrentWallTime(); + std::this_thread::sleep_for(1ms); + const auto timing = recorder->RecordRasterEnd(); + std::this_thread::sleep_for(1ms); + const auto after_raster_end_wall_time = fml::TimePoint::CurrentWallTime(); + + ASSERT_EQ(raster_start, recorder->GetRasterStartTime()); + ASSERT_GT(recorder->GetRasterEndWallTime(), before_raster_end_wall_time); + ASSERT_LT(recorder->GetRasterEndWallTime(), after_raster_end_wall_time); + ASSERT_EQ(recorder->GetFrameNumber(), timing.GetFrameNumber()); +} + +// Windows and Fuchsia don't allow testing with killed by signal. +#if !defined(OS_FUCHSIA) && !defined(OS_WIN) && \ + (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) + +TEST(FrameTimingsRecorderTest, ThrowWhenRecordBuildBeforeVsync) { + auto recorder = std::make_unique(); + + const auto build_start = fml::TimePoint::Now(); + EXPECT_EXIT(recorder->RecordBuildStart(build_start), + ::testing::KilledBySignal(SIGABRT), + "Check failed: state_ == State::kVsync."); +} + +TEST(FrameTimingsRecorderTest, ThrowWhenRecordRasterBeforeBuildEnd) { + auto recorder = std::make_unique(); + + const auto st = fml::TimePoint::Now(); + const auto en = st + fml::TimeDelta::FromMillisecondsF(16); + recorder->RecordVsync(st, en); + + const auto raster_start = fml::TimePoint::Now(); + EXPECT_EXIT(recorder->RecordRasterStart(raster_start), + ::testing::KilledBySignal(SIGABRT), + "Check failed: state_ == State::kBuildEnd."); +} + +#endif + +TEST(FrameTimingsRecorderTest, RecordersHaveUniqueFrameNumbers) { + auto recorder1 = std::make_unique(); + auto recorder2 = std::make_unique(); + + ASSERT_TRUE(recorder2->GetFrameNumber() > recorder1->GetFrameNumber()); +} + +TEST(FrameTimingsRecorderTest, ClonedHasSameFrameNumber) { + auto recorder = std::make_unique(); + + auto cloned = + recorder->CloneUntil(FrameTimingsRecorder::State::kUninitialized); + ASSERT_EQ(recorder->GetFrameNumber(), cloned->GetFrameNumber()); +} + +TEST(FrameTimingsRecorderTest, ClonedHasSameVsyncStartAndTarget) { + auto recorder = std::make_unique(); + + const auto now = fml::TimePoint::Now(); + recorder->RecordVsync(now, now + fml::TimeDelta::FromMilliseconds(16)); + + auto cloned = recorder->CloneUntil(FrameTimingsRecorder::State::kVsync); + ASSERT_EQ(recorder->GetFrameNumber(), cloned->GetFrameNumber()); + ASSERT_EQ(recorder->GetVsyncStartTime(), cloned->GetVsyncStartTime()); + ASSERT_EQ(recorder->GetVsyncTargetTime(), cloned->GetVsyncTargetTime()); +} + +TEST(FrameTimingsRecorderTest, ClonedHasSameBuildStart) { + auto recorder = std::make_unique(); + + const auto now = fml::TimePoint::Now(); + recorder->RecordVsync(now, now + fml::TimeDelta::FromMilliseconds(16)); + recorder->RecordBuildStart(fml::TimePoint::Now()); + + auto cloned = recorder->CloneUntil(FrameTimingsRecorder::State::kBuildStart); + ASSERT_EQ(recorder->GetFrameNumber(), cloned->GetFrameNumber()); + ASSERT_EQ(recorder->GetVsyncStartTime(), cloned->GetVsyncStartTime()); + ASSERT_EQ(recorder->GetVsyncTargetTime(), cloned->GetVsyncTargetTime()); + ASSERT_EQ(recorder->GetBuildStartTime(), cloned->GetBuildStartTime()); +} + +TEST(FrameTimingsRecorderTest, ClonedHasSameBuildEnd) { + auto recorder = std::make_unique(); + + const auto now = fml::TimePoint::Now(); + recorder->RecordVsync(now, now + fml::TimeDelta::FromMilliseconds(16)); + recorder->RecordBuildStart(fml::TimePoint::Now()); + recorder->RecordBuildEnd(fml::TimePoint::Now()); + + auto cloned = recorder->CloneUntil(FrameTimingsRecorder::State::kBuildEnd); + ASSERT_EQ(recorder->GetFrameNumber(), cloned->GetFrameNumber()); + ASSERT_EQ(recorder->GetVsyncStartTime(), cloned->GetVsyncStartTime()); + ASSERT_EQ(recorder->GetVsyncTargetTime(), cloned->GetVsyncTargetTime()); + ASSERT_EQ(recorder->GetBuildStartTime(), cloned->GetBuildStartTime()); + ASSERT_EQ(recorder->GetBuildEndTime(), cloned->GetBuildEndTime()); +} + +TEST(FrameTimingsRecorderTest, ClonedHasSameRasterStart) { + auto recorder = std::make_unique(); + + const auto now = fml::TimePoint::Now(); + recorder->RecordVsync(now, now + fml::TimeDelta::FromMilliseconds(16)); + recorder->RecordBuildStart(fml::TimePoint::Now()); + recorder->RecordBuildEnd(fml::TimePoint::Now()); + recorder->RecordRasterStart(fml::TimePoint::Now()); + + auto cloned = recorder->CloneUntil(FrameTimingsRecorder::State::kRasterStart); + ASSERT_EQ(recorder->GetFrameNumber(), cloned->GetFrameNumber()); + ASSERT_EQ(recorder->GetVsyncStartTime(), cloned->GetVsyncStartTime()); + ASSERT_EQ(recorder->GetVsyncTargetTime(), cloned->GetVsyncTargetTime()); + ASSERT_EQ(recorder->GetBuildStartTime(), cloned->GetBuildStartTime()); + ASSERT_EQ(recorder->GetBuildEndTime(), cloned->GetBuildEndTime()); + ASSERT_EQ(recorder->GetRasterStartTime(), cloned->GetRasterStartTime()); +} + +TEST(FrameTimingsRecorderTest, ClonedHasSameRasterEnd) { + auto recorder = std::make_unique(); + + const auto now = fml::TimePoint::Now(); + recorder->RecordVsync(now, now + fml::TimeDelta::FromMilliseconds(16)); + recorder->RecordBuildStart(fml::TimePoint::Now()); + recorder->RecordBuildEnd(fml::TimePoint::Now()); + recorder->RecordRasterStart(fml::TimePoint::Now()); + recorder->RecordRasterEnd(); + + auto cloned = recorder->CloneUntil(FrameTimingsRecorder::State::kRasterEnd); + ASSERT_EQ(recorder->GetFrameNumber(), cloned->GetFrameNumber()); + ASSERT_EQ(recorder->GetVsyncStartTime(), cloned->GetVsyncStartTime()); + ASSERT_EQ(recorder->GetVsyncTargetTime(), cloned->GetVsyncTargetTime()); + ASSERT_EQ(recorder->GetBuildStartTime(), cloned->GetBuildStartTime()); + ASSERT_EQ(recorder->GetBuildEndTime(), cloned->GetBuildEndTime()); + ASSERT_EQ(recorder->GetRasterStartTime(), cloned->GetRasterStartTime()); + ASSERT_EQ(recorder->GetRasterEndTime(), cloned->GetRasterEndTime()); + ASSERT_EQ(recorder->GetRasterEndWallTime(), cloned->GetRasterEndWallTime()); +} + +TEST(FrameTimingsRecorderTest, FrameNumberTraceArgIsValid) { + auto recorder = std::make_unique(); + + char buff[50]; + sprintf(buff, "%d", static_cast(recorder->GetFrameNumber())); + std::string actual_arg = buff; + std::string expected_arg = recorder->GetFrameNumberTraceArg(); + + ASSERT_EQ(actual_arg, expected_arg); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/gl_context_switch_unittests.cc b/flow/gl_context_switch_unittests.cc index 916297912ba85..f31de0398cbda 100644 --- a/flow/gl_context_switch_unittests.cc +++ b/flow/gl_context_switch_unittests.cc @@ -1,9 +1,10 @@ // 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. + #define FML_USED_ON_EMBEDDER -#include "flutter/flow/gl_context_switch.h" +#include "flutter/common/graphics/gl_context_switch.h" #include #include diff --git a/flow/instrumentation.cc b/flow/instrumentation.cc index 541b8635b6aa7..49a05a9aa42e5 100644 --- a/flow/instrumentation.cc +++ b/flow/instrumentation.cc @@ -225,8 +225,7 @@ void Stopwatch::Visualize(SkCanvas* canvas, const SkRect& rect) const { prev_drawn_sample_index_ = current_sample_; // Draw the cached surface onto the output canvas. - paint.reset(); - visualize_cache_surface_->draw(canvas, rect.x(), rect.y(), &paint); + visualize_cache_surface_->draw(canvas, rect.x(), rect.y()); } CounterValues::CounterValues() : current_sample_(kMaxSamples - 1) { diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index ce86db3deaad7..8d28f9ee9c8bb 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -6,23 +6,63 @@ namespace flutter { -BackdropFilterLayer::BackdropFilterLayer(sk_sp filter) - : filter_(std::move(filter)) {} +BackdropFilterLayer::BackdropFilterLayer(sk_sp filter, + SkBlendMode blend_mode) + : filter_(std::move(filter)), blend_mode_(blend_mode) {} + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void BackdropFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (filter_ != prev->filter_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + + // Backdrop filter paints everywhere in cull rect + auto paint_bounds = context->GetCullRect(); + context->AddLayerBounds(paint_bounds); + + // convert paint bounds and filter to screen coordinates + context->GetTransform().mapRect(&paint_bounds); + auto input_filter_bounds = paint_bounds.roundOut(); + auto filter = filter_->makeWithLocalMatrix(context->GetTransform()); + + auto filter_bounds = // in screen coordinates + filter->filterBounds(input_filter_bounds, SkMatrix::I(), + SkImageFilter::kReverse_MapDirection); + + context->AddReadbackRegion(filter_bounds); + + DiffChildren(context, prev); + + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT void BackdropFilterLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_)); - ContainerLayer::Preroll(context, matrix); + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); + child_paint_bounds.join(context->cull_rect); + set_paint_bounds(child_paint_bounds); } void BackdropFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); + SkPaint paint; + paint.setBlendMode(blend_mode_); Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( context, - SkCanvas::SaveLayerRec{&paint_bounds(), nullptr, filter_.get(), 0}); + SkCanvas::SaveLayerRec{&paint_bounds(), &paint, filter_.get(), 0}); PaintChildren(context); } diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index e1fd667d712e9..c4f1b33b44384 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -12,7 +12,13 @@ namespace flutter { class BackdropFilterLayer : public ContainerLayer { public: - BackdropFilterLayer(sk_sp filter); + BackdropFilterLayer(sk_sp filter, SkBlendMode blend_mode); + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT void Preroll(PrerollContext* context, const SkMatrix& matrix) override; @@ -20,6 +26,7 @@ class BackdropFilterLayer : public ContainerLayer { private: sk_sp filter_; + SkBlendMode blend_mode_; FML_DISALLOW_COPY_AND_ASSIGN(BackdropFilterLayer); }; diff --git a/flow/layers/backdrop_filter_layer_unittests.cc b/flow/layers/backdrop_filter_layer_unittests.cc index 47f7b66e35771..b01137c4b5410 100644 --- a/flow/layers/backdrop_filter_layer_unittests.cc +++ b/flow/layers/backdrop_filter_layer_unittests.cc @@ -3,7 +3,11 @@ // found in the LICENSE file. #include "flutter/flow/layers/backdrop_filter_layer.h" +#include "flutter/flow/layers/clip_rect_layer.h" +#include "flutter/flow/layers/clip_rect_layer.h" +#include "flutter/flow/layers/transform_layer.h" +#include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" @@ -18,27 +22,30 @@ using BackdropFilterLayerTest = LayerTest; #ifndef NDEBUG TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) { - auto layer = std::make_shared(sk_sp()); + auto layer = std::make_shared(sk_sp(), + SkBlendMode::kSrcOver); + auto parent = std::make_shared(kEmptyRect, Clip::hardEdge); + parent->Add(layer); - layer->Preroll(preroll_context(), SkMatrix()); + parent->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(BackdropFilterLayerTest, PaintBeforePrerollDies) { const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); const SkPath child_path = SkPath().addRect(child_bounds); auto mock_layer = std::make_shared(child_path); - auto layer = std::make_shared(sk_sp()); + auto layer = std::make_shared(sk_sp(), + SkBlendMode::kSrcOver); layer->Add(mock_layer); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -48,12 +55,15 @@ TEST_F(BackdropFilterLayerTest, EmptyFilter) { const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(nullptr); + auto layer = + std::make_shared(nullptr, SkBlendMode::kSrcOver); layer->Add(mock_layer); + auto parent = std::make_shared(child_bounds, Clip::hardEdge); + parent->Add(layer); - layer->Preroll(preroll_context(), initial_transform); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); layer->Paint(paint_context()); @@ -74,12 +84,15 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) { const SkPaint child_paint = SkPaint(SkColors::kYellow); auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); auto mock_layer = std::make_shared(child_path, child_paint); - auto layer = std::make_shared(layer_filter); + auto layer = std::make_shared(layer_filter, + SkBlendMode::kSrcOver); layer->Add(mock_layer); + auto parent = std::make_shared(child_bounds, Clip::hardEdge); + parent->Add(layer); - layer->Preroll(preroll_context(), initial_transform); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); layer->Paint(paint_context()); @@ -93,6 +106,38 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) { MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +TEST_F(BackdropFilterLayerTest, NonSrcOverBlend) { + const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); + const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + const SkPath child_path = SkPath().addRect(child_bounds); + const SkPaint child_paint = SkPaint(SkColors::kYellow); + auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); + auto mock_layer = std::make_shared(child_path, child_paint); + auto layer = + std::make_shared(layer_filter, SkBlendMode::kSrc); + layer->Add(mock_layer); + auto parent = std::make_shared(child_bounds, Clip::hardEdge); + parent->Add(layer); + + parent->Preroll(preroll_context(), initial_transform); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); + + SkPaint filter_paint = SkPaint(); + filter_paint.setBlendMode(SkBlendMode::kSrc); + + layer->Paint(paint_context()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + layer_filter, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, child_paint}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + TEST_F(BackdropFilterLayerTest, MultipleChildren) { const SkMatrix initial_transform = SkMatrix::Translate(0.5f, 1.0f); const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 2.5f, 3.5f); @@ -101,22 +146,26 @@ TEST_F(BackdropFilterLayerTest, MultipleChildren) { SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); const SkPaint child_paint1 = SkPaint(SkColors::kYellow); const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer = std::make_shared(layer_filter); + auto layer = std::make_shared(layer_filter, + SkBlendMode::kSrcOver); layer->Add(mock_layer1); layer->Add(mock_layer2); + auto parent = + std::make_shared(children_bounds, Clip::hardEdge); + parent->Add(layer); - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer->Preroll(preroll_context(), initial_transform); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -141,45 +190,50 @@ TEST_F(BackdropFilterLayerTest, Nested) { SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); const SkPaint child_paint1 = SkPaint(SkColors::kYellow); const SkPaint child_paint2 = SkPaint(SkColors::kCyan); + SkRect children_bounds = child_path1.getBounds(); + children_bounds.join(child_path2.getBounds()); auto layer_filter1 = SkImageFilters::Paint(SkPaint(SkColors::kMagenta)); auto layer_filter2 = SkImageFilters::Paint(SkPaint(SkColors::kDkGray)); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); - auto layer1 = std::make_shared(layer_filter1); - auto layer2 = std::make_shared(layer_filter2); + auto layer1 = std::make_shared(layer_filter1, + SkBlendMode::kSrcOver); + auto layer2 = std::make_shared(layer_filter2, + SkBlendMode::kSrcOver); layer2->Add(mock_layer2); layer1->Add(mock_layer1); layer1->Add(layer2); + auto parent = + std::make_shared(children_bounds, Clip::hardEdge); + parent->Add(layer1); - SkRect children_bounds = child_path1.getBounds(); - children_bounds.join(child_path2.getBounds()); - layer1->Preroll(preroll_context(), initial_transform); + parent->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_bounds); - EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); + EXPECT_EQ(layer2->paint_bounds(), children_bounds); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); layer1->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{ - 0, MockCanvas::SaveLayerData{children_bounds, SkPaint(), - layer_filter1, 1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_path2.getBounds(), - SkPaint(), layer_filter2, 2}}, - 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, SkPaint(), + layer_filter1, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::SaveLayerData{children_bounds, SkPaint(), + layer_filter2, 2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(BackdropFilterLayerTest, Readback) { @@ -188,13 +242,15 @@ TEST_F(BackdropFilterLayerTest, Readback) { auto initial_transform = SkMatrix(); // BDF with filter always reads from surface - auto layer1 = std::make_shared(layer_filter); + auto layer1 = std::make_shared(layer_filter, + SkBlendMode::kSrcOver); preroll_context()->surface_needs_readback = false; layer1->Preroll(preroll_context(), initial_transform); EXPECT_TRUE(preroll_context()->surface_needs_readback); // BDF with no filter does not read from surface itself - auto layer2 = std::make_shared(no_filter); + auto layer2 = + std::make_shared(no_filter, SkBlendMode::kSrcOver); preroll_context()->surface_needs_readback = false; layer2->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); @@ -206,12 +262,74 @@ TEST_F(BackdropFilterLayerTest, Readback) { // BDF with no filter blocks child with readback auto mock_layer = - std::make_shared(SkPath(), SkPaint(), false, false, true); + std::make_shared(SkPath(), SkPaint(), false, true); layer2->Add(mock_layer); preroll_context()->surface_needs_readback = false; layer2->Preroll(preroll_context(), initial_transform); EXPECT_FALSE(preroll_context()->surface_needs_readback); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using BackdropLayerDiffTest = DiffContextTest; + +TEST_F(BackdropLayerDiffTest, BackdropLayer) { + auto filter = SkImageFilters::Blur(10, 10, SkTileMode::kClamp, nullptr); + + { + // tests later assume 30px readback area, fail early if that's not the case + auto readback = filter->filterBounds(SkIRect::MakeWH(10, 10), SkMatrix::I(), + SkImageFilter::kReverse_MapDirection); + EXPECT_EQ(readback, SkIRect::MakeLTRB(-30, -30, 40, 40)); + } + + MockLayerTree l1(SkISize::Make(100, 100)); + l1.root()->Add( + std::make_shared(filter, SkBlendMode::kSrcOver)); + + // no clip, effect over entire surface + auto damage = DiffLayerTree(l1, MockLayerTree(SkISize::Make(100, 100))); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeWH(100, 100)); + + MockLayerTree l2(SkISize::Make(100, 100)); + + auto clip = std::make_shared(SkRect::MakeLTRB(20, 20, 60, 60), + Clip::hardEdge); + clip->Add( + std::make_shared(filter, SkBlendMode::kSrcOver)); + l2.root()->Add(clip); + damage = DiffLayerTree(l2, MockLayerTree(SkISize::Make(100, 100))); + + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 90, 90)); + + MockLayerTree l3; + auto scale = std::make_shared(SkMatrix::Scale(2.0, 2.0)); + scale->Add(clip); + l3.root()->Add(scale); + + damage = DiffLayerTree(l3, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 180, 180)); + + MockLayerTree l4; + l4.root()->Add(scale); + + // path just outside of readback region, doesn't affect blur + auto path1 = SkPath().addRect(SkRect::MakeLTRB(180, 180, 190, 190)); + l4.root()->Add(std::make_shared(path1)); + damage = DiffLayerTree(l4, l3); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(180, 180, 190, 190)); + + MockLayerTree l5; + l5.root()->Add(scale); + + // path just inside of readback region, must trigger backdrop repaint + auto path2 = SkPath().addRect(SkRect::MakeLTRB(179, 179, 189, 189)); + l5.root()->Add(std::make_shared(path2)); + damage = DiffLayerTree(l5, l4); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 190, 190)); +} + +#endif + } // namespace testing } // namespace flutter diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index 337094ae983dd..24b7db407b98b 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -19,7 +19,7 @@ using CheckerBoardLayerTest = LayerTest; #ifndef NDEBUG TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); @@ -43,8 +43,8 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), @@ -69,7 +69,7 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerNotCheckBoard) { } TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { - const SkMatrix initial_matrix = SkMatrix::MakeTrans(0.5f, 1.0f); + const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect cull_bounds = SkRect::MakeXYWH(0.0, 0.0, 2.0, 4.0); const SkRect child_bounds = SkRect::MakeXYWH(2.5, 5.0, 4.5, 4.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); @@ -93,8 +93,8 @@ TEST_F(CheckerBoardLayerTest, ClipRectSaveLayerCheckBoard) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), @@ -136,8 +136,8 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerNotCheckBoard) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); @@ -177,8 +177,8 @@ TEST_F(CheckerBoardLayerTest, ClipPathSaveLayerCheckBoard) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); @@ -218,8 +218,8 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerNotCheckBoard) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); @@ -259,8 +259,8 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); @@ -294,15 +294,11 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not do any painting there. EXPECT_EQ(layer->paint_bounds(), - PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), - initial_elevation, 1.0f)); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + PhysicalShapeLayer::ComputeShadowBounds( + layer_path, initial_elevation, 1.0f, SkMatrix())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(layer->elevation(), initial_elevation); - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(LEGACY_FUCHSIA_EMBEDDER) const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); const SkPaint clip_paint; SkPaint layer_paint; @@ -323,7 +319,6 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { MockCanvas::DrawCall{2, MockCanvas::DrawPaint{layer_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -#endif } TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { @@ -338,15 +333,11 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not do any painting there. EXPECT_EQ(layer->paint_bounds(), - PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), - initial_elevation, 1.0f)); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + PhysicalShapeLayer::ComputeShadowBounds( + layer_path, initial_elevation, 1.0f, SkMatrix())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(layer->elevation(), initial_elevation); - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(LEGACY_FUCHSIA_EMBEDDER) const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); const SkPaint clip_paint; SkPaint layer_paint; @@ -367,7 +358,6 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { MockCanvas::DrawCall{2, MockCanvas::DrawPaint{layer_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); -#endif } #endif diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc deleted file mode 100644 index 9ba5eff3c5021..0000000000000 --- a/flow/layers/child_scene_layer.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/layers/child_scene_layer.h" - -namespace flutter { - -ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, - const SkPoint& offset, - const SkSize& size, - bool hit_testable) - : layer_id_(layer_id), - offset_(offset), - size_(size), - hit_testable_(hit_testable) {} - -void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll"); - - context->child_scene_layer_exists_below = true; - CheckForChildLayerBelow(context); -} - -void ChildSceneLayer::Paint(PaintContext& context) const { - FML_NOTREACHED(); -} - -void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "ChildSceneLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); - context.UpdateView(layer_id_, offset_, size_, hit_testable_); -} - -} // namespace flutter diff --git a/flow/layers/child_scene_layer.h b/flow/layers/child_scene_layer.h deleted file mode 100644 index cad47db00462a..0000000000000 --- a/flow/layers/child_scene_layer.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FLOW_LAYERS_CHILD_SCENE_LAYER_H_ -#define FLUTTER_FLOW_LAYERS_CHILD_SCENE_LAYER_H_ - -#include "flutter/flow/layers/layer.h" -#include "flutter/flow/scene_update_context.h" -#include "third_party/skia/include/core/SkMatrix.h" -#include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkSize.h" - -namespace flutter { - -// Layer that represents an embedded child. -class ChildSceneLayer : public Layer { - public: - ChildSceneLayer(zx_koid_t layer_id, - const SkPoint& offset, - const SkSize& size, - bool hit_testable); - ~ChildSceneLayer() override = default; - - void Preroll(PrerollContext* context, const SkMatrix& matrix) override; - - void Paint(PaintContext& context) const override; - - void UpdateScene(SceneUpdateContext& context) override; - - private: - zx_koid_t layer_id_ = ZX_KOID_INVALID; - SkPoint offset_; - SkSize size_; - bool hit_testable_ = true; - - FML_DISALLOW_COPY_AND_ASSIGN(ChildSceneLayer); -}; - -} // namespace flutter - -#endif // FLUTTER_FLOW_LAYERS_CHILD_SCENE_LAYER_H_ diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 038ead12a6805..c895f6611be1e 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -5,10 +5,6 @@ #include "flutter/flow/layers/clip_path_layer.h" #include "flutter/flow/paint_utils.h" -#if defined(LEGACY_FUCHSIA_EMBEDDER) -#include "lib/ui/scenic/cpp/commands.h" -#endif - namespace flutter { ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) @@ -16,50 +12,51 @@ ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) FML_DCHECK(clip_behavior != Clip::none); } -void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - TRACE_EVENT0("flutter", "ClipPathLayer::Preroll"); - - SkRect previous_cull_rect = context->cull_rect; - SkRect clip_path_bounds = clip_path_.getBounds(); - children_inside_clip_ = context->cull_rect.intersect(clip_path_bounds); - if (children_inside_clip_) { - TRACE_EVENT_INSTANT0("flutter", "children inside clip rect"); - - Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); - context->mutators_stack.PushClipPath(clip_path_); - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT - if (child_paint_bounds.intersect(clip_path_bounds)) { - set_paint_bounds(child_paint_bounds); +void ClipPathLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (clip_behavior_ != prev->clip_behavior_ || + clip_path_ != prev->clip_path_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); } - context->mutators_stack.Pop(); } - context->cull_rect = previous_cull_rect; + if (context->PushCullRect(clip_path_.getBounds())) { + DiffChildren(context, prev); + } + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -#if defined(LEGACY_FUCHSIA_EMBEDDER) +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "ClipPathLayer::Preroll"); -void ClipPathLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "ClipPathLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); + SkRect previous_cull_rect = context->cull_rect; + SkRect clip_path_bounds = clip_path_.getBounds(); + if (!context->cull_rect.intersect(clip_path_bounds)) { + context->cull_rect.setEmpty(); + } + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); + context->mutators_stack.PushClipPath(clip_path_); + + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); + if (child_paint_bounds.intersect(clip_path_bounds)) { + set_paint_bounds(child_paint_bounds); + } - // TODO(liyuqian): respect clip_behavior_ - SceneUpdateContext::Clip clip(context, clip_path_.getBounds()); - UpdateSceneChildren(context); + context->mutators_stack.Pop(); + context->cull_rect = previous_cull_rect; } -#endif - void ClipPathLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipPathLayer::Paint"); - FML_DCHECK(needs_painting()); - - if (!children_inside_clip_) { - TRACE_EVENT_INSTANT0("flutter", "children not inside clip rect, skipping"); - return; - } + FML_DCHECK(needs_painting(context)); SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->clipPath(clip_path_, diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index ce2e4e54d66e3..bed8c9609f2d5 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -13,6 +13,12 @@ class ClipPathLayer : public ContainerLayer { public: ClipPathLayer(const SkPath& clip_path, Clip clip_behavior = Clip::antiAlias); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; @@ -21,14 +27,9 @@ class ClipPathLayer : public ContainerLayer { return clip_behavior_ == Clip::antiAliasWithSaveLayer; } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context) override; -#endif - private: SkPath clip_path_; Clip clip_behavior_; - bool children_inside_clip_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ClipPathLayer); }; diff --git a/flow/layers/clip_path_layer_unittests.cc b/flow/layers/clip_path_layer_unittests.cc index 6491fe5b40db7..3cbbf0f055476 100644 --- a/flow/layers/clip_path_layer_unittests.cc +++ b/flow/layers/clip_path_layer_unittests.cc @@ -28,10 +28,10 @@ TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ClipPathLayerTest, PaintBeforePrerollDies) { @@ -39,37 +39,41 @@ TEST_F(ClipPathLayerTest, PaintBeforePrerollDies) { const SkPath layer_path = SkPath().addRect(layer_bounds); auto layer = std::make_shared(layer_path, Clip::hardEdge); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect distant_bounds = SkRect::MakeXYWH(100.0, 100.0, 10.0, 10.0); const SkPath child_path = SkPath().addRect(child_bounds); const SkPath layer_path = SkPath().addRect(layer_bounds); auto mock_layer = std::make_shared(child_path); auto layer = std::make_shared(layer_path, Clip::hardEdge); layer->Add(mock_layer); - preroll_context()->cull_rect = kEmptyRect; // Cull everything + preroll_context()->cull_rect = distant_bounds; // Cull these children layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_EQ(preroll_context()->cull_rect, distant_bounds); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); + paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); + EXPECT_FALSE(mock_layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -96,8 +100,8 @@ TEST_F(ClipPathLayerTest, ChildOutsideBounds) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); @@ -131,8 +135,8 @@ TEST_F(ClipPathLayerTest, FullyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); @@ -173,8 +177,8 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_path)})); @@ -218,7 +222,7 @@ TEST_F(ClipPathLayerTest, Readback) { const Clip save_layer = Clip::antiAliasWithSaveLayer; std::shared_ptr nochild; - auto reader = std::make_shared(path, paint, false, false, true); + auto reader = std::make_shared(path, paint, false, true); auto nonreader = std::make_shared(path, paint); // No children, no prior readback -> no readback after diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index fbd36dab65d05..3c885a472ce97 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -12,49 +12,50 @@ ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) FML_DCHECK(clip_behavior != Clip::none); } -void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - TRACE_EVENT0("flutter", "ClipRectLayer::Preroll"); - - SkRect previous_cull_rect = context->cull_rect; - children_inside_clip_ = context->cull_rect.intersect(clip_rect_); - if (children_inside_clip_) { - TRACE_EVENT_INSTANT0("flutter", "children inside clip rect"); - - Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); - context->mutators_stack.PushClipRect(clip_rect_); - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); - - if (child_paint_bounds.intersect(clip_rect_)) { - set_paint_bounds(child_paint_bounds); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void ClipRectLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (clip_behavior_ != prev->clip_behavior_ || + clip_rect_ != prev->clip_rect_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); } - context->mutators_stack.Pop(); } - context->cull_rect = previous_cull_rect; + if (context->PushCullRect(clip_rect_)) { + DiffChildren(context, prev); + } + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); } -#if defined(LEGACY_FUCHSIA_EMBEDDER) +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "ClipRectLayer::Preroll"); -void ClipRectLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "ClipRectLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); + SkRect previous_cull_rect = context->cull_rect; + if (!context->cull_rect.intersect(clip_rect_)) { + context->cull_rect.setEmpty(); + } + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); + context->mutators_stack.PushClipRect(clip_rect_); + + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); + if (child_paint_bounds.intersect(clip_rect_)) { + set_paint_bounds(child_paint_bounds); + } - // TODO(liyuqian): respect clip_behavior_ - SceneUpdateContext::Clip clip(context, clip_rect_); - UpdateSceneChildren(context); + context->mutators_stack.Pop(); + context->cull_rect = previous_cull_rect; } -#endif - void ClipRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRectLayer::Paint"); - FML_DCHECK(needs_painting()); - - if (!children_inside_clip_) { - TRACE_EVENT_INSTANT0("flutter", "children not inside clip rect, skipping"); - return; - } + FML_DCHECK(needs_painting(context)); SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->clipRect(clip_rect_, diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index b52a9512262d3..e37227fbe21b3 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -13,6 +13,12 @@ class ClipRectLayer : public ContainerLayer { public: ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; @@ -20,14 +26,9 @@ class ClipRectLayer : public ContainerLayer { return clip_behavior_ == Clip::antiAliasWithSaveLayer; } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context) override; -#endif - private: SkRect clip_rect_; Clip clip_behavior_; - bool children_inside_clip_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ClipRectLayer); }; diff --git a/flow/layers/clip_rect_layer_unittests.cc b/flow/layers/clip_rect_layer_unittests.cc index 70decfa154e34..13c90d76e8f4e 100644 --- a/flow/layers/clip_rect_layer_unittests.cc +++ b/flow/layers/clip_rect_layer_unittests.cc @@ -28,46 +28,51 @@ TEST_F(ClipRectLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ClipRectLayerTest, PaintBeforePrerollDies) { const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); auto layer = std::make_shared(layer_bounds, Clip::hardEdge); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect distant_bounds = SkRect::MakeXYWH(100.0, 100.0, 10.0, 10.0); const SkPath child_path = SkPath().addRect(child_bounds); auto mock_layer = std::make_shared(child_path); auto layer = std::make_shared(layer_bounds, Clip::hardEdge); layer->Add(mock_layer); - preroll_context()->cull_rect = kEmptyRect; // Cull everything + preroll_context()->cull_rect = distant_bounds; // Cull these children layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_EQ(preroll_context()->cull_rect, distant_bounds); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), + std::vector({Mutator(layer_bounds)})); + paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); + EXPECT_FALSE(mock_layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -93,8 +98,8 @@ TEST_F(ClipRectLayerTest, ChildOutsideBounds) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), @@ -128,8 +133,8 @@ TEST_F(ClipRectLayerTest, FullyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), @@ -170,8 +175,8 @@ TEST_F(ClipRectLayerTest, PartiallyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), @@ -215,7 +220,7 @@ TEST_F(ClipRectLayerTest, Readback) { const Clip save_layer = Clip::antiAliasWithSaveLayer; std::shared_ptr nochild; - auto reader = std::make_shared(path, paint, false, false, true); + auto reader = std::make_shared(path, paint, false, true); auto nonreader = std::make_shared(path, paint); // No children, no prior readback -> no readback after diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index b3ff833c4a4f3..225c110abda04 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -12,50 +12,51 @@ ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) FML_DCHECK(clip_behavior != Clip::none); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void ClipRRectLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (clip_behavior_ != prev->clip_behavior_ || + clip_rrect_ != prev->clip_rrect_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + if (context->PushCullRect(clip_rrect_.getBounds())) { + DiffChildren(context, prev); + } + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ClipRRectLayer::Preroll"); SkRect previous_cull_rect = context->cull_rect; SkRect clip_rrect_bounds = clip_rrect_.getBounds(); - children_inside_clip_ = context->cull_rect.intersect(clip_rrect_bounds); - if (children_inside_clip_) { - TRACE_EVENT_INSTANT0("flutter", "children inside clip rect"); - - Layer::AutoPrerollSaveLayerState save = - Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); - context->mutators_stack.PushClipRRect(clip_rrect_); - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); - - if (child_paint_bounds.intersect(clip_rrect_bounds)) { - set_paint_bounds(child_paint_bounds); - } - context->mutators_stack.Pop(); + if (!context->cull_rect.intersect(clip_rrect_bounds)) { + context->cull_rect.setEmpty(); + } + Layer::AutoPrerollSaveLayerState save = + Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); + context->mutators_stack.PushClipRRect(clip_rrect_); + + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); + if (child_paint_bounds.intersect(clip_rrect_bounds)) { + set_paint_bounds(child_paint_bounds); } - context->cull_rect = previous_cull_rect; -} - -#if defined(LEGACY_FUCHSIA_EMBEDDER) - -void ClipRRectLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "ClipRRectLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); - // TODO(liyuqian): respect clip_behavior_ - SceneUpdateContext::Clip clip(context, clip_rrect_.getBounds()); - UpdateSceneChildren(context); + context->mutators_stack.Pop(); + context->cull_rect = previous_cull_rect; } -#endif - void ClipRRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRRectLayer::Paint"); - FML_DCHECK(needs_painting()); - - if (!children_inside_clip_) { - TRACE_EVENT_INSTANT0("flutter", "children not inside clip rect, skipping"); - return; - } + FML_DCHECK(needs_painting(context)); SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->clipRRect(clip_rrect_, diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index 314886a1fed30..85626b0d2a9c3 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -13,6 +13,12 @@ class ClipRRectLayer : public ContainerLayer { public: ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; @@ -21,14 +27,9 @@ class ClipRRectLayer : public ContainerLayer { return clip_behavior_ == Clip::antiAliasWithSaveLayer; } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context) override; -#endif - private: SkRRect clip_rrect_; Clip clip_behavior_; - bool children_inside_clip_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer); }; diff --git a/flow/layers/clip_rrect_layer_unittests.cc b/flow/layers/clip_rrect_layer_unittests.cc index 39d40b72221ee..b2adb2253747c 100644 --- a/flow/layers/clip_rrect_layer_unittests.cc +++ b/flow/layers/clip_rrect_layer_unittests.cc @@ -30,27 +30,28 @@ TEST_F(ClipRRectLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -TEST_F(ClipRRectLayerTest, PaintBeforePreollDies) { +TEST_F(ClipRRectLayerTest, PaintBeforePrerollDies) { const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); auto layer = std::make_shared(layer_rrect, Clip::hardEdge); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { const SkMatrix initial_matrix = SkMatrix::Translate(0.5f, 1.0f); const SkRect child_bounds = SkRect::MakeXYWH(1.0, 2.0, 2.0, 2.0); const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0); + const SkRect distant_bounds = SkRect::MakeXYWH(100.0, 100.0, 10.0, 10.0); const SkPath child_path = SkPath().addRect(child_bounds); const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); @@ -58,21 +59,24 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) { auto layer = std::make_shared(layer_rrect, Clip::hardEdge); layer->Add(mock_layer); - preroll_context()->cull_rect = kEmptyRect; // Cull everything + preroll_context()->cull_rect = distant_bounds; // Cull these children layer->Preroll(preroll_context(), initial_matrix); - EXPECT_EQ(preroll_context()->cull_rect, kEmptyRect); // Untouched + EXPECT_EQ(preroll_context()->cull_rect, distant_bounds); // Untouched EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched - EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); + EXPECT_EQ(layer->paint_bounds(), child_bounds); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect); - EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); - EXPECT_EQ(mock_layer->parent_mutators(), std::vector()); + EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); + EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); + paint_context().internal_nodes_canvas->clipRect(distant_bounds, false); + EXPECT_FALSE(mock_layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -99,8 +103,8 @@ TEST_F(ClipRRectLayerTest, ChildOutsideBounds) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); @@ -134,8 +138,8 @@ TEST_F(ClipRRectLayerTest, FullyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); @@ -176,8 +180,8 @@ TEST_F(ClipRRectLayerTest, PartiallyContainedChild) { EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds); EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)})); @@ -221,7 +225,7 @@ TEST_F(ClipRRectLayerTest, Readback) { const Clip save_layer = Clip::antiAliasWithSaveLayer; std::shared_ptr nochild; - auto reader = std::make_shared(path, paint, false, false, true); + auto reader = std::make_shared(path, paint, false, true); auto nonreader = std::make_shared(path, paint); // No children, no prior readback -> no readback after diff --git a/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index b212720da581d..7d662f07f4ab2 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -9,6 +9,25 @@ namespace flutter { ColorFilterLayer::ColorFilterLayer(sk_sp filter) : filter_(std::move(filter)) {} +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void ColorFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (filter_ != prev->filter_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + + DiffChildren(context, prev); + + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void ColorFilterLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = @@ -18,7 +37,7 @@ void ColorFilterLayer::Preroll(PrerollContext* context, void ColorFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ColorFilterLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); SkPaint paint; paint.setColorFilter(filter_); diff --git a/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index cd3c584b4405b..b1fb32acfbcad 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -14,6 +14,12 @@ class ColorFilterLayer : public ContainerLayer { public: ColorFilterLayer(sk_sp filter); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/color_filter_layer_unittests.cc b/flow/layers/color_filter_layer_unittests.cc index 5c16f8c0cb1f0..a48ca56409619 100644 --- a/flow/layers/color_filter_layer_unittests.cc +++ b/flow/layers/color_filter_layer_unittests.cc @@ -22,11 +22,10 @@ TEST_F(ColorFilterLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ColorFilterLayerTest, PaintBeforePrerollDies) { @@ -38,7 +37,7 @@ TEST_F(ColorFilterLayerTest, PaintBeforePrerollDies) { EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -53,7 +52,7 @@ TEST_F(ColorFilterLayerTest, EmptyFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -82,7 +81,7 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -120,9 +119,9 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -168,10 +167,10 @@ TEST_F(ColorFilterLayerTest, Nested) { EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_bounds); EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -207,7 +206,7 @@ TEST_F(ColorFilterLayerTest, Readback) { // ColorFilterLayer blocks child with readback auto mock_layer = - std::make_shared(SkPath(), SkPaint(), false, false, true); + std::make_shared(SkPath(), SkPaint(), false, true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; layer->Preroll(preroll_context(), initial_transform); diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 825826b70835f..2a62359ba4dc8 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -10,6 +10,102 @@ namespace flutter { ContainerLayer::ContainerLayer() {} +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void ContainerLayer::Diff(DiffContext* context, const Layer* old_layer) { + auto old_container = static_cast(old_layer); + DiffContext::AutoSubtreeRestore subtree(context); + DiffChildren(context, old_container); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +void ContainerLayer::PreservePaintRegion(DiffContext* context) { + Layer::PreservePaintRegion(context); + for (auto& layer : layers_) { + layer->PreservePaintRegion(context); + } +} + +void ContainerLayer::DiffChildren(DiffContext* context, + const ContainerLayer* old_layer) { + if (context->IsSubtreeDirty()) { + for (auto& layer : layers_) { + layer->Diff(context, nullptr); + } + return; + } + FML_DCHECK(old_layer); + + const auto& prev_layers = old_layer->layers_; + + // first mismatched element + int new_children_top = 0; + int old_children_top = 0; + + // last mismatched element + int new_children_bottom = layers_.size() - 1; + int old_children_bottom = prev_layers.size() - 1; + + while ((old_children_top <= old_children_bottom) && + (new_children_top <= new_children_bottom)) { + if (!layers_[new_children_top]->IsReplacing( + context, prev_layers[old_children_top].get())) { + break; + } + ++new_children_top; + ++old_children_top; + } + + while ((old_children_top <= old_children_bottom) && + (new_children_top <= new_children_bottom)) { + if (!layers_[new_children_bottom]->IsReplacing( + context, prev_layers[old_children_bottom].get())) { + break; + } + --new_children_bottom; + --old_children_bottom; + } + + // old layers that don't match + for (int i = old_children_top; i <= old_children_bottom; ++i) { + auto layer = prev_layers[i]; + context->AddDamage(context->GetOldLayerPaintRegion(layer.get())); + } + + for (int i = 0; i < static_cast(layers_.size()); ++i) { + if (i < new_children_top || i > new_children_bottom) { + int i_prev = + i < new_children_top ? i : prev_layers.size() - (layers_.size() - i); + auto layer = layers_[i]; + auto prev_layer = prev_layers[i_prev]; + auto paint_region = context->GetOldLayerPaintRegion(prev_layer.get()); + if (layer == prev_layer && !paint_region.has_readback()) { + // for retained layers, stop processing the subtree and add existing + // region; We know current subtree is not dirty (every ancestor up to + // here matches) so the retained subtree will render identically to + // previous frame; We can only do this if there is no readback in the + // subtree. Layers that do readback must be able to register readback + // inside Diff + context->AddExistingPaintRegion(paint_region); + + // While we don't need to diff retained layers, we still need to + // associate their paint region with current layer tree so that we can + // retrieve it in next frame diff + layer->PreservePaintRegion(context); + } else { + layer->Diff(context, prev_layer.get()); + } + } else { + DiffContext::AutoSubtreeRestore subtree(context); + context->MarkSubtreeDirty(); + auto layer = layers_[i]; + layer->Diff(context, nullptr); + } + } +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void ContainerLayer::Add(std::shared_ptr layer) { layers_.emplace_back(std::move(layer)); } @@ -23,7 +119,7 @@ void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { } void ContainerLayer::Paint(PaintContext& context) const { - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); PaintChildren(context); } @@ -31,18 +127,11 @@ void ContainerLayer::Paint(PaintContext& context) const { void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { -#if defined(LEGACY_FUCHSIA_EMBEDDER) - // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), - // Layers that appear above the embedded content will be turned into their own - // Scenic layers. - child_layer_exists_below_ = context->child_scene_layer_exists_below; - context->child_scene_layer_exists_below = false; -#endif - // Platform views have no children, so context->has_platform_view should // always be false. FML_DCHECK(!context->has_platform_view); bool child_has_platform_view = false; + bool child_has_texture_layer = false; for (auto& layer : layers_) { // Reset context->has_platform_view to false so that layers aren't treated // as if they have a platform view based on one being previously found in a @@ -50,34 +139,30 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, context->has_platform_view = false; layer->Preroll(context, child_matrix); - - if (layer->needs_system_composite()) { - set_needs_system_composite(true); - } child_paint_bounds->join(layer->paint_bounds()); child_has_platform_view = child_has_platform_view || context->has_platform_view; + child_has_texture_layer = + child_has_texture_layer || context->has_texture_layer; } context->has_platform_view = child_has_platform_view; - -#if defined(LEGACY_FUCHSIA_EMBEDDER) - if (child_layer_exists_below_) { - set_needs_system_composite(true); - } - context->child_scene_layer_exists_below = - context->child_scene_layer_exists_below || child_layer_exists_below_; -#endif + context->has_texture_layer = child_has_texture_layer; + set_subtree_has_platform_view(child_has_platform_view); } void ContainerLayer::PaintChildren(PaintContext& context) const { - FML_DCHECK(needs_painting()); + // We can no longer call FML_DCHECK here on the needs_painting(context) + // condition as that test is only valid for the PaintContext that + // is initially handed to a layer's Paint() method. By the time the + // layer calls PaintChildren(), though, it may have modified the + // PaintContext so the test doesn't work in this "context". // Intentionally not tracing here as there should be no self-time // and the trace event on this common function has a small overhead. for (auto& layer : layers_) { - if (layer->needs_painting()) { + if (layer->needs_painting(context)) { layer->Paint(context); } } @@ -86,42 +171,13 @@ void ContainerLayer::PaintChildren(PaintContext& context) const { void ContainerLayer::TryToPrepareRasterCache(PrerollContext* context, Layer* layer, const SkMatrix& matrix) { - if (!context->has_platform_view && context->raster_cache && + if (!context->has_platform_view && !context->has_texture_layer && + context->raster_cache && SkRect::Intersects(context->cull_rect, layer->paint_bounds())) { context->raster_cache->Prepare(context, layer, matrix); } } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - -void ContainerLayer::CheckForChildLayerBelow(PrerollContext* context) { - // All ContainerLayers make the check in PrerollChildren. -} - -void ContainerLayer::UpdateScene(SceneUpdateContext& context) { - UpdateSceneChildren(context); -} - -void ContainerLayer::UpdateSceneChildren(SceneUpdateContext& context) { - FML_DCHECK(needs_system_composite()); - - std::optional frame; - if (child_layer_exists_below_) { - frame.emplace( - context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT, - SkScalarRoundToInt(context.alphaf() * 255), "flutter::ContainerLayer"); - frame->AddPaintLayer(this); - } - - for (auto& layer : layers_) { - if (layer->needs_system_composite()) { - layer->UpdateScene(context); - } - } -} - -#endif - MergedContainerLayer::MergedContainerLayer() { // Ensure the layer has only one direct child. // @@ -135,6 +191,24 @@ MergedContainerLayer::MergedContainerLayer() { ContainerLayer::Add(std::make_shared()); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT +void MergedContainerLayer::DiffChildren(DiffContext* context, + const ContainerLayer* old_layer) { + if (context->IsSubtreeDirty()) { + GetChildContainer()->Diff(context, nullptr); + return; + } + FML_DCHECK(old_layer); + + // For MergedContainerLayer we want to diff children of ChildContainer + // instead of the ChildContainer itself. This works around the fact + // that ChildContainerLayer is ephemeral and its original_layer_id_ is always + // different. + auto layer = static_cast(old_layer); + GetChildContainer()->DiffChildren(context, layer->GetChildContainer()); +} +#endif + void MergedContainerLayer::Add(std::shared_ptr layer) { GetChildContainer()->Add(std::move(layer)); } diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index 0e5b27d7083fd..699cf4d61b530 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -15,27 +15,33 @@ class ContainerLayer : public Layer { public: ContainerLayer(); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + void PreservePaintRegion(DiffContext* context) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + virtual void Add(std::shared_ptr layer); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void CheckForChildLayerBelow(PrerollContext* context) override; - void UpdateScene(SceneUpdateContext& context) override; -#endif const std::vector>& layers() const { return layers_; } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + virtual void DiffChildren(DiffContext* context, + const ContainerLayer* old_layer); + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + protected: void PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds); void PaintChildren(PaintContext& context) const; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateSceneChildren(SceneUpdateContext& context); -#endif - // Try to prepare the raster cache for a given layer. // // The raster cache would fail if either of the followings is true: @@ -43,7 +49,7 @@ class ContainerLayer : public Layer { // 2. The context does not have a valid raster cache. // 3. The layer's paint bounds does not intersect with the cull rect. // - // We make this a static function instead of a member function that directy + // We make this a static function instead of a member function that directly // uses the "this" pointer as the layer because we sometimes need to raster // cache a child layer and one can't access its child's protected method. static void TryToPrepareRasterCache(PrerollContext* context, @@ -103,6 +109,11 @@ class MergedContainerLayer : public ContainerLayer { void Add(std::shared_ptr layer) override; +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + void DiffChildren(DiffContext* context, + const ContainerLayer* old_layer) override; +#endif + protected: /** * @brief Returns the ContainerLayer used to hold all of the children of the diff --git a/flow/layers/container_layer_unittests.cc b/flow/layers/container_layer_unittests.cc index 65c2ff365fa0c..aa34048bf2b89 100644 --- a/flow/layers/container_layer_unittests.cc +++ b/flow/layers/container_layer_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" @@ -28,14 +29,13 @@ TEST_F(ContainerLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -TEST_F(ContainerLayerTest, PaintBeforePreollDies) { +TEST_F(ContainerLayerTest, PaintBeforePrerollDies) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); auto mock_layer = std::make_shared(child_path); @@ -44,7 +44,7 @@ TEST_F(ContainerLayerTest, PaintBeforePreollDies) { EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -62,10 +62,8 @@ TEST_F(ContainerLayerTest, Simple) { EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); @@ -98,12 +96,9 @@ TEST_F(ContainerLayerTest, Multiple) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); @@ -137,12 +132,9 @@ TEST_F(ContainerLayerTest, MultipleWithEmpty) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_FALSE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_FALSE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); @@ -164,8 +156,7 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) { SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); auto mock_layer1 = std::make_shared( - child_path1, child_paint1, false /* fake_has_platform_view */, - true /* fake_needs_system_composite */); + child_path1, child_paint1, false /* fake_has_platform_view */); auto mock_layer2 = std::make_shared(child_path2, child_paint2); auto layer = std::make_shared(); layer->Add(mock_layer1); @@ -178,12 +169,9 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_TRUE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_TRUE(layer->needs_system_composite()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); @@ -212,10 +200,8 @@ TEST_F(ContainerLayerTest, MergedOneChild) { EXPECT_FALSE(preroll_context()->has_platform_view); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); @@ -247,12 +233,9 @@ TEST_F(ContainerLayerTest, MergedMultipleChildren) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); @@ -268,5 +251,212 @@ TEST_F(ContainerLayerTest, MergedMultipleChildren) { child_path2, child_paint2}}})); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using ContainerLayerDiffTest = DiffContextTest; + +// Insert PictureLayer amongst container layers +TEST_F(ContainerLayerDiffTest, PictureLayerInsertion) { + auto pic1 = CreatePicture(SkRect::MakeLTRB(0, 0, 50, 50), 1); + auto pic2 = CreatePicture(SkRect::MakeLTRB(100, 0, 150, 50), 1); + auto pic3 = CreatePicture(SkRect::MakeLTRB(200, 0, 250, 50), 1); + + MockLayerTree t1; + + auto t1_c1 = CreateContainerLayer(CreatePictureLayer(pic1)); + t1.root()->Add(t1_c1); + + auto t1_c2 = CreateContainerLayer(CreatePictureLayer(pic2)); + t1.root()->Add(t1_c2); + + auto damage = DiffLayerTree(t1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 150, 50)); + + // Add in the middle + + MockLayerTree t2; + auto t2_c1 = CreateContainerLayer(CreatePictureLayer(pic1)); + t2_c1->AssignOldLayer(t1_c1.get()); + t2.root()->Add(t2_c1); + + t2.root()->Add(CreatePictureLayer(pic3)); + + auto t2_c2 = CreateContainerLayer(CreatePictureLayer(pic2)); + t2_c2->AssignOldLayer(t1_c2.get()); + t2.root()->Add(t2_c2); + + damage = DiffLayerTree(t2, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); + + // Add in the beginning + + t2 = MockLayerTree(); + t2.root()->Add(CreatePictureLayer(pic3)); + t2.root()->Add(t2_c1); + t2.root()->Add(t2_c2); + damage = DiffLayerTree(t2, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); + + // Add at the end + + t2 = MockLayerTree(); + t2.root()->Add(t2_c1); + t2.root()->Add(t2_c2); + t2.root()->Add(CreatePictureLayer(pic3)); + damage = DiffLayerTree(t2, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); +} + +// Insert picture layer amongst other picture layers +TEST_F(ContainerLayerDiffTest, PictureInsertion) { + auto pic1 = CreatePicture(SkRect::MakeLTRB(0, 0, 50, 50), 1); + auto pic2 = CreatePicture(SkRect::MakeLTRB(100, 0, 150, 50), 1); + auto pic3 = CreatePicture(SkRect::MakeLTRB(200, 0, 250, 50), 1); + + MockLayerTree t1; + t1.root()->Add(CreatePictureLayer(pic1)); + t1.root()->Add(CreatePictureLayer(pic2)); + + auto damage = DiffLayerTree(t1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 150, 50)); + + MockLayerTree t2; + t2.root()->Add(CreatePictureLayer(pic3)); + t2.root()->Add(CreatePictureLayer(pic1)); + t2.root()->Add(CreatePictureLayer(pic2)); + + damage = DiffLayerTree(t2, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); + + MockLayerTree t3; + t3.root()->Add(CreatePictureLayer(pic1)); + t3.root()->Add(CreatePictureLayer(pic3)); + t3.root()->Add(CreatePictureLayer(pic2)); + + damage = DiffLayerTree(t3, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); + + MockLayerTree t4; + t4.root()->Add(CreatePictureLayer(pic1)); + t4.root()->Add(CreatePictureLayer(pic2)); + t4.root()->Add(CreatePictureLayer(pic3)); + + damage = DiffLayerTree(t4, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); +} + +TEST_F(ContainerLayerDiffTest, LayerDeletion) { + auto path1 = SkPath().addRect(SkRect::MakeLTRB(0, 0, 50, 50)); + auto path2 = SkPath().addRect(SkRect::MakeLTRB(100, 0, 150, 50)); + auto path3 = SkPath().addRect(SkRect::MakeLTRB(200, 0, 250, 50)); + + auto c1 = CreateContainerLayer(std::make_shared(path1)); + auto c2 = CreateContainerLayer(std::make_shared(path2)); + auto c3 = CreateContainerLayer(std::make_shared(path3)); + + MockLayerTree t1; + t1.root()->Add(c1); + t1.root()->Add(c2); + t1.root()->Add(c3); + + auto damage = DiffLayerTree(t1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 250, 50)); + + MockLayerTree t2; + t2.root()->Add(c2); + t2.root()->Add(c3); + + damage = DiffLayerTree(t2, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 50, 50)); + + MockLayerTree t3; + t3.root()->Add(c1); + t3.root()->Add(c3); + + damage = DiffLayerTree(t3, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(100, 0, 150, 50)); + + MockLayerTree t4; + t4.root()->Add(c1); + t4.root()->Add(c2); + + damage = DiffLayerTree(t4, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); + + MockLayerTree t5; + t5.root()->Add(c1); + + damage = DiffLayerTree(t5, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(100, 0, 250, 50)); + + MockLayerTree t6; + t6.root()->Add(c2); + + damage = DiffLayerTree(t6, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 250, 50)); + + MockLayerTree t7; + t7.root()->Add(c3); + + damage = DiffLayerTree(t7, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 150, 50)); +} + +TEST_F(ContainerLayerDiffTest, ReplaceLayer) { + auto path1 = SkPath().addRect(SkRect::MakeLTRB(0, 0, 50, 50)); + auto path2 = SkPath().addRect(SkRect::MakeLTRB(100, 0, 150, 50)); + auto path3 = SkPath().addRect(SkRect::MakeLTRB(200, 0, 250, 50)); + + auto path1a = SkPath().addRect(SkRect::MakeLTRB(0, 100, 50, 150)); + auto path2a = SkPath().addRect(SkRect::MakeLTRB(100, 100, 150, 150)); + auto path3a = SkPath().addRect(SkRect::MakeLTRB(200, 100, 250, 150)); + + auto c1 = CreateContainerLayer(std::make_shared(path1)); + auto c2 = CreateContainerLayer(std::make_shared(path2)); + auto c3 = CreateContainerLayer(std::make_shared(path3)); + + MockLayerTree t1; + t1.root()->Add(c1); + t1.root()->Add(c2); + t1.root()->Add(c3); + + auto damage = DiffLayerTree(t1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 250, 50)); + + MockLayerTree t2; + t2.root()->Add(c1); + t2.root()->Add(c2); + t2.root()->Add(c3); + + damage = DiffLayerTree(t2, t1); + EXPECT_TRUE(damage.frame_damage.isEmpty()); + + MockLayerTree t3; + t3.root()->Add(CreateContainerLayer({std::make_shared(path1a)})); + t3.root()->Add(c2); + t3.root()->Add(c3); + + damage = DiffLayerTree(t3, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 50, 150)); + + MockLayerTree t4; + t4.root()->Add(c1); + t4.root()->Add(CreateContainerLayer(std::make_shared(path2a))); + t4.root()->Add(c3); + + damage = DiffLayerTree(t4, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(100, 0, 150, 150)); + + MockLayerTree t5; + t5.root()->Add(c1); + t5.root()->Add(c2); + t5.root()->Add(CreateContainerLayer(std::make_shared(path3a))); + + damage = DiffLayerTree(t5, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 150)); +} + +#endif + } // namespace testing } // namespace flutter diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc new file mode 100644 index 0000000000000..a40cc314c82ec --- /dev/null +++ b/flow/layers/display_list_layer.cc @@ -0,0 +1,129 @@ +// 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/flow/layers/display_list_layer.h" + +#include "flutter/flow/display_list_canvas.h" + +namespace flutter { + +DisplayListLayer::DisplayListLayer(const SkPoint& offset, + sk_sp display_list, + bool is_complex, + bool will_change) + : offset_(offset), + display_list_(display_list), + is_complex_(is_complex), + will_change_(will_change) {} + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +bool DisplayListLayer::IsReplacing(DiffContext* context, + const Layer* layer) const { + // Only return true for identical display lists; This way + // ContainerLayer::DiffChildren can detect when a display list layer + // got inserted between other display list layers + auto old_layer = layer->as_display_list_layer(); + return old_layer != nullptr && offset_ == old_layer->offset_ && + Compare(context->statistics(), this, old_layer); +} + +void DisplayListLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + if (!context->IsSubtreeDirty()) { +#ifndef NDEBUG + FML_DCHECK(old_layer); + auto prev = old_layer->as_display_list_layer(); + DiffContext::Statistics dummy_statistics; + // IsReplacing has already determined that the display list is same + FML_DCHECK(prev->offset_ == offset_ && + Compare(dummy_statistics, this, prev)); +#endif + } + context->PushTransform(SkMatrix::Translate(offset_.x(), offset_.y())); + context->AddLayerBounds(display_list_->bounds()); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, + const DisplayListLayer* l1, + const DisplayListLayer* l2) { + const auto& dl1 = l1->display_list_; + const auto& dl2 = l2->display_list_; + if (dl1.get() == dl2.get()) { + statistics.AddSameInstancePicture(); + return true; + } + const auto op_cnt_1 = dl1->op_count(); + const auto op_cnt_2 = dl2->op_count(); + const auto op_bytes_1 = dl1->bytes(); + const auto op_bytes_2 = dl2->bytes(); + if (op_cnt_1 != op_cnt_2 || op_bytes_1 != op_bytes_2 || + dl1->bounds() != dl2->bounds()) { + statistics.AddNewPicture(); + return false; + } + + if (op_bytes_1 > kMaxBytesToCompare) { + statistics.AddPictureTooComplexToCompare(); + return false; + } + + statistics.AddDeepComparePicture(); + + auto res = dl1->Equals(*dl2); + if (res) { + statistics.AddDifferentInstanceButEqualPicture(); + } else { + statistics.AddNewPicture(); + } + return res; +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +void DisplayListLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "DisplayListLayer::Preroll"); + + DisplayList* disp_list = display_list(); + + if (auto* cache = context->raster_cache) { + TRACE_EVENT0("flutter", "DisplayListLayer::RasterCache (Preroll)"); + + SkMatrix ctm = matrix; + ctm.preTranslate(offset_.x(), offset_.y()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + ctm = RasterCache::GetIntegralTransCTM(ctm); +#endif + cache->Prepare(context->gr_context, disp_list, ctm, + context->dst_color_space, is_complex_, will_change_); + } + + SkRect bounds = disp_list->bounds().makeOffset(offset_.x(), offset_.y()); + set_paint_bounds(bounds); +} + +void DisplayListLayer::Paint(PaintContext& context) const { + TRACE_EVENT0("flutter", "DisplayListLayer::Paint"); + FML_DCHECK(display_list_.get()); + FML_DCHECK(needs_painting(context)); + + 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 && + context.raster_cache->Draw(*display_list(), *context.leaf_nodes_canvas)) { + TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); + return; + } + + display_list()->RenderTo(context.leaf_nodes_canvas); +} + +} // namespace flutter diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h new file mode 100644 index 0000000000000..4760d2d200654 --- /dev/null +++ b/flow/layers/display_list_layer.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ +#define FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ + +#include "flutter/flow/display_list.h" +#include "flutter/flow/layers/layer.h" + +namespace flutter { + +class DisplayListLayer : public Layer { + public: + static constexpr size_t kMaxBytesToCompare = 10000; + + DisplayListLayer(const SkPoint& offset, + sk_sp display_list, + bool is_complex, + bool will_change); + + DisplayList* display_list() const { return display_list_.get(); } + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + bool IsReplacing(DiffContext* context, const Layer* layer) const override; + + void Diff(DiffContext* context, const Layer* old_layer) override; + + const DisplayListLayer* as_display_list_layer() const override { + return this; + } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + + void Preroll(PrerollContext* frame, const SkMatrix& matrix) override; + + void Paint(PaintContext& context) const override; + + private: + SkPoint offset_; + sk_sp display_list_; + bool is_complex_ = false; + bool will_change_ = false; + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + sk_sp SerializedPicture() const; + mutable sk_sp cached_serialized_picture_; + static bool Compare(DiffContext::Statistics& statistics, + const DisplayListLayer* l1, + const DisplayListLayer* l2); + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListLayer); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc new file mode 100644 index 0000000000000..ad1842b8c5a73 --- /dev/null +++ b/flow/layers/display_list_layer_unittests.cc @@ -0,0 +1,159 @@ +// 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. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/flow/layers/display_list_layer.h" + +#include "flutter/flow/testing/diff_context_test.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +#ifndef SUPPORT_FRACTIONAL_TRANSLATION +#include "flutter/flow/raster_cache.h" +#endif + +namespace flutter { +namespace testing { + +using DisplayListLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(DisplayListLayerTest, PaintBeforePrerollInvalidDisplayListDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_ref_sp(nullptr), false, false); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "display_list_\\.get\\(\\)"); +} + +TEST_F(DisplayListLayerTest, PaintBeforePrerollDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_make_sp(), false, false); + + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(context\\)"); +} + +TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_make_sp(), false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting(paint_context())); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(context\\)"); +} + +TEST_F(DisplayListLayerTest, InvalidDisplayListDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_ref_sp(nullptr), false, false); + + // Crashes reading a nullptr. + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); +} +#endif + +TEST_F(DisplayListLayerTest, SimpleDisplayList) { + const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); + const SkMatrix layer_offset_matrix = + SkMatrix::Translate(layer_offset.fX, layer_offset.fY); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + DisplayListBuilder builder; + builder.drawRect(picture_bounds); + auto display_list = builder.Build(); + auto layer = std::make_shared(layer_offset, display_list, + false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), + picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_EQ(layer->display_list(), display_list.get()); + EXPECT_TRUE(layer->needs_painting(paint_context())); + + layer->Paint(paint_context()); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_offset_matrix)}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{SkM44( + RasterCache::GetIntegralTransCTM(layer_offset_matrix))}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{picture_bounds, SkPaint()}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using DisplayListLayerDiffTest = DiffContextTest; + +TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) { + auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + + MockLayerTree tree1; + tree1.root()->Add(CreateDisplayListLayer(display_list)); + + auto damage = DiffLayerTree(tree1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + MockLayerTree tree2; + tree2.root()->Add(CreateDisplayListLayer(display_list)); + + damage = DiffLayerTree(tree2, tree1); + EXPECT_TRUE(damage.frame_damage.isEmpty()); + + MockLayerTree tree3; + damage = DiffLayerTree(tree3, tree2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); +} + +TEST_F(DisplayListLayerDiffTest, DisplayListCompare) { + MockLayerTree tree1; + auto display_list1 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + tree1.root()->Add(CreateDisplayListLayer(display_list1)); + + auto damage = DiffLayerTree(tree1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + MockLayerTree tree2; + auto display_list2 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + tree2.root()->Add(CreateDisplayListLayer(display_list2)); + + damage = DiffLayerTree(tree2, tree1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeEmpty()); + + MockLayerTree tree3; + auto display_list3 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + // add offset + tree3.root()->Add( + CreateDisplayListLayer(display_list3, SkPoint::Make(10, 10))); + + damage = DiffLayerTree(tree3, tree2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 70, 70)); + + MockLayerTree tree4; + // different color + auto display_list4 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 2); + tree4.root()->Add( + CreateDisplayListLayer(display_list4, SkPoint::Make(10, 10))); + + damage = DiffLayerTree(tree4, tree3); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 70, 70)); +} + +#endif + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/fuchsia_layer_unittests.cc b/flow/layers/fuchsia_layer_unittests.cc deleted file mode 100644 index df8f8e746ae00..0000000000000 --- a/flow/layers/fuchsia_layer_unittests.cc +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "flutter/flow/layers/child_scene_layer.h" -#include "flutter/flow/layers/container_layer.h" -#include "flutter/flow/layers/opacity_layer.h" -#include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/flow/layers/transform_layer.h" -#include "flutter/flow/view_holder.h" -#include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h" -#include "flutter/fml/task_runner.h" -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { - -using FuchsiaLayerTest = ::testing::Test; - -class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase { - public: - MockSession() : binding_(this) {} - - void NotImplemented_(const std::string& name) final {} - - void Bind(fidl::InterfaceRequest<::fuchsia::ui::scenic::Session> request, - ::fuchsia::ui::scenic::SessionListenerPtr listener) { - binding_.Bind(std::move(request)); - listener_ = std::move(listener); - } - - static std::string Vec3ValueToString(fuchsia::ui::gfx::Vector3Value value) { - return "{" + std::to_string(value.value.x) + ", " + - std::to_string(value.value.y) + ", " + - std::to_string(value.value.z) + "}"; - } - - static std::string QuaternionValueToString( - fuchsia::ui::gfx::QuaternionValue value) { - return "{" + std::to_string(value.value.x) + ", " + - std::to_string(value.value.y) + ", " + - std::to_string(value.value.z) + ", " + - std::to_string(value.value.w) + "}"; - } - - static std::string GfxCreateResourceCmdToString( - const fuchsia::ui::gfx::CreateResourceCmd& cmd) { - std::string id = " id: " + std::to_string(cmd.id); - switch (cmd.resource.Which()) { - case fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle: - return "Rectangle" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle: - return "RoundedRectangle" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder: - return "ViewHolder" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode: - return "OpacityNode" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode: - return "EntityNode" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode: - return "ShapeNode" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial: - return "Material" + id; - case fuchsia::ui::gfx::ResourceArgs::Tag::kImage: - return "Image" + id + ", memory_id: " + - std::to_string(cmd.resource.image().memory_id) + - ", memory_offset: " + - std::to_string(cmd.resource.image().memory_offset); - default: - return "Unhandled CreateResource command" + - std::to_string(cmd.resource.Which()); - } - } - - static std::string GfxCmdToString(const fuchsia::ui::gfx::Command& cmd) { - switch (cmd.Which()) { - case fuchsia::ui::gfx::Command::Tag::kCreateResource: - return "CreateResource: " + - GfxCreateResourceCmdToString(cmd.create_resource()); - case fuchsia::ui::gfx::Command::Tag::kReleaseResource: - return "ReleaseResource id: " + - std::to_string(cmd.release_resource().id); - case fuchsia::ui::gfx::Command::Tag::kAddChild: - return "AddChild id: " + std::to_string(cmd.add_child().node_id) + - " child_id: " + std::to_string(cmd.add_child().child_id); - case fuchsia::ui::gfx::Command::Tag::kSetTranslation: - return "SetTranslation id: " + - std::to_string(cmd.set_translation().id) + - " value: " + Vec3ValueToString(cmd.set_translation().value); - case fuchsia::ui::gfx::Command::Tag::kSetScale: - return "SetScale id: " + std::to_string(cmd.set_scale().id) + - " value: " + Vec3ValueToString(cmd.set_scale().value); - case fuchsia::ui::gfx::Command::Tag::kSetRotation: - return "SetRotation id: " + std::to_string(cmd.set_rotation().id) + - " value: " + QuaternionValueToString(cmd.set_rotation().value); - case fuchsia::ui::gfx::Command::Tag::kSetOpacity: - return "SetOpacity id: " + std::to_string(cmd.set_opacity().node_id) + - ", opacity: " + std::to_string(cmd.set_opacity().opacity); - case fuchsia::ui::gfx::Command::Tag::kSetColor: - return "SetColor id: " + std::to_string(cmd.set_color().material_id) + - ", rgba: (" + std::to_string(cmd.set_color().color.value.red) + - ", " + std::to_string(cmd.set_color().color.value.green) + ", " + - std::to_string(cmd.set_color().color.value.blue) + ", " + - std::to_string(cmd.set_color().color.value.alpha) + ")"; - case fuchsia::ui::gfx::Command::Tag::kSetLabel: - return "SetLabel id: " + std::to_string(cmd.set_label().id) + " " + - cmd.set_label().label; - case fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior: - return "SetHitTestBehavior node_id: " + - std::to_string(cmd.set_hit_test_behavior().node_id); - case fuchsia::ui::gfx::Command::Tag::kSetClipPlanes: - return "SetClipPlanes node_id: " + - std::to_string(cmd.set_clip_planes().node_id); - case fuchsia::ui::gfx::Command::Tag::kSetShape: - return "SetShape node_id: " + std::to_string(cmd.set_shape().node_id) + - ", shape_id: " + std::to_string(cmd.set_shape().shape_id); - case fuchsia::ui::gfx::Command::Tag::kSetMaterial: - return "SetMaterial node_id: " + - std::to_string(cmd.set_material().node_id) + ", material_id: " + - std::to_string(cmd.set_material().material_id); - case fuchsia::ui::gfx::Command::Tag::kSetTexture: - return "SetTexture material_id: " + - std::to_string(cmd.set_texture().material_id) + - ", texture_id: " + std::to_string(cmd.set_texture().texture_id); - - default: - return "Unhandled gfx command" + std::to_string(cmd.Which()); - } - } - - static std::string ScenicCmdToString( - const fuchsia::ui::scenic::Command& cmd) { - if (cmd.Which() != fuchsia::ui::scenic::Command::Tag::kGfx) { - return "Unhandled non-gfx command: " + std::to_string(cmd.Which()); - } - return GfxCmdToString(cmd.gfx()); - } - - // |fuchsia::ui::scenic::Session| - void Enqueue(std::vector cmds) override { - for (const auto& cmd : cmds) { - num_enqueued_commands_++; - EXPECT_FALSE(expected_.empty()) - << "Received more commands than expected; command: <" - << ScenicCmdToString(cmd) - << ">, num_enqueued_commands: " << num_enqueued_commands_; - if (!expected_.empty()) { - EXPECT_TRUE(AreCommandsEqual(expected_.front(), cmd)) - << "actual command: <" << ScenicCmdToString(cmd) - << ">, expected command: <" << ScenicCmdToString(expected_.front()) - << ">, num_enqueued_commands: " << num_enqueued_commands_; - expected_.pop_front(); - } - } - } - - void SetExpectedCommands(std::vector gfx_cmds) { - std::deque scenic_commands; - for (auto it = gfx_cmds.begin(); it != gfx_cmds.end(); it++) { - scenic_commands.push_back(scenic::NewCommand(std::move((*it)))); - } - expected_ = std::move(scenic_commands); - num_enqueued_commands_ = 0; - } - - size_t num_enqueued_commands() { return num_enqueued_commands_; } - - private: - static bool IsGfxCommand(const fuchsia::ui::scenic::Command& cmd, - fuchsia::ui::gfx::Command::Tag tag) { - return cmd.Which() == fuchsia::ui::scenic::Command::Tag::kGfx && - cmd.gfx().Which() == tag; - } - - static bool IsCreateResourceCommand(const fuchsia::ui::scenic::Command& cmd, - fuchsia::ui::gfx::ResourceArgs::Tag tag) { - return IsGfxCommand(cmd, fuchsia::ui::gfx::Command::Tag::kCreateResource) && - cmd.gfx().create_resource().resource.Which() == tag; - } - - static bool AreCommandsEqual(const fuchsia::ui::scenic::Command& command1, - const fuchsia::ui::scenic::Command& command2) { - // For CreateViewHolderCommand, just compare the id and ignore the - // view_holder_token. - if (IsCreateResourceCommand( - command1, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder)) { - return IsCreateResourceCommand( - command2, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder) && - command1.gfx().create_resource().id == - command2.gfx().create_resource().id; - } - // For CreateImageCommand, just compare the id and memory_id. - if (IsCreateResourceCommand(command1, - fuchsia::ui::gfx::ResourceArgs::Tag::kImage)) { - return IsCreateResourceCommand( - command2, fuchsia::ui::gfx::ResourceArgs::Tag::kImage) && - command1.gfx().create_resource().id == - command2.gfx().create_resource().id && - command1.gfx().create_resource().resource.image().memory_id == - command2.gfx().create_resource().resource.image().memory_id; - } - // For SetHitTestBehaviorCommand, just compare the node_id. - if (IsGfxCommand(command1, - fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior)) { - return IsGfxCommand( - command2, - fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior) && - command1.gfx().set_hit_test_behavior().node_id == - command2.gfx().set_hit_test_behavior().node_id; - } - // For SetHitTestBehaviorCommand, just compare the node_id. - if (IsGfxCommand(command1, - fuchsia::ui::gfx::Command::Tag::kSetClipPlanes)) { - return IsGfxCommand(command2, - fuchsia::ui::gfx::Command::Tag::kSetClipPlanes) && - command1.gfx().set_clip_planes().node_id == - command2.gfx().set_clip_planes().node_id; - } - return fidl::Equals(command1, command2); - } - - std::deque expected_; - size_t num_enqueued_commands_ = 0; - fidl::Binding binding_; - fuchsia::ui::scenic::SessionListenerPtr listener_; -}; - -class MockSessionWrapper : public flutter::SessionWrapper { - public: - MockSessionWrapper(fuchsia::ui::scenic::SessionPtr session_ptr) - : session_(std::move(session_ptr)) {} - ~MockSessionWrapper() override = default; - - scenic::Session* get() override { return &session_; } - void Present() override { session_.Flush(); } - - private: - scenic::Session session_; -}; - -struct TestContext { - // Message loop. - fml::RefPtr loop; - fml::RefPtr task_runner; - - // Session. - fidl::InterfaceRequest listener_request; - MockSession mock_session; - std::unique_ptr mock_session_wrapper; - - // SceneUpdateContext. - std::unique_ptr scene_update_context; - - // PrerollContext. - MutatorsStack unused_stack; - const Stopwatch unused_stopwatch; - TextureRegistry unused_texture_registry; - std::unique_ptr preroll_context; -}; - -std::unique_ptr InitTest() { - std::unique_ptr context = std::make_unique(); - - // Init message loop. - context->loop = fml::MakeRefCounted(); - context->task_runner = fml::MakeRefCounted(context->loop); - - // Init Session. - fuchsia::ui::scenic::SessionPtr session_ptr; - fuchsia::ui::scenic::SessionListenerPtr listener; - context->listener_request = listener.NewRequest(); - context->mock_session.Bind(session_ptr.NewRequest(), std::move(listener)); - context->mock_session_wrapper = - std::make_unique(std::move(session_ptr)); - - // Init SceneUpdateContext. - context->scene_update_context = std::make_unique( - "fuchsia_layer_unittest", fuchsia::ui::views::ViewToken(), - scenic::ViewRefPair::New(), *(context->mock_session_wrapper)); - - // Init PrerollContext. - context->preroll_context = std::unique_ptr(new PrerollContext{ - nullptr, // raster_cache (don't consult the cache) - nullptr, // gr_context (used for the raster cache) - nullptr, // external view embedder - context->unused_stack, // mutator stack - nullptr, // SkColorSpace* dst_color_space - kGiantRect, // SkRect cull_rect - false, // layer reads from surface - context->unused_stopwatch, // frame time (dont care) - context->unused_stopwatch, // engine time (dont care) - context->unused_texture_registry, // texture registry (not - // supported) - false, // checkerboard_offscreen_layers - 1.f // ratio between logical and physical - }); - - return context; -} - -zx_koid_t GetChildLayerId() { - static zx_koid_t sChildLayerId = 17324; - return sChildLayerId++; -} - -class AutoDestroyChildLayerId { - public: - AutoDestroyChildLayerId(zx_koid_t id) : id_(id) {} - ~AutoDestroyChildLayerId() { ViewHolder::Destroy(id_); } - - private: - zx_koid_t id_; -}; - -// Create a hierarchy with PhysicalShapeLayers and ChildSceneLayers, and -// inspect the commands sent to Scenic. -// -// -// What we expect: -// -// The Scenic elevations of the PhysicalShapeLayers are monotically -// increasing, even though the elevations we gave them when creating them are -// decreasing. The two should not have any correlation; we're merely mirror -// the paint order using Scenic elevation. -// -// PhysicalShapeLayers created before/below a ChildView do not get their own -// node; PhysicalShapeLayers created afterward do. -// -// Nested PhysicalShapeLayers are collapsed. -// -// This test has been temporarily disabled: fxb/52028 -TEST_F(FuchsiaLayerTest, DISABLED_PhysicalShapeLayersAndChildSceneLayers) { - auto test_context = InitTest(); - - // Root. - auto root = std::make_shared(); - SkPath path; - path.addRect(SkRect::MakeWH(10.f, 10.f)); - - // Child #1: PhysicalShapeLayer. - auto physical_shape1 = std::make_shared( - /*color=*/SK_ColorCYAN, - /*shadow_color=*/SK_ColorBLACK, - /*elevation*/ 23.f, path, Clip::antiAlias); - root->Add(physical_shape1); - - // Child #2: ChildSceneLayer. - const zx_koid_t kChildLayerId1 = GetChildLayerId(); - auto [unused_view_token1, unused_view_holder_token1] = - scenic::ViewTokenPair::New(); - ViewHolder::Create(kChildLayerId1, test_context->task_runner, - std::move(unused_view_holder_token1), - /*bind_callback=*/[](scenic::ResourceId id) {}); - // Will destroy only when we go out of scope (i.e. end of the test). - AutoDestroyChildLayerId auto_destroy1(kChildLayerId1); - auto child_view1 = std::make_shared( - kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10), - /*hit_testable=*/false); - root->Add(child_view1); - - // Child #3: PhysicalShapeLayer - auto physical_shape2 = std::make_shared( - /*color=*/SK_ColorCYAN, - /*shadow_color=*/SK_ColorBLACK, - /*elevation*/ 21.f, path, Clip::antiAlias); - root->Add(physical_shape2); - - // Grandchild (child of #3): PhysicalShapeLayer - auto physical_shape3 = std::make_shared( - /*color=*/SK_ColorCYAN, - /*shadow_color=*/SK_ColorBLACK, - /*elevation*/ 19.f, path, Clip::antiAlias); - physical_shape2->Add(physical_shape3); - - // Child #4: ChildSceneLayer - const zx_koid_t kChildLayerId2 = GetChildLayerId(); - auto [unused_view_token2, unused_view_holder_token2] = - scenic::ViewTokenPair::New(); - ViewHolder::Create(kChildLayerId2, test_context->task_runner, - std::move(unused_view_holder_token2), - /*bind_callback=*/[](scenic::ResourceId id) {}); - // Will destroy only when we go out of scope (i.e. end of the test). - AutoDestroyChildLayerId auto_destroy2(kChildLayerId2); - auto child_view2 = std::make_shared( - kChildLayerId2, SkPoint::Make(1, 1), SkSize::Make(10, 10), - /*hit_testable=*/false); - root->Add(child_view2); - - // Child #5: PhysicalShapeLayer - auto physical_shape4 = std::make_shared( - /*color=*/SK_ColorCYAN, - /*shadow_color=*/SK_ColorBLACK, - /*elevation*/ 17.f, path, Clip::antiAlias); - root->Add(physical_shape4); - - // Preroll. - root->Preroll(test_context->preroll_context.get(), SkMatrix()); - - // Create another frame to be the "real" root. Required because - // UpdateScene() traversal expects there to already be a top node. - SceneUpdateContext::Frame frame(*(test_context->scene_update_context), - SkRRect::MakeRect(SkRect::MakeWH(100, 100)), - SK_ColorTRANSPARENT, SK_AlphaOPAQUE, - "fuchsia test root"); - - // Submit the list of command we will expect Scenic to see. - // - // Some things we expect: - // - // The Scenic elevations of the PhysicalShapeLayers are monotically - // increasing, even though the elevations we gave them when creating them are - // decreasing. The two should not have any correlation; we're merely mirror - // the paint order using Scenic elevation. - // - // PhysicalShapeLayers created before/below a ChildView do not get their own - // node; PhysicalShapeLayers created afterward do. - // - // Nested PhysicalShapeLayers are collapsed. - - std::vector expected; - - // - // Test root. - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root")); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0})); - expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2)); - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon)); - - // - // Child #1: PhysicalShapeLayer - // - // Expect no new commands! Should be composited into base layer. - - // - // Child #2: ChildSceneLayer. - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/4)); - auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New(); - expected.push_back(scenic::NewCreateViewHolderCmd( - /*id=*/5, std::move(view_holder_token1), "")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/3)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/4, "flutter::ViewHolder")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/4)); - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/4, 1.f)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {1, 1, -0.1})); - expected.push_back(scenic::NewSetHitTestBehaviorCmd( - /*id=*/3, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); - - // - // Child #3: PhysicalShapeLayer - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/6)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/6)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/7)); - expected.push_back( - scenic::NewSetLabelCmd(/*id=*/6, "flutter::PhysicalShapeLayer")); - expected.push_back(scenic::NewSetTranslationCmd( - /*id=*/6, {0, 0, -kScenicZElevationBetweenLayers})); - expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/7)); - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/7, kOneMinusEpsilon)); - expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/6, /*ignored*/ {})); - expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/8)); - expected.push_back(scenic::NewCreateRectangleCmd( - /*id=*/9, /*width=*/10, /*height=*/10)); - expected.push_back(scenic::NewSetShapeCmd(/*id=*/8, /*shape_id=*/9)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/8, {5, 5, 0})); - expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/10)); - expected.push_back(scenic::NewSetMaterialCmd(/*id=*/8, /*material_id=*/10)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/8)); - - expected.push_back(scenic::NewCreateImageCmd(/*id=*/11, 0, 0, {})); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/6)); - expected.push_back(scenic::NewSetColorCmd(/*id=*/10, /*r*/ 255, /*g*/ 255, - /*b*/ 255, /*a*/ 255)); - expected.push_back( - scenic::NewSetTextureCmd(/*material_id=*/10, /*texture_id=*/11)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7)); - - // - // Grandchild (child of #3): PhysicalShapeLayer - // - // Expect no new commands! Should be composited into parent. - - // - // Child #4: ChildSceneLayer - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/12)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/13)); - auto [view_token2, view_holder_token2] = scenic::ViewTokenPair::New(); - expected.push_back(scenic::NewCreateViewHolderCmd( - /*id=*/14, std::move(view_holder_token2), "")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/13, /*child_id=*/12)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/13, "flutter::ViewHolder")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/12, /*child_id=*/14)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/13)); - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/13, 1.f)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/12, {1, 1, -0.1})); - expected.push_back(scenic::NewSetHitTestBehaviorCmd( - /*id=*/12, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); - - // - // Child #5: PhysicalShapeLayer - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/15)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/15)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/16)); - expected.push_back( - scenic::NewSetLabelCmd(/*id=*/15, "flutter::PhysicalShapeLayer")); - expected.push_back(scenic::NewSetTranslationCmd( - /*id=*/15, {0, 0, -2 * kScenicZElevationBetweenLayers})); - expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/16)); - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/16, kOneMinusEpsilon)); - expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/15, /*ignored*/ {})); - expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/17)); - expected.push_back(scenic::NewCreateRectangleCmd( - /*id=*/18, /*width=*/10, /*height=*/10)); - expected.push_back(scenic::NewSetShapeCmd(/*id=*/17, /*shape_id=*/18)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/17, {5, 5, 0})); - expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/19)); - expected.push_back(scenic::NewSetMaterialCmd(/*id=*/17, /*material_id=*/19)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/17)); - - expected.push_back(scenic::NewCreateImageCmd(/*id=*/20, 0, 0, {})); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/15)); - expected.push_back(scenic::NewSetColorCmd(/*id=*/19, /*r*/ 255, /*g*/ 255, - /*b*/ 255, /*a*/ 255)); - expected.push_back( - scenic::NewSetTextureCmd(/*material_id=*/19, /*texture_id=*/20)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/19)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/18)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/17)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/16)); - - test_context->mock_session.SetExpectedCommands(std::move(expected)); - - // Finally, UpdateScene(). The MockSession will check the emitted commands - // against the list above. - root->UpdateScene(*(test_context->scene_update_context)); - - test_context->mock_session_wrapper->Present(); - - // Run loop until idle, so that the Session receives and processes - // its method calls. - async_loop_run_until_idle( - async_loop_from_dispatcher(async_get_default_dispatcher())); - - // Ensure we saw enough commands. - EXPECT_EQ(72u, test_context->mock_session.num_enqueued_commands()); -} - -// Create a hierarchy with OpacityLayers, TransformLayer, PhysicalShapeLayers -// and ChildSceneLayers, and inspect the commands sent to Scenic. -// -// We are interested in verifying that the opacity values of children are -// correct, and the transform values as well. -// -// This test has been temporarily disabled: fxb/52028 -TEST_F(FuchsiaLayerTest, DISABLED_OpacityAndTransformLayer) { - auto test_context = InitTest(); - - // Root. - auto root = std::make_shared(); - SkPath path; - path.addRect(SkRect::MakeWH(10.f, 10.f)); - - // OpacityLayer #1 - auto opacity_layer1 = - std::make_shared(127, SkPoint::Make(0, 0)); - root->Add(opacity_layer1); - - // OpacityLayer #2 - auto opacity_layer2 = - std::make_shared(127, SkPoint::Make(0, 0)); - opacity_layer1->Add(opacity_layer2); - - // TransformLayer - SkMatrix translate_and_scale; - translate_and_scale.setScaleTranslate(1.1f, 1.1f, 2.f, 2.f); - auto transform_layer = std::make_shared(translate_and_scale); - opacity_layer2->Add(transform_layer); - - // TransformLayer Child #1: ChildSceneLayer. - const zx_koid_t kChildLayerId1 = GetChildLayerId(); - auto [unused_view_token1, unused_view_holder_token1] = - scenic::ViewTokenPair::New(); - - ViewHolder::Create(kChildLayerId1, test_context->task_runner, - std::move(unused_view_holder_token1), - /*bind_callback=*/[](scenic::ResourceId id) {}); - // Will destroy only when we go out of scope (i.e. end of the test). - AutoDestroyChildLayerId auto_destroy1(kChildLayerId1); - auto child_view1 = std::make_shared( - kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10), - /*hit_testable=*/false); - transform_layer->Add(child_view1); - - // TransformLayer Child #2: PhysicalShapeLayer. - auto physical_shape1 = std::make_shared( - /*color=*/SK_ColorCYAN, - /*shadow_color=*/SK_ColorBLACK, - /*elevation*/ 23.f, path, Clip::antiAlias); - transform_layer->Add(physical_shape1); - - // Preroll. - root->Preroll(test_context->preroll_context.get(), SkMatrix()); - - // Create another frame to be the "real" root. Required because - // UpdateScene() traversal expects there to already be a top node. - SceneUpdateContext::Frame frame(*(test_context->scene_update_context), - SkRRect::MakeRect(SkRect::MakeWH(100, 100)), - SK_ColorTRANSPARENT, SK_AlphaOPAQUE, - "fuchsia test root"); - - // Submit the list of command we will expect Scenic to see. - // - // We are interested in verifying that the opacity values of children are - // correct. - - std::vector expected; - - // - // Test root. - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root")); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0, 0})); - expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2)); - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon)); - - // - // OpacityLayer #1 - // - // Expect no new commands for this. - - // - // OpacityLayer #2 - // - // Expect no new commands for this. - - // - // TransformLayer - // - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/3)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/3, "flutter::Transform")); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {2.f, 2.f, 0.f})); - expected.push_back(scenic::NewSetScaleCmd(/*id=*/3, {1.1f, 1.1f, 1.f})); - expected.push_back(scenic::NewSetRotationCmd(/*id=*/3, {0.f, 0.f, 0.f, 1.f})); - - // - // TransformLayer Child #1: ChildSceneLayer. - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/4)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/5)); - auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New(); - expected.push_back(scenic::NewCreateViewHolderCmd( - /*id=*/6, std::move(view_holder_token1), "")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/5, /*child_id=*/4)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/5, "flutter::ViewHolder")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/6)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5)); - - // Check opacity value. Extra rounding required because we pass alpha as - // a uint/SkAlpha to SceneUpdateContext::Frame. - float opacity1 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f); - opacity1 = SkScalarRoundToInt(opacity1 * 255) / 255.f; - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/5, opacity1)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/4, {1, 1, -0.1})); - expected.push_back(scenic::NewSetHitTestBehaviorCmd( - /*id=*/4, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); - - // - // TransformLayer Child #2: PhysicalShapeLayer - // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/7)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/7)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/8)); - expected.push_back( - scenic::NewSetLabelCmd(/*id=*/7, "flutter::PhysicalShapeLayer")); - expected.push_back(scenic::NewSetTranslationCmd( - /*id=*/7, {0, 0, -kScenicZElevationBetweenLayers})); - expected.push_back(scenic::NewAddChildCmd(/*id=*/7, /*child_id=*/8)); - - // Check opacity value. Extra rounding required because we pass alpha as - // a uint/SkAlpha to SceneUpdateContext::Frame. - float opacity2 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f); - opacity2 = SkScalarRoundToInt(opacity2 * 255) / 255.f; - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/8, opacity2)); - expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/7, /*ignored*/ {})); - expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/9)); - expected.push_back(scenic::NewCreateRectangleCmd( - /*id=*/10, /*width=*/10, /*height=*/10)); - expected.push_back(scenic::NewSetShapeCmd(/*id=*/9, /*shape_id=*/10)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/9, {5, 5, 0})); - expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/11)); - expected.push_back(scenic::NewSetMaterialCmd(/*id=*/9, - /*material_id=*/11)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/7, - /*child_id=*/9)); - - expected.push_back(scenic::NewCreateImageCmd(/*id=*/12, 0, 0, {})); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7)); - expected.push_back(scenic::NewSetColorCmd(/*id=*/11, /*r*/ 255, - /*g*/ 255, - /*b*/ 255, /*a*/ 63)); - expected.push_back( - scenic::NewSetTextureCmd(/*material_id=*/11, /*texture_id=*/12)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/11)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/3)); - - test_context->mock_session.SetExpectedCommands(std::move(expected)); - - // Finally, UpdateScene(). The MockSession will check the emitted - // commands against the list above. - root->UpdateScene(*(test_context->scene_update_context)); - - test_context->mock_session_wrapper->Present(); - - // Run loop until idle, so that the Session receives and processes - // its method calls. - async_loop_run_until_idle( - async_loop_from_dispatcher(async_get_default_dispatcher())); - - // Ensure we saw enough commands. - EXPECT_EQ(46u, test_context->mock_session.num_enqueued_commands()); -} - -} // namespace testing -} // namespace flutter diff --git a/flow/layers/image_filter_layer.cc b/flow/layers/image_filter_layer.cc index 6f2d7aa59e407..e332e067c7db9 100644 --- a/flow/layers/image_filter_layer.cc +++ b/flow/layers/image_filter_layer.cc @@ -11,22 +11,56 @@ ImageFilterLayer::ImageFilterLayer(sk_sp filter) transformed_filter_(nullptr), render_count_(1) {} +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void ImageFilterLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (filter_ != prev->filter_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + + DiffChildren(context, prev); + + SkMatrix inverse; + if (context->GetTransform().invert(&inverse)) { + auto screen_bounds = context->CurrentSubtreeRegion().ComputeBounds(); + + auto filter = filter_->makeWithLocalMatrix(context->GetTransform()); + + auto filter_bounds = + filter->filterBounds(screen_bounds.roundOut(), SkMatrix::I(), + SkImageFilter::kForward_MapDirection); + context->AddLayerBounds(inverse.mapRect(SkRect::Make(filter_bounds))); + } + + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void ImageFilterLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ImageFilterLayer::Preroll"); - Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); SkRect child_bounds = SkRect::MakeEmpty(); PrerollChildren(context, matrix, &child_bounds); - if (filter_) { - const SkIRect filter_input_bounds = child_bounds.roundOut(); - SkIRect filter_output_bounds = - filter_->filterBounds(filter_input_bounds, SkMatrix::I(), - SkImageFilter::kForward_MapDirection); - child_bounds = SkRect::Make(filter_output_bounds); + + if (!filter_) { + set_paint_bounds(child_bounds); + return; } + + const SkIRect filter_input_bounds = child_bounds.roundOut(); + SkIRect filter_output_bounds = filter_->filterBounds( + filter_input_bounds, SkMatrix::I(), SkImageFilter::kForward_MapDirection); + child_bounds = SkRect::Make(filter_output_bounds); + set_paint_bounds(child_bounds); transformed_filter_ = nullptr; @@ -63,7 +97,7 @@ void ImageFilterLayer::Preroll(PrerollContext* context, void ImageFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ImageFilterLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); if (context.raster_cache) { if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas)) { diff --git a/flow/layers/image_filter_layer.h b/flow/layers/image_filter_layer.h index 4b274fef8fdc3..635d57a432365 100644 --- a/flow/layers/image_filter_layer.h +++ b/flow/layers/image_filter_layer.h @@ -14,6 +14,12 @@ class ImageFilterLayer : public MergedContainerLayer { public: ImageFilterLayer(sk_sp filter); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index bcc2d8e08e8b0..61dcdc18fcb63 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -4,11 +4,13 @@ #include "flutter/flow/layers/image_filter_layer.h" +#include "flutter/flow/layers/transform_layer.h" +#include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" -#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/effects/SkImageFilters.h" namespace flutter { namespace testing { @@ -21,11 +23,10 @@ TEST_F(ImageFilterLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) { @@ -37,7 +38,7 @@ TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) { EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -52,7 +53,7 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -74,8 +75,9 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); - auto layer_filter = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto mock_layer = std::make_shared(child_path, child_paint); auto layer = std::make_shared(layer_filter); layer->Add(mock_layer); @@ -85,7 +87,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -108,8 +110,9 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); const SkMatrix filter_transform = SkMatrix::Scale(2.0, 2.0); - auto layer_filter = SkImageFilter::MakeMatrixFilter( - filter_transform, SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter = SkImageFilters::MatrixTransform( + filter_transform, + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto mock_layer = std::make_shared(child_path, child_paint); auto layer = std::make_shared(layer_filter); layer->Add(mock_layer); @@ -118,7 +121,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), filter_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -143,8 +146,9 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); const SkPaint child_paint1 = SkPaint(SkColors::kYellow); const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); auto layer = std::make_shared(layer_filter); @@ -159,9 +163,9 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_rounded_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -188,10 +192,12 @@ TEST_F(ImageFilterLayerTest, Nested) { SkPath().addRect(child_bounds.makeOffset(3.0f, 0.0f)); const SkPaint child_paint1 = SkPaint(SkColors::kYellow); const SkPaint child_paint2 = SkPaint(SkColors::kCyan); - auto layer_filter1 = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); - auto layer_filter2 = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter1 = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); + auto layer_filter2 = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); auto layer1 = std::make_shared(layer_filter1); @@ -212,10 +218,10 @@ TEST_F(ImageFilterLayerTest, Nested) { EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_rounded_bounds); EXPECT_EQ(layer2->paint_bounds(), mock_layer2_rounded_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -241,8 +247,9 @@ TEST_F(ImageFilterLayerTest, Nested) { } TEST_F(ImageFilterLayerTest, Readback) { - auto layer_filter = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto initial_transform = SkMatrix(); // ImageFilterLayer does not read from surface @@ -253,7 +260,7 @@ TEST_F(ImageFilterLayerTest, Readback) { // ImageFilterLayer blocks child with readback auto mock_layer = - std::make_shared(SkPath(), SkPaint(), false, false, true); + std::make_shared(SkPath(), SkPaint(), false, true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; layer->Preroll(preroll_context(), initial_transform); @@ -261,8 +268,9 @@ TEST_F(ImageFilterLayerTest, Readback) { } TEST_F(ImageFilterLayerTest, ChildIsCached) { - auto layer_filter = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); @@ -290,8 +298,9 @@ TEST_F(ImageFilterLayerTest, ChildIsCached) { } TEST_F(ImageFilterLayerTest, ChildrenNotCached) { - auto layer_filter = SkImageFilter::MakeMatrixFilter( - SkMatrix(), SkFilterQuality::kMedium_SkFilterQuality, nullptr); + auto layer_filter = SkImageFilters::MatrixTransform( + SkMatrix(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr); auto initial_transform = SkMatrix::Translate(50.0, 25.5); auto other_transform = SkMatrix::Scale(1.0, 2.0); const SkPath child_path1 = SkPath().addRect(SkRect::MakeWH(5.0f, 5.0f)); @@ -325,5 +334,58 @@ TEST_F(ImageFilterLayerTest, ChildrenNotCached) { EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas)); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using ImageFilterLayerDiffTest = DiffContextTest; + +TEST_F(ImageFilterLayerDiffTest, ImageFilterLayer) { + auto filter = SkImageFilters::Blur(10, 10, SkTileMode::kClamp, nullptr); + + { + // tests later assume 30px paint area, fail early if that's not the case + auto paint_rect = + filter->filterBounds(SkIRect::MakeWH(10, 10), SkMatrix::I(), + SkImageFilter::kForward_MapDirection); + EXPECT_EQ(paint_rect, SkIRect::MakeLTRB(-30, -30, 40, 40)); + } + + MockLayerTree l1; + auto filter_layer = std::make_shared(filter); + auto path = SkPath().addRect(SkRect::MakeLTRB(100, 100, 110, 110)); + filter_layer->Add(std::make_shared(path)); + l1.root()->Add(filter_layer); + + auto damage = DiffLayerTree(l1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(70, 70, 140, 140)); + + MockLayerTree l2; + auto scale = std::make_shared(SkMatrix::Scale(2.0, 2.0)); + scale->Add(filter_layer); + l2.root()->Add(scale); + + damage = DiffLayerTree(l2, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(140, 140, 280, 280)); + + MockLayerTree l3; + l3.root()->Add(scale); + + // path outside of ImageFilterLayer + auto path1 = SkPath().addRect(SkRect::MakeLTRB(130, 130, 140, 140)); + l3.root()->Add(std::make_shared(path1)); + damage = DiffLayerTree(l3, l2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(130, 130, 140, 140)); + + // path intersecting ImageFilterLayer, shouldn't trigger entire + // ImageFilterLayer repaint + MockLayerTree l4; + l4.root()->Add(scale); + auto path2 = SkPath().addRect(SkRect::MakeLTRB(130, 130, 141, 141)); + l4.root()->Add(std::make_shared(path2)); + damage = DiffLayerTree(l4, l3); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(130, 130, 141, 141)); +} + +#endif + } // namespace testing } // namespace flutter diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index a242f977cde29..06a5ae438b367 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -12,7 +12,8 @@ namespace flutter { Layer::Layer() : paint_bounds_(SkRect::MakeEmpty()), unique_id_(NextUniqueID()), - needs_system_composite_(false) {} + original_layer_id_(unique_id_), + subtree_has_platform_view_(false) {} Layer::~Layer() = default; @@ -55,31 +56,6 @@ Layer::AutoPrerollSaveLayerState::~AutoPrerollSaveLayerState() { } } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - -void Layer::CheckForChildLayerBelow(PrerollContext* context) { - // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), - // PhysicalShapeLayers that appear above the embedded content will be turned - // into their own Scenic layers. - child_layer_exists_below_ = context->child_scene_layer_exists_below; - if (child_layer_exists_below_) { - set_needs_system_composite(true); - } -} - -void Layer::UpdateScene(SceneUpdateContext& context) { - FML_DCHECK(needs_system_composite()); - FML_DCHECK(child_layer_exists_below_); - - SceneUpdateContext::Frame frame( - context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT, - SkScalarRoundToInt(context.alphaf() * 255), "flutter::Layer"); - - frame.AddPaintLayer(this); -} - -#endif - Layer::AutoSaveLayer::AutoSaveLayer(const PaintContext& paint_context, const SkRect& bounds, const SkPaint* paint) diff --git a/flow/layers/layer.h b/flow/layers/layer.h index eeffabacbb325..9ad88e6b7843e 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -8,10 +8,11 @@ #include #include +#include "flutter/common/graphics/texture.h" +#include "flutter/flow/diff_context.h" #include "flutter/flow/embedded_views.h" #include "flutter/flow/instrumentation.h" #include "flutter/flow/raster_cache.h" -#include "flutter/flow/texture.h" #include "flutter/fml/build_config.h" #include "flutter/fml/compiler_specific.h" #include "flutter/fml/logging.h" @@ -27,14 +28,12 @@ #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" -#if defined(LEGACY_FUCHSIA_EMBEDDER) -#include "flutter/flow/scene_update_context.h" //nogncheck -#include "lib/ui/scenic/cpp/resources.h" //nogncheck -#include "lib/ui/scenic/cpp/session.h" //nogncheck -#endif - namespace flutter { +namespace testing { +class MockLayer; +} // namespace testing + static constexpr SkRect kGiantRect = SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); // This should be an exact copy of the Clip enum in painting.dart. @@ -59,13 +58,16 @@ struct PrerollContext { // These allow us to track properties like elevation, opacity, and the // prescence of a platform view during Preroll. bool has_platform_view = false; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - // True if, during the traversal so far, we have seen a child_scene_layer. - // Informs whether a layer needs to be system composited. - bool child_scene_layer_exists_below = false; -#endif + // These allow us to track properties like elevation, opacity, and the + // prescence of a texture layer during Preroll. + bool has_texture_layer = false; }; +class PictureLayer; +class DisplayListLayer; +class PerformanceOverlayLayer; +class TextureLayer; + // Represents a single composited layer. Created on the UI thread but then // subquently used on the Rasterizer thread. class Layer { @@ -73,6 +75,33 @@ class Layer { Layer(); virtual ~Layer(); + void AssignOldLayer(Layer* old_layer) { + original_layer_id_ = old_layer->original_layer_id_; + } + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + // Used to establish link between old layer and new layer that replaces it. + // If this method returns true, it is assumed that this layer replaces the old + // layer in tree and is able to diff with it. + virtual bool IsReplacing(DiffContext* context, const Layer* old_layer) const { + return original_layer_id_ == old_layer->original_layer_id_; + } + + // Performs diff with given layer + virtual void Diff(DiffContext* context, const Layer* old_layer) {} + + // Used when diffing retained layer; In case the layer is identical, it + // doesn't need to be diffed, but the paint region needs to be stored in diff + // context so that it can be used in next frame + virtual void PreservePaintRegion(DiffContext* context) { + // retained layer means same instance so 'this' is used to index into both + // current and old region + context->SetLayerPaintRegion(this, context->GetOldLayerPaintRegion(this)); + } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + virtual void Preroll(PrerollContext* context, const SkMatrix& matrix); // Used during Preroll by layers that employ a saveLayer to manage the @@ -152,38 +181,81 @@ class Layer { virtual void Paint(PaintContext& context) const = 0; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - // Updates the system composited scene. - virtual void UpdateScene(SceneUpdateContext& context); - virtual void CheckForChildLayerBelow(PrerollContext* context); -#endif - - bool needs_system_composite() const { return needs_system_composite_; } - void set_needs_system_composite(bool value) { - needs_system_composite_ = value; + bool subtree_has_platform_view() const { return subtree_has_platform_view_; } + void set_subtree_has_platform_view(bool value) { + subtree_has_platform_view_ = value; } + // Returns the paint bounds in the layer's local coordinate system + // as determined during Preroll(). The bounds should include any + // transform, clip or distortions performed by the layer itself, + // but not any similar modifications inherited from its ancestors. const SkRect& paint_bounds() const { return paint_bounds_; } // This must be set by the time Preroll() returns otherwise the layer will // be assumed to have empty paint bounds (paints no content). + // The paint bounds should be independent of the context outside of this + // layer as the layer may be painted under different conditions than + // the Preroll context. The most common example of this condition is + // that we might Preroll the layer with a cull_rect established by a + // clip layer above it but then we might be asked to paint anyway if + // another layer above us needs to cache its children. During the + // paint operation that arises due to the caching, the clip will + // be the bounds of the layer needing caching, not the cull_rect + // that we saw in the overall Preroll operation. void set_paint_bounds(const SkRect& paint_bounds) { paint_bounds_ = paint_bounds; } - bool needs_painting() const { return !paint_bounds_.isEmpty(); } + // Determines if the layer has any content. + bool is_empty() const { return paint_bounds_.isEmpty(); } + + // Determines if the Paint() method is necessary based on the properties + // of the indicated PaintContext object. + bool needs_painting(PaintContext& context) const { + if (subtree_has_platform_view_) { + // Workaround for the iOS embedder. The iOS embedder expects that + // if we preroll it, then we will later call its Paint() method. + // Now that we preroll all layers without any culling, we may + // call its Preroll() without calling its Paint(). For now, we + // will not perform paint culling on any subtree that has a + // platform view. + // See https://github.com/flutter/flutter/issues/81419 + return true; + } + // Workaround for Skia bug (quickReject does not reject empty bounds). + // https://bugs.chromium.org/p/skia/issues/detail?id=10951 + if (paint_bounds_.isEmpty()) { + return false; + } + return !context.leaf_nodes_canvas->quickReject(paint_bounds_); + } + + // Propagated unique_id of the first layer in "chain" of replacement layers + // that can be diffed. + uint64_t original_layer_id() const { return original_layer_id_; } uint64_t unique_id() const { return unique_id_; } - protected: -#if defined(LEGACY_FUCHSIA_EMBEDDER) - bool child_layer_exists_below_ = false; -#endif +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + virtual const PictureLayer* as_picture_layer() const { return nullptr; } + virtual const DisplayListLayer* as_display_list_layer() const { + return nullptr; + } + virtual const TextureLayer* as_texture_layer() const { return nullptr; } + virtual const PerformanceOverlayLayer* as_performance_overlay_layer() const { + return nullptr; + } + virtual const testing::MockLayer* as_mock_layer() const { return nullptr; } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT private: SkRect paint_bounds_; uint64_t unique_id_; - bool needs_system_composite_; + uint64_t original_layer_id_; + bool subtree_has_platform_view_; static uint64_t NextUniqueID(); diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index e0ac5157fc746..58205ae64590f 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -4,7 +4,9 @@ #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/frame_timings.h" #include "flutter/flow/layers/layer.h" +#include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" @@ -20,15 +22,6 @@ LayerTree::LayerTree(const SkISize& frame_size, float device_pixel_ratio) FML_CHECK(device_pixel_ratio_ != 0.0f); } -void LayerTree::RecordBuildTime(fml::TimePoint vsync_start, - fml::TimePoint build_start, - fml::TimePoint target_time) { - vsync_start_ = vsync_start; - build_start_ = build_start; - target_time_ = target_time; - build_finish_ = fml::TimePoint::Now(); -} - bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) { TRACE_EVENT0("flutter", "LayerTree::Preroll"); @@ -61,31 +54,6 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, return context.surface_needs_readback; } -#if defined(LEGACY_FUCHSIA_EMBEDDER) -void LayerTree::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "LayerTree::UpdateScene"); - - // Reset for a new Scene. - context.Reset(); - - const float inv_dpr = 1.0f / device_pixel_ratio_; - SceneUpdateContext::Transform transform(context, inv_dpr, inv_dpr, 1.0f); - - SceneUpdateContext::Frame frame( - context, - SkRRect::MakeRect( - SkRect::MakeWH(frame_size_.width(), frame_size_.height())), - SK_ColorTRANSPARENT, SK_AlphaOPAQUE, "flutter::LayerTree"); - if (root_layer_->needs_system_composite()) { - root_layer_->UpdateScene(context); - } - if (root_layer_->needs_painting()) { - frame.AddPaintLayer(root_layer_.get()); - } - context.root_node().AddChild(transform.entity_node()); -} -#endif - void LayerTree::Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) const { TRACE_EVENT0("flutter", "LayerTree::Paint"); @@ -117,7 +85,7 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, checkerboard_offscreen_layers_, device_pixel_ratio_}; - if (root_layer_->needs_painting()) { + if (root_layer_->needs_painting(context)) { root_layer_->Paint(context); } } @@ -176,7 +144,7 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { if (root_layer_) { root_layer_->Preroll(&preroll_context, root_surface_transformation); // The needs painting flag may be set after the preroll. So check it after. - if (root_layer_->needs_painting()) { + if (root_layer_->needs_painting(paint_context)) { root_layer_->Paint(paint_context); } } diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index b9cd8d37de7c0..6e4a202deaf76 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -31,10 +31,6 @@ class LayerTree { bool Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false); -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context); -#endif - void Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache = false) const; @@ -49,15 +45,12 @@ class LayerTree { const SkISize& frame_size() const { return frame_size_; } float device_pixel_ratio() const { return device_pixel_ratio_; } - void RecordBuildTime(fml::TimePoint vsync_start, - fml::TimePoint build_start, - fml::TimePoint target_time); - fml::TimePoint vsync_start() const { return vsync_start_; } - fml::TimeDelta vsync_overhead() const { return build_start_ - vsync_start_; } - fml::TimePoint build_start() const { return build_start_; } - fml::TimePoint build_finish() const { return build_finish_; } - fml::TimeDelta build_time() const { return build_finish_ - build_start_; } - fml::TimePoint target_time() const { return target_time_; } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + const PaintRegionMap& paint_region_map() const { return paint_region_map_; } + PaintRegionMap& paint_region_map() { return paint_region_map_; } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT // The number of frame intervals missed after which the compositor must // trace the rasterized picture to a trace file. Specify 0 to disable all @@ -80,16 +73,16 @@ class LayerTree { private: std::shared_ptr root_layer_; - fml::TimePoint vsync_start_; - fml::TimePoint build_start_; - fml::TimePoint build_finish_; - fml::TimePoint target_time_; SkISize frame_size_ = SkISize::MakeEmpty(); // Physical pixels. const float device_pixel_ratio_; // Logical / Physical pixels ratio. uint32_t rasterizer_tracing_threshold_; bool checkerboard_raster_cache_images_; bool checkerboard_offscreen_layers_; +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + PaintRegionMap paint_region_map_; +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + FML_DISALLOW_COPY_AND_ASSIGN(LayerTree); }; diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 7045497692afe..2c70149a2ccdb 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -46,12 +46,12 @@ TEST_F(LayerTreeTest, PaintingEmptyLayerDies) { layer_tree().set_root_layer(layer); layer_tree().Preroll(frame()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_TRUE(layer->is_empty()); layer_tree().Paint(frame()); } -TEST_F(LayerTreeTest, PaintBeforePreollDies) { +TEST_F(LayerTreeTest, PaintBeforePrerollDies) { const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); SkPath child_path; child_path.addRect(child_bounds); @@ -62,8 +62,8 @@ TEST_F(LayerTreeTest, PaintBeforePreollDies) { layer_tree().set_root_layer(layer); EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->is_empty()); + EXPECT_TRUE(layer->is_empty()); layer_tree().Paint(frame()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); @@ -81,8 +81,8 @@ TEST_F(LayerTreeTest, Simple) { layer_tree().Preroll(frame()); EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_FALSE(mock_layer->is_empty()); + EXPECT_FALSE(layer->is_empty()); EXPECT_EQ(mock_layer->parent_matrix(), root_transform()); layer_tree().Paint(frame()); @@ -110,12 +110,9 @@ TEST_F(LayerTreeTest, Multiple) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(mock_layer1->is_empty()); + EXPECT_FALSE(mock_layer2->is_empty()); + EXPECT_FALSE(layer->is_empty()); EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); @@ -146,12 +143,9 @@ TEST_F(LayerTreeTest, MultipleWithEmpty) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_FALSE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(mock_layer1->is_empty()); + EXPECT_TRUE(mock_layer2->is_empty()); + EXPECT_FALSE(layer->is_empty()); EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); @@ -169,8 +163,7 @@ TEST_F(LayerTreeTest, NeedsSystemComposite) { const SkPaint child_paint1(SkColors::kGray); const SkPaint child_paint2(SkColors::kGreen); auto mock_layer1 = std::make_shared( - child_path1, child_paint1, false /* fake_has_platform_view */, - true /* fake_needs_system_composite */); + child_path1, child_paint1, false /* fake_has_platform_view */); auto mock_layer2 = std::make_shared(child_path2, child_paint2); auto layer = std::make_shared(); layer->Add(mock_layer1); @@ -183,12 +176,9 @@ TEST_F(LayerTreeTest, NeedsSystemComposite) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_TRUE(mock_layer1->needs_system_composite()); - EXPECT_FALSE(mock_layer2->needs_system_composite()); - EXPECT_TRUE(layer->needs_system_composite()); + EXPECT_FALSE(mock_layer1->is_empty()); + EXPECT_FALSE(mock_layer2->is_empty()); + EXPECT_FALSE(layer->is_empty()); EXPECT_EQ(mock_layer1->parent_matrix(), root_transform()); EXPECT_EQ(mock_layer2->parent_matrix(), root_transform()); EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 131b1d9f253b7..0d19c9c5d6151 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -12,12 +12,30 @@ namespace flutter { OpacityLayer::OpacityLayer(SkAlpha alpha, const SkPoint& offset) : alpha_(alpha), offset_(offset) {} +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (alpha_ != prev->alpha_ || offset_ != prev->offset_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + context->PushTransform(SkMatrix::Translate(offset_.fX, offset_.fY)); + DiffChildren(context, prev); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "OpacityLayer::Preroll"); FML_DCHECK(!GetChildContainer()->layers().empty()); // We can't be a leaf. SkMatrix child_matrix = matrix; - child_matrix.postTranslate(offset_.fX, offset_.fY); + child_matrix.preTranslate(offset_.fX, offset_.fY); // Similar to what's done in TransformLayer::Preroll, we have to apply the // reverse transformation to the cull rect to properly cull child layers. @@ -46,7 +64,7 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { void OpacityLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "OpacityLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); SkPaint paint; paint.setAlpha(alpha_); @@ -83,15 +101,4 @@ void OpacityLayer::Paint(PaintContext& context) const { PaintChildren(context); } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - -void OpacityLayer::UpdateScene(SceneUpdateContext& context) { - float saved_alpha = context.alphaf(); - context.set_alphaf(context.alphaf() * (alpha_ / 255.f)); - ContainerLayer::UpdateScene(context); - context.set_alphaf(saved_alpha); -} - -#endif - } // namespace flutter diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index ed5f0283ad356..52010ce895ff1 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -27,14 +27,16 @@ class OpacityLayer : public MergedContainerLayer { // the propagation as repainting the OpacityLayer is expensive. OpacityLayer(SkAlpha alpha, const SkPoint& offset); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context) override; -#endif - private: SkAlpha alpha_; SkPoint offset_; diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 1ca9ddb2c4041..80c16052f8ecb 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -34,14 +34,14 @@ TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_FALSE(mock_layer->needs_painting()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(mock_layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -TEST_F(OpacityLayerTest, PaintBeforePreollDies) { +TEST_F(OpacityLayerTest, PaintBeforePrerollDies) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); auto mock_layer = std::make_shared(child_path); @@ -50,10 +50,27 @@ TEST_F(OpacityLayerTest, PaintBeforePreollDies) { layer->Add(mock_layer); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif +TEST_F(OpacityLayerTest, TranslateChildren) { + SkPath child_path1; + child_path1.addRect(10.0f, 10.0f, 20.0f, 20.f); + SkPaint child_paint1(SkColors::kGray); + auto layer = std::make_shared(0.5, SkPoint::Make(10, 10)); + auto mock_layer1 = std::make_shared(child_path1, child_paint1); + layer->Add(mock_layer1); + + auto initial_transform = SkMatrix::Scale(2.0, 2.0); + layer->Preroll(preroll_context(), initial_transform); + + SkRect layer_bounds = mock_layer1->paint_bounds(); + mock_layer1->parent_matrix().mapRect(&layer_bounds); + + EXPECT_EQ(layer_bounds, SkRect::MakeXYWH(40, 40, 20, 20)); +} + TEST_F(OpacityLayerTest, ChildIsCached) { const SkAlpha alpha_half = 255 / 2; auto initial_transform = SkMatrix::Translate(50.0, 25.5); @@ -139,8 +156,8 @@ TEST_F(OpacityLayerTest, FullyOpaque) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix::Concat(initial_transform, layer_transform)); EXPECT_EQ(mock_layer->parent_mutators(), @@ -152,10 +169,11 @@ TEST_F(OpacityLayerTest, FullyOpaque) { .roundOut(&opacity_bounds); auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, #ifndef SUPPORT_FRACTIONAL_TRANSLATION MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, + 1, MockCanvas::SetMatrixData{SkM44(integral_layer_transform)}}, #endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, @@ -189,8 +207,8 @@ TEST_F(OpacityLayerTest, FullyTransparent) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix::Concat(initial_transform, layer_transform)); EXPECT_EQ( @@ -199,17 +217,16 @@ TEST_F(OpacityLayerTest, FullyTransparent) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, #ifndef SUPPORT_FRACTIONAL_TRANSLATION MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, + 1, MockCanvas::SetMatrixData{SkM44(integral_layer_transform)}}, #endif MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, MockCanvas::DrawCall{ 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, MockCanvas::kHard_ClipEdgeStyle}}, - MockCanvas::DrawCall{2, - MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); layer->Paint(paint_context()); @@ -237,8 +254,8 @@ TEST_F(OpacityLayerTest, HalfTransparent) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix::Concat(initial_transform, layer_transform)); EXPECT_EQ(mock_layer->parent_mutators(), @@ -251,10 +268,11 @@ TEST_F(OpacityLayerTest, HalfTransparent) { .roundOut(&opacity_bounds); auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, #ifndef SUPPORT_FRACTIONAL_TRANSLATION MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, + 1, MockCanvas::SetMatrixData{SkM44(integral_layer_transform)}}, #endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, @@ -312,11 +330,11 @@ TEST_F(OpacityLayerTest, Nested) { EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds()); EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(mock_layer3->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer3->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), SkMatrix::Concat(initial_transform, layer1_transform)); // EXPECT_EQ(mock_layer1->parent_mutators(), @@ -344,10 +362,11 @@ TEST_F(OpacityLayerTest, Nested) { .roundOut(&opacity2_bounds); auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer1_transform)}}, #ifndef SUPPORT_FRACTIONAL_TRANSLATION MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer1_transform}}, + 1, MockCanvas::SetMatrixData{SkM44(integral_layer1_transform)}}, #endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint, @@ -355,10 +374,11 @@ TEST_F(OpacityLayerTest, Nested) { MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child1_path, child1_paint}}, MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, - MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}}, + MockCanvas::DrawCall{ + 3, MockCanvas::ConcatMatrixData{SkM44(layer2_transform)}}, #ifndef SUPPORT_FRACTIONAL_TRANSLATION MockCanvas::DrawCall{ - 3, MockCanvas::SetMatrixData{integral_layer2_transform}}, + 3, MockCanvas::SetMatrixData{SkM44(integral_layer2_transform)}}, #endif MockCanvas::DrawCall{ 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint, @@ -387,7 +407,7 @@ TEST_F(OpacityLayerTest, Readback) { // OpacityLayer blocks child with readback auto mock_layer = - std::make_shared(SkPath(), SkPaint(), false, false, true); + std::make_shared(SkPath(), SkPaint(), false, true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; layer->Preroll(preroll_context(), initial_transform); diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index a2e4a33adfc16..b9ccee84c7a79 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -74,6 +74,22 @@ PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options, } } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void PerformanceOverlayLayer::Diff(DiffContext* context, + const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(old_layer); + auto prev = old_layer->as_performance_overlay_layer(); + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(prev)); + } + context->AddLayerBounds(paint_bounds()); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void PerformanceOverlayLayer::Paint(PaintContext& context) const { const int padding = 8; diff --git a/flow/layers/performance_overlay_layer.h b/flow/layers/performance_overlay_layer.h index b1434a221e688..91b90244e3f61 100644 --- a/flow/layers/performance_overlay_layer.h +++ b/flow/layers/performance_overlay_layer.h @@ -26,6 +26,20 @@ class PerformanceOverlayLayer : public Layer { const std::string& label_prefix, const std::string& font_path); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + bool IsReplacing(DiffContext* context, const Layer* layer) const override { + return layer->as_performance_overlay_layer() != nullptr; + } + + void Diff(DiffContext* context, const Layer* old_layer) override; + + const PerformanceOverlayLayer* as_performance_overlay_layer() const override { + return this; + } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + explicit PerformanceOverlayLayer(uint64_t options, const char* font_path = nullptr); diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 7a010fd4e703c..0fe52300cb5ee 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -126,7 +126,7 @@ TEST_F(PerformanceOverlayLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); // Crashes reading a nullptr. EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), ""); @@ -143,7 +143,7 @@ TEST_F(PerformanceOverlayLayerTest, InvalidOptions) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); // Nothing is drawn if options are invalid (0). layer->Paint(paint_context()); @@ -161,7 +161,7 @@ TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); layer->Paint(paint_context()); auto overlay_text = PerformanceOverlayLayer::MakeStatisticsText( @@ -170,6 +170,13 @@ TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { SkPaint text_paint; text_paint.setColor(SK_ColorGRAY); SkPoint text_position = SkPoint::Make(16.0f, 22.0f); + + // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the + // performance overlay can use Fuchsia's font manager instead of the empty + // default. +#if defined(OS_FUCHSIA) + GTEST_SKIP() << "Expectation requires a valid default font manager"; +#endif // OS_FUCHSIA EXPECT_EQ(mock_canvas().draw_calls(), std::vector({MockCanvas::DrawCall{ 0, MockCanvas::DrawTextData{overlay_text_data, text_paint, diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 534962fff0a40..a9b76e1ea18eb 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -23,6 +23,39 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, path_(path), clip_behavior_(clip_behavior) {} +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void PhysicalShapeLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (color_ != prev->color_ || shadow_color_ != prev->shadow_color_ || + elevation_ != prev->elevation() || path_ != prev->path_ || + clip_behavior_ != prev->clip_behavior_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + + SkRect bounds; + if (elevation_ == 0) { + bounds = path_.getBounds(); + } else { + bounds = ComputeShadowBounds(path_, elevation_, + context->frame_device_pixel_ratio(), + context->GetTransform()); + } + + context->AddLayerBounds(bounds); + + if (context->PushCullRect(bounds)) { + DiffChildren(context, prev); + } + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll"); @@ -38,14 +71,14 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, // We will draw the shadow in Paint(), so add some margin to the paint // bounds to leave space for the shadow. We fill this whole region and clip // children to it so we don't need to join the child paint bounds. - set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_, - context->frame_device_pixel_ratio)); + set_paint_bounds(ComputeShadowBounds( + path_, elevation_, context->frame_device_pixel_ratio, matrix)); } } void PhysicalShapeLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); if (elevation_ != 0) { DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation_, @@ -95,47 +128,15 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { } } -SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkRect& bounds, +SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkPath& path, float elevation, - float pixel_ratio) { - // The shadow offset is calculated as follows: - // .--- (kLightRadius) - // -------/ (light) - // | / - // | / - // |/ - // |O - // /| (kLightHeight) - // / | - // / | - // / | - // / | - // ------------- (layer) - // /| | - // / | | (elevation) - // A / | |B - // ------------------------------------------------ (canvas) - // --- (extent of shadow) - // - // E = lt } t = (r + w/2)/h - // } => - // r + w/2 = ht } E = (l/h)(r + w/2) - // - // Where: E = extent of shadow - // l = elevation of layer - // r = radius of the light source - // w = width of the layer - // h = light height - // t = tangent of AOB, i.e., multiplier for elevation to extent - // tangent for x - double tx = - (kLightRadius * pixel_ratio + bounds.width() * 0.5) / kLightHeight; - // tangent for y - double ty = - (kLightRadius * pixel_ratio + bounds.height() * 0.5) / kLightHeight; - SkRect shadow_bounds(bounds); - shadow_bounds.outset(elevation * tx, elevation * ty); - + SkScalar dpr, + const SkMatrix& ctm) { + SkRect shadow_bounds(path.getBounds()); + SkShadowUtils::GetLocalBounds( + ctm, path, SkPoint3::Make(0, 0, dpr * elevation), + SkPoint3::Make(0, -1, 1), kLightRadius / kLightHeight, + SkShadowFlags::kDirectionalLight_ShadowFlag, &shadow_bounds); return shadow_bounds; } @@ -148,21 +149,19 @@ void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas, const SkScalar kAmbientAlpha = 0.039f; const SkScalar kSpotAlpha = 0.25f; - SkShadowFlags flags = transparentOccluder - ? SkShadowFlags::kTransparentOccluder_ShadowFlag - : SkShadowFlags::kNone_ShadowFlag; - const SkRect& bounds = path.getBounds(); - SkScalar shadow_x = (bounds.left() + bounds.right()) / 2; - SkScalar shadow_y = bounds.top() - 600.0f; + uint32_t flags = transparentOccluder + ? SkShadowFlags::kTransparentOccluder_ShadowFlag + : SkShadowFlags::kNone_ShadowFlag; + flags |= SkShadowFlags::kDirectionalLight_ShadowFlag; SkColor inAmbient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color)); SkColor inSpot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color)); SkColor ambientColor, spotColor; SkShadowUtils::ComputeTonalColors(inAmbient, inSpot, &ambientColor, &spotColor); - SkShadowUtils::DrawShadow( - canvas, path, SkPoint3::Make(0, 0, dpr * elevation), - SkPoint3::Make(shadow_x, shadow_y, dpr * kLightHeight), - dpr * kLightRadius, ambientColor, spotColor, flags); + SkShadowUtils::DrawShadow(canvas, path, SkPoint3::Make(0, 0, dpr * elevation), + SkPoint3::Make(0, -1, 1), + kLightRadius / kLightHeight, ambientColor, + spotColor, flags); } } // namespace flutter diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index ce49af1a003ae..44e555e9fb5ef 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -17,9 +17,10 @@ class PhysicalShapeLayer : public ContainerLayer { const SkPath& path, Clip clip_behavior); - static SkRect ComputeShadowBounds(const SkRect& bounds, + static SkRect ComputeShadowBounds(const SkPath& path, float elevation, - float pixel_ratio); + SkScalar dpr, + const SkMatrix& ctm); static void DrawShadow(SkCanvas* canvas, const SkPath& path, SkColor color, @@ -27,6 +28,12 @@ class PhysicalShapeLayer : public ContainerLayer { bool transparentOccluder, SkScalar dpr); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index bb5d0acfad757..e81c28080ea53 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -23,14 +23,13 @@ TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -TEST_F(PhysicalShapeLayerTest, PaintBeforePreollDies) { +TEST_F(PhysicalShapeLayerTest, PaintBeforePrerollDies) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); auto mock_layer = std::make_shared(child_path, SkPaint()); @@ -41,7 +40,7 @@ TEST_F(PhysicalShapeLayerTest, PaintBeforePreollDies) { layer->Add(mock_layer); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -54,8 +53,7 @@ TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) { layer_path, Clip::none); layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(layer->needs_painting(paint_context())); SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); @@ -93,8 +91,7 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) { child_paint_bounds.join(child2->paint_bounds()); EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds()); EXPECT_NE(layer->paint_bounds(), child_paint_bounds); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(layer->needs_painting(paint_context())); SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); @@ -127,15 +124,11 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not do any painting there. EXPECT_EQ(layer->paint_bounds(), - PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), - initial_elevation, 1.0f)); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + PhysicalShapeLayer::ComputeShadowBounds( + layer_path, initial_elevation, 1.0f, SkMatrix())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(layer->elevation(), initial_elevation); - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(LEGACY_FUCHSIA_EMBEDDER) SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); layer_paint.setAntiAlias(true); @@ -146,7 +139,6 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { {MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, MockCanvas::DrawCall{ 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -#endif } TEST_F(PhysicalShapeLayerTest, ElevationComplex) { @@ -182,15 +174,11 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { // there. EXPECT_EQ(layers[i]->paint_bounds(), (PhysicalShapeLayer::ComputeShadowBounds( - layer_path.getBounds(), initial_elevations[i], - 1.0f /* pixel_ratio */))); - EXPECT_TRUE(layers[i]->needs_painting()); - EXPECT_FALSE(layers[i]->needs_system_composite()); + layer_path, initial_elevations[i], 1.0f /* pixel_ratio */, + SkMatrix()))); + EXPECT_TRUE(layers[i]->needs_painting(paint_context())); } - // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and - // their shadows , so we do not use the direct |Paint()| path there. -#if !defined(LEGACY_FUCHSIA_EMBEDDER) SkPaint layer_paint; layer_paint.setColor(SK_ColorBLACK); layer_paint.setAntiAlias(true); @@ -210,7 +198,93 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}}, MockCanvas::DrawCall{ 0, MockCanvas::DrawPathData{layer_path, layer_paint}}})); -#endif +} + +TEST_F(PhysicalShapeLayerTest, ShadowNotDependsCtm) { + constexpr SkScalar elevations[] = {1, 2, 3, 4, 5, 10}; + constexpr SkScalar scales[] = {0.5, 1, 1.5, 2, 3, 5}; + constexpr SkScalar translates[] = {0, 1, -1, 0.5, 2, 10}; + + SkPath path; + path.addRect(0, 0, 8, 8).close(); + + for (SkScalar elevation : elevations) { + SkRect baseline_bounds = PhysicalShapeLayer::ComputeShadowBounds( + path, elevation, 1.0f, SkMatrix()); + for (SkScalar scale : scales) { + for (SkScalar translateX : translates) { + for (SkScalar translateY : translates) { + SkMatrix ctm; + ctm.setScaleTranslate(scale, scale, translateX, translateY); + SkRect bounds = PhysicalShapeLayer::ComputeShadowBounds( + path, elevation, scale, ctm); + EXPECT_FLOAT_EQ(bounds.fLeft, baseline_bounds.fLeft); + EXPECT_FLOAT_EQ(bounds.fTop, baseline_bounds.fTop); + EXPECT_FLOAT_EQ(bounds.fRight, baseline_bounds.fRight); + EXPECT_FLOAT_EQ(bounds.fBottom, baseline_bounds.fBottom); + } + } + } + } +} + +static int RasterizedDifferenceInPixels( + const std::function& actual_draw_function, + const std::function& expected_draw_function, + const SkSize& canvas_size) { + sk_sp actual_surface = + SkSurface::MakeRasterN32Premul(canvas_size.width(), canvas_size.height()); + sk_sp expected_surface = + SkSurface::MakeRasterN32Premul(canvas_size.width(), canvas_size.height()); + + actual_surface->getCanvas()->drawColor(SK_ColorWHITE); + expected_surface->getCanvas()->drawColor(SK_ColorWHITE); + + actual_draw_function(actual_surface->getCanvas()); + expected_draw_function(expected_surface->getCanvas()); + + SkPixmap actual_pixels; + EXPECT_TRUE(actual_surface->peekPixels(&actual_pixels)); + + SkPixmap expected_pixels; + EXPECT_TRUE(expected_surface->peekPixels(&expected_pixels)); + + int different_pixels = 0; + for (int y = 0; y < canvas_size.height(); y++) { + const uint32_t* actual_row = actual_pixels.addr32(0, y); + const uint32_t* expected_row = expected_pixels.addr32(0, y); + for (int x = 0; x < canvas_size.width(); x++) { + if (actual_row[x] != expected_row[x]) { + different_pixels++; + } + } + } + return different_pixels; +} + +TEST_F(PhysicalShapeLayerTest, ShadowNotDependsPathSize) { + constexpr SkRect test_cases[][2] = { + {{20, -100, 80, 80}, {20, -1000, 80, 80}}, + {{20, 20, 80, 200}, {20, 20, 80, 2000}}, + }; + + for (const SkRect* test_case : test_cases) { + EXPECT_EQ(RasterizedDifferenceInPixels( + [=](SkCanvas* canvas) { + SkPath path; + path.addRect(test_case[0]).close(); + PhysicalShapeLayer::DrawShadow(canvas, path, SK_ColorBLACK, + 1.0f, false, 1.0f); + }, + [=](SkCanvas* canvas) { + SkPath path; + path.addRect(test_case[1]).close(); + PhysicalShapeLayer::DrawShadow(canvas, path, SK_ColorBLACK, + 1.0f, false, 1.0f); + }, + SkSize::Make(100, 100)), + 0); + } } static bool ReadbackResult(PrerollContext* context, @@ -242,7 +316,7 @@ TEST_F(PhysicalShapeLayerTest, Readback) { const Clip save_layer = Clip::antiAliasWithSaveLayer; std::shared_ptr nochild; - auto reader = std::make_shared(path, paint, false, false, true); + auto reader = std::make_shared(path, paint, false, true); auto nonreader = std::make_shared(path, paint); // No children, no prior readback -> no readback after diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index d5d6a34b573e8..007a4edbdba83 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -5,6 +5,7 @@ #include "flutter/flow/layers/picture_layer.h" #include "flutter/fml/logging.h" +#include "third_party/skia/include/core/SkSerialProcs.h" namespace flutter { @@ -17,12 +18,96 @@ PictureLayer::PictureLayer(const SkPoint& offset, is_complex_(is_complex), will_change_(will_change) {} -void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - TRACE_EVENT0("flutter", "PictureLayer::Preroll"); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +bool PictureLayer::IsReplacing(DiffContext* context, const Layer* layer) const { + // Only return true for identical pictures; This way + // ContainerLayer::DiffChildren can detect when a picture layer got inserted + // between other picture layers + auto picture_layer = layer->as_picture_layer(); + return picture_layer != nullptr && offset_ == picture_layer->offset_ && + Compare(context->statistics(), this, picture_layer); +} -#if defined(LEGACY_FUCHSIA_EMBEDDER) - CheckForChildLayerBelow(context); +void PictureLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + if (!context->IsSubtreeDirty()) { +#ifndef NDEBUG + FML_DCHECK(old_layer); + auto prev = old_layer->as_picture_layer(); + DiffContext::Statistics dummy_statistics; + // IsReplacing has already determined that the picture is same + FML_DCHECK(prev->offset_ == offset_ && + Compare(dummy_statistics, this, prev)); #endif + } + context->PushTransform(SkMatrix::Translate(offset_.x(), offset_.y())); + context->AddLayerBounds(picture()->cullRect()); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +bool PictureLayer::Compare(DiffContext::Statistics& statistics, + const PictureLayer* l1, + const PictureLayer* l2) { + const auto& pic1 = l1->picture_.skia_object(); + const auto& pic2 = l2->picture_.skia_object(); + if (pic1.get() == pic2.get()) { + statistics.AddSameInstancePicture(); + return true; + } + auto op_cnt_1 = pic1->approximateOpCount(); + auto op_cnt_2 = pic2->approximateOpCount(); + if (op_cnt_1 != op_cnt_2 || pic1->cullRect() != pic2->cullRect()) { + statistics.AddNewPicture(); + return false; + } + + if (op_cnt_1 > 10) { + statistics.AddPictureTooComplexToCompare(); + return false; + } + + statistics.AddDeepComparePicture(); + + // TODO(knopp) we don't actually need the data; this could be done without + // allocations by implementing stream that calculates SHA hash and + // comparing those hashes + auto d1 = l1->SerializedPicture(); + auto d2 = l2->SerializedPicture(); + auto res = d1->equals(d2.get()); + if (res) { + statistics.AddDifferentInstanceButEqualPicture(); + } else { + statistics.AddNewPicture(); + } + return res; +} + +sk_sp PictureLayer::SerializedPicture() const { + if (!cached_serialized_picture_) { + SkSerialProcs procs = { + nullptr, + nullptr, + [](SkImage* i, void* ctx) { + auto id = i->uniqueID(); + return SkData::MakeWithCopy(&id, sizeof(id)); + }, + nullptr, + [](SkTypeface* tf, void* ctx) { + auto id = tf->uniqueID(); + return SkData::MakeWithCopy(&id, sizeof(id)); + }, + nullptr, + }; + cached_serialized_picture_ = picture_.skia_object()->serialize(&procs); + } + return cached_serialized_picture_; +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "PictureLayer::Preroll"); SkPicture* sk_picture = picture(); @@ -30,7 +115,7 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "PictureLayer::RasterCache (Preroll)"); SkMatrix ctm = matrix; - ctm.postTranslate(offset_.x(), offset_.y()); + ctm.preTranslate(offset_.x(), offset_.y()); #ifndef SUPPORT_FRACTIONAL_TRANSLATION ctm = RasterCache::GetIntegralTransCTM(ctm); #endif @@ -44,8 +129,8 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { void PictureLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "PictureLayer::Paint"); - FML_DCHECK(picture_.get()); - FML_DCHECK(needs_painting()); + FML_DCHECK(picture_.skia_object()); + FML_DCHECK(needs_painting(context)); SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); diff --git a/flow/layers/picture_layer.h b/flow/layers/picture_layer.h index e733e7455ca6c..d90d32cedf2b3 100644 --- a/flow/layers/picture_layer.h +++ b/flow/layers/picture_layer.h @@ -20,7 +20,17 @@ class PictureLayer : public Layer { bool is_complex, bool will_change); - SkPicture* picture() const { return picture_.get().get(); } + SkPicture* picture() const { return picture_.skia_object().get(); } + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + bool IsReplacing(DiffContext* context, const Layer* layer) const override; + + void Diff(DiffContext* context, const Layer* old_layer) override; + + const PictureLayer* as_picture_layer() const override { return this; } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT void Preroll(PrerollContext* frame, const SkMatrix& matrix) override; @@ -34,6 +44,16 @@ class PictureLayer : public Layer { bool is_complex_ = false; bool will_change_ = false; +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + sk_sp SerializedPicture() const; + mutable sk_sp cached_serialized_picture_; + static bool Compare(DiffContext::Statistics& statistics, + const PictureLayer* l1, + const PictureLayer* l2); + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + FML_DISALLOW_COPY_AND_ASSIGN(PictureLayer); }; diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index dc9e6080c1508..b24e697265b3c 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -6,6 +6,7 @@ #include "flutter/flow/layers/picture_layer.h" +#include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/skia_gpu_object_layer_test.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" @@ -27,10 +28,10 @@ TEST_F(PictureLayerTest, PaintBeforePrerollInvalidPictureDies) { layer_offset, SkiaGPUObject(), false, false); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "picture_\\.get\\(\\)"); + "picture_\\.skia_object\\(\\)"); } -TEST_F(PictureLayerTest, PaintBeforePreollDies) { +TEST_F(PictureLayerTest, PaintBeforePrerollDies) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); auto mock_picture = SkPicture::MakePlaceholder(picture_bounds); @@ -39,7 +40,7 @@ TEST_F(PictureLayerTest, PaintBeforePreollDies) { EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(PictureLayerTest, PaintingEmptyLayerDies) { @@ -51,13 +52,11 @@ TEST_F(PictureLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -#endif TEST_F(PictureLayerTest, InvalidPictureDies) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); @@ -67,6 +66,7 @@ TEST_F(PictureLayerTest, InvalidPictureDies) { // Crashes reading a nullptr. EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); } +#endif TEST_F(PictureLayerTest, SimplePicture) { const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); @@ -81,22 +81,79 @@ TEST_F(PictureLayerTest, SimplePicture) { EXPECT_EQ(layer->paint_bounds(), picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); EXPECT_EQ(layer->picture(), mock_picture.get()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(layer->needs_painting(paint_context())); layer->Paint(paint_context()); auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, - MockCanvas::ConcatMatrixData{layer_offset_matrix}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_offset_matrix)}}, #ifndef SUPPORT_FRACTIONAL_TRANSLATION MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( - layer_offset_matrix)}}, + 1, MockCanvas::SetMatrixData{SkM44( + RasterCache::GetIntegralTransCTM(layer_offset_matrix))}}, #endif MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using PictureLayerDiffTest = DiffContextTest; + +TEST_F(PictureLayerDiffTest, SimplePicture) { + auto picture = CreatePicture(SkRect::MakeLTRB(10, 10, 60, 60), 1); + + MockLayerTree tree1; + tree1.root()->Add(CreatePictureLayer(picture)); + + auto damage = DiffLayerTree(tree1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + MockLayerTree tree2; + tree2.root()->Add(CreatePictureLayer(picture)); + + damage = DiffLayerTree(tree2, tree1); + EXPECT_TRUE(damage.frame_damage.isEmpty()); + + MockLayerTree tree3; + damage = DiffLayerTree(tree3, tree2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); +} + +TEST_F(PictureLayerDiffTest, PictureCompare) { + MockLayerTree tree1; + auto picture1 = CreatePicture(SkRect::MakeLTRB(10, 10, 60, 60), 1); + tree1.root()->Add(CreatePictureLayer(picture1)); + + auto damage = DiffLayerTree(tree1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + MockLayerTree tree2; + auto picture2 = CreatePicture(SkRect::MakeLTRB(10, 10, 60, 60), 1); + tree2.root()->Add(CreatePictureLayer(picture2)); + + damage = DiffLayerTree(tree2, tree1); + EXPECT_TRUE(damage.frame_damage.isEmpty()); + + MockLayerTree tree3; + auto picture3 = CreatePicture(SkRect::MakeLTRB(10, 10, 60, 60), 1); + // add offset + tree3.root()->Add(CreatePictureLayer(picture3, SkPoint::Make(10, 10))); + + damage = DiffLayerTree(tree3, tree2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 70, 70)); + + MockLayerTree tree4; + // different color + auto picture4 = CreatePicture(SkRect::MakeLTRB(10, 10, 60, 60), 2); + tree4.root()->Add(CreatePictureLayer(picture4, SkPoint::Make(10, 10))); + + damage = DiffLayerTree(tree4, tree3); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 70, 70)); +} + +#endif + } // namespace testing } // namespace flutter diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 80514b5213e18..285db390cc91d 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -13,11 +13,6 @@ PlatformViewLayer::PlatformViewLayer(const SkPoint& offset, void PlatformViewLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { -#if defined(LEGACY_FUCHSIA_EMBEDDER) - context->child_scene_layer_exists_below = true; - CheckForChildLayerBelow(context); -#endif - set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); @@ -27,6 +22,7 @@ void PlatformViewLayer::Preroll(PrerollContext* context, return; } context->has_platform_view = true; + set_subtree_has_platform_view(true); std::unique_ptr params = std::make_unique(matrix, size_, context->mutators_stack); @@ -36,22 +32,12 @@ void PlatformViewLayer::Preroll(PrerollContext* context, void PlatformViewLayer::Paint(PaintContext& context) const { if (context.view_embedder == nullptr) { -#if !defined(LEGACY_FUCHSIA_EMBEDDER) FML_LOG(ERROR) << "Trying to embed a platform view but the PaintContext " "does not support embedding"; -#endif return; } SkCanvas* canvas = context.view_embedder->CompositeEmbeddedView(view_id_); context.leaf_nodes_canvas = canvas; } -#if defined(LEGACY_FUCHSIA_EMBEDDER) -void PlatformViewLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "PlatformViewLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); - context.UpdateView(view_id_, offset_, size_); -} -#endif - } // namespace flutter diff --git a/flow/layers/platform_view_layer.h b/flow/layers/platform_view_layer.h index a75c69689dabf..242b3734dd3b1 100644 --- a/flow/layers/platform_view_layer.h +++ b/flow/layers/platform_view_layer.h @@ -17,10 +17,6 @@ class PlatformViewLayer : public Layer { void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - // Updates the system composited scene. - void UpdateScene(SceneUpdateContext& context) override; -#endif private: SkPoint offset_; diff --git a/flow/layers/platform_view_layer_unittests.cc b/flow/layers/platform_view_layer_unittests.cc index 98393651e27f0..58a791d7acca8 100644 --- a/flow/layers/platform_view_layer_unittests.cc +++ b/flow/layers/platform_view_layer_unittests.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/flow/layers/clip_rect_layer.h" #include "flutter/flow/layers/platform_view_layer.h" #include "flutter/flow/testing/layer_test.h" +#include "flutter/flow/testing/mock_embedder.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" @@ -26,17 +28,60 @@ TEST_F(PlatformViewLayerTest, NullViewEmbedderDoesntPrerollCompositeOrPaint) { EXPECT_EQ(layer->paint_bounds(), SkRect::MakeSize(layer_size) .makeOffset(layer_offset.fX, layer_offset.fY)); - EXPECT_TRUE(layer->needs_painting()); -#if defined(LEGACY_FUCHSIA_EMBEDDER) - EXPECT_TRUE(layer->needs_system_composite()); -#else - EXPECT_FALSE(layer->needs_system_composite()); -#endif // LEGACY_FUCHSIA_EMBEDDER + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->subtree_has_platform_view()); layer->Paint(paint_context()); EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } +TEST_F(PlatformViewLayerTest, ClippedPlatformViewPrerollsAndPaintsNothing) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(8.0f, 8.0f); + const SkRect child_clip = SkRect::MakeLTRB(20.0f, 20.0f, 40.0f, 40.0f); + const SkRect parent_clip = SkRect::MakeLTRB(50.0f, 50.0f, 80.0f, 80.0f); + const int64_t view_id = 0; + auto layer = + std::make_shared(layer_offset, layer_size, view_id); + auto child_clip_layer = + std::make_shared(child_clip, Clip::hardEdge); + auto parent_clip_layer = + std::make_shared(parent_clip, Clip::hardEdge); + parent_clip_layer->Add(child_clip_layer); + child_clip_layer->Add(layer); + + auto embedder = MockViewEmbedder(); + preroll_context()->view_embedder = &embedder; + + parent_clip_layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_TRUE(preroll_context()->has_platform_view); + EXPECT_EQ(layer->paint_bounds(), + SkRect::MakeSize(layer_size) + .makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_TRUE(child_clip_layer->needs_painting(paint_context())); + EXPECT_TRUE(parent_clip_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->subtree_has_platform_view()); + EXPECT_TRUE(child_clip_layer->subtree_has_platform_view()); + EXPECT_TRUE(parent_clip_layer->subtree_has_platform_view()); + + parent_clip_layer->Paint(paint_context()); + EXPECT_EQ(paint_context().leaf_nodes_canvas, &mock_canvas()); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ClipRectData{parent_clip, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ClipRectData{child_clip, SkClipOp::kIntersect, + MockCanvas::kHard_ClipEdgeStyle}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); +} + } // namespace testing } // namespace flutter diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index a03edfd504879..5106d2b3d8251 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -11,11 +11,27 @@ ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, SkBlendMode blend_mode) : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {} -void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { -#if defined(LEGACY_FUCHSIA_EMBEDDER) - CheckForChildLayerBelow(context); -#endif +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (shader_ != prev->shader_ || mask_rect_ != prev->mask_rect_ || + blend_mode_ != prev->blend_mode_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + + DiffChildren(context, prev); + + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); ContainerLayer::Preroll(context, matrix); @@ -23,7 +39,7 @@ void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create(context, paint_bounds(), nullptr); diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index 9bfce9644bc5a..193ba5baf1cfd 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -16,6 +16,12 @@ class ShaderMaskLayer : public ContainerLayer { const SkRect& mask_rect, SkBlendMode blend_mode); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; diff --git a/flow/layers/shader_mask_layer_unittests.cc b/flow/layers/shader_mask_layer_unittests.cc index a4118ce2dbc75..c979d54278c62 100644 --- a/flow/layers/shader_mask_layer_unittests.cc +++ b/flow/layers/shader_mask_layer_unittests.cc @@ -23,13 +23,13 @@ TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -TEST_F(ShaderMaskLayerTest, PaintBeforePreollDies) { +TEST_F(ShaderMaskLayerTest, PaintBeforePrerollDies) { const SkRect child_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); const SkPath child_path = SkPath().addRect(child_bounds); auto mock_layer = std::make_shared(child_path); @@ -39,7 +39,7 @@ TEST_F(ShaderMaskLayerTest, PaintBeforePreollDies) { EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -57,8 +57,8 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -73,7 +73,7 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) { MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( + 1, MockCanvas::ConcatMatrixData{SkM44::Translate( layer_bounds.fLeft, layer_bounds.fTop)}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawRectData{SkRect::MakeWH( @@ -90,7 +90,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) { const SkPath child_path = SkPath().addRect(child_bounds); const SkPaint child_paint = SkPaint(SkColors::kYellow); auto layer_filter = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f); auto mock_layer = std::make_shared(child_path, child_paint); auto layer = std::make_shared(layer_filter, layer_bounds, SkBlendMode::kSrc); @@ -98,7 +98,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) { layer->Preroll(preroll_context(), initial_transform); EXPECT_EQ(layer->paint_bounds(), child_bounds); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); SkPaint filter_paint; @@ -113,7 +113,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) { MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( + 1, MockCanvas::ConcatMatrixData{SkM44::Translate( layer_bounds.fLeft, layer_bounds.fTop)}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawRectData{SkRect::MakeWH( @@ -133,7 +133,7 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) { const SkPaint child_paint1 = SkPaint(SkColors::kYellow); const SkPaint child_paint2 = SkPaint(SkColors::kCyan); auto layer_filter = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); auto layer = std::make_shared(layer_filter, layer_bounds, @@ -147,9 +147,9 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) { EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), children_bounds); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -167,7 +167,7 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) { MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( + 1, MockCanvas::ConcatMatrixData{SkM44::Translate( layer_bounds.fLeft, layer_bounds.fTop)}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawRectData{SkRect::MakeWH( @@ -187,9 +187,9 @@ TEST_F(ShaderMaskLayerTest, Nested) { const SkPaint child_paint1 = SkPaint(SkColors::kYellow); const SkPaint child_paint2 = SkPaint(SkColors::kCyan); auto layer_filter1 = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f); auto layer_filter2 = - SkPerlinNoiseShader::MakeImprovedNoise(2.0f, 2.0f, 2, 2.0f); + SkPerlinNoiseShader::MakeFractalNoise(2.0f, 2.0f, 2, 2.0f); auto mock_layer1 = std::make_shared(child_path1, child_paint1); auto mock_layer2 = std::make_shared(child_path2, child_paint2); auto layer1 = std::make_shared(layer_filter1, layer_bounds, @@ -207,10 +207,10 @@ TEST_F(ShaderMaskLayerTest, Nested) { EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer1->paint_bounds(), children_bounds); EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); @@ -233,18 +233,18 @@ TEST_F(ShaderMaskLayerTest, Nested) { nullptr, 2}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{SkMatrix::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{2, + MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, MockCanvas::DrawCall{ 2, MockCanvas::DrawRectData{ SkRect::MakeWH(layer_bounds.width(), layer_bounds.height()), filter_paint2}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{SkMatrix::Translate( - layer_bounds.fLeft, layer_bounds.fTop)}}, + MockCanvas::DrawCall{1, + MockCanvas::ConcatMatrixData{SkM44::Translate( + layer_bounds.fLeft, layer_bounds.fTop)}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawRectData{ @@ -257,7 +257,7 @@ TEST_F(ShaderMaskLayerTest, Readback) { auto initial_transform = SkMatrix(); const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f); auto layer_filter = - SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f); + SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 1, 1.0f); auto layer = std::make_shared(layer_filter, layer_bounds, SkBlendMode::kSrc); @@ -268,7 +268,7 @@ TEST_F(ShaderMaskLayerTest, Readback) { // ShaderMaskLayer blocks child with readback auto mock_layer = - std::make_shared(SkPath(), SkPaint(), false, false, true); + std::make_shared(SkPath(), SkPaint(), false, true); layer->Add(mock_layer); preroll_context()->surface_needs_readback = false; layer->Preroll(preroll_context(), initial_transform); diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index 55e3ed3f16e3c..7197d52296be9 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -4,7 +4,7 @@ #include "flutter/flow/layers/texture_layer.h" -#include "flutter/flow/texture.h" +#include "flutter/common/graphics/texture.h" namespace flutter { @@ -12,26 +12,42 @@ TextureLayer::TextureLayer(const SkPoint& offset, const SkSize& size, int64_t texture_id, bool freeze, - SkFilterQuality filter_quality) + const SkSamplingOptions& sampling) : offset_(offset), size_(size), texture_id_(texture_id), freeze_(freeze), - filter_quality_(filter_quality) {} + sampling_(sampling) {} + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void TextureLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(old_layer); + auto prev = old_layer->as_texture_layer(); + // TODO(knopp) It would be nice to be able to determine that a texture is + // dirty + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(prev)); + } + context->AddLayerBounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), + size_.width(), size_.height())); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "TextureLayer::Preroll"); -#if defined(LEGACY_FUCHSIA_EMBEDDER) - CheckForChildLayerBelow(context); -#endif - set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); + context->has_texture_layer = true; } void TextureLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "TextureLayer::Paint"); + FML_DCHECK(needs_painting(context)); std::shared_ptr texture = context.texture_registry.GetTexture(texture_id_); @@ -40,7 +56,7 @@ void TextureLayer::Paint(PaintContext& context) const { return; } texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_, - context.gr_context, filter_quality_); + context.gr_context, sampling_); } } // namespace flutter diff --git a/flow/layers/texture_layer.h b/flow/layers/texture_layer.h index 250cefd10eea6..61f7149f7b4c4 100644 --- a/flow/layers/texture_layer.h +++ b/flow/layers/texture_layer.h @@ -6,7 +6,6 @@ #define FLUTTER_FLOW_LAYERS_TEXTURE_LAYER_H_ #include "flutter/flow/layers/layer.h" -#include "third_party/skia/include/core/SkFilterQuality.h" #include "third_party/skia/include/core/SkPoint.h" #include "third_party/skia/include/core/SkSize.h" @@ -18,7 +17,19 @@ class TextureLayer : public Layer { const SkSize& size, int64_t texture_id, bool freeze, - SkFilterQuality filter_quality); + const SkSamplingOptions& sampling); + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + bool IsReplacing(DiffContext* context, const Layer* layer) const override { + return layer->as_texture_layer() != nullptr; + } + + void Diff(DiffContext* context, const Layer* old_layer) override; + + const TextureLayer* as_texture_layer() const override { return this; } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; @@ -28,7 +39,7 @@ class TextureLayer : public Layer { SkSize size_; int64_t texture_id_; bool freeze_; - SkFilterQuality filter_quality_; + SkSamplingOptions sampling_; FML_DISALLOW_COPY_AND_ASSIGN(TextureLayer); }; diff --git a/flow/layers/texture_layer_unittests.cc b/flow/layers/texture_layer_unittests.cc index 525239a8e0f5f..4f6adf9944fd5 100644 --- a/flow/layers/texture_layer_unittests.cc +++ b/flow/layers/texture_layer_unittests.cc @@ -19,48 +19,63 @@ TEST_F(TextureLayerTest, InvalidTexture) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); const SkSize layer_size = SkSize::Make(8.0f, 8.0f); auto layer = std::make_shared(layer_offset, layer_size, 0, - false, kNone_SkFilterQuality); + false, SkSamplingOptions()); layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), (SkRect::MakeSize(layer_size) .makeOffset(layer_offset.fX, layer_offset.fY))); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } +#ifndef NDEBUG TEST_F(TextureLayerTest, PaintingEmptyLayerDies) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); const SkSize layer_size = SkSize::Make(0.0f, 0.0f); const int64_t texture_id = 0; auto mock_texture = std::make_shared(texture_id); auto layer = std::make_shared( - layer_offset, layer_size, texture_id, false, kNone_SkFilterQuality); + layer_offset, layer_size, texture_id, false, SkSamplingOptions()); // Ensure the texture is located by the Layer. preroll_context()->texture_registry.RegisterTexture(mock_texture); layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); - layer->Paint(paint_context()); - EXPECT_EQ(mock_texture->paint_calls(), - std::vector({MockTexture::PaintCall{ - mock_canvas(), layer->paint_bounds(), false, nullptr, - kNone_SkFilterQuality}})); - EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(context\\)"); +} + +TEST_F(TextureLayerTest, PaintBeforePrerollDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + const SkSize layer_size = SkSize::Make(8.0f, 8.0f); + const int64_t texture_id = 0; + auto mock_texture = std::make_shared(texture_id); + auto layer = std::make_shared( + layer_offset, layer_size, texture_id, false, + SkSamplingOptions(SkFilterMode::kLinear)); + + // Ensure the texture is located by the Layer. + preroll_context()->texture_registry.RegisterTexture(mock_texture); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(context\\)"); } +#endif -TEST_F(TextureLayerTest, PaintingWithLowFilterQuality) { +TEST_F(TextureLayerTest, PaintingWithLinearSampling) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); const SkSize layer_size = SkSize::Make(8.0f, 8.0f); const int64_t texture_id = 0; auto mock_texture = std::make_shared(texture_id); auto layer = std::make_shared( - layer_offset, layer_size, texture_id, false, kLow_SkFilterQuality); + layer_offset, layer_size, texture_id, false, + SkSamplingOptions(SkFilterMode::kLinear)); // Ensure the texture is located by the Layer. preroll_context()->texture_registry.RegisterTexture(mock_texture); @@ -69,13 +84,13 @@ TEST_F(TextureLayerTest, PaintingWithLowFilterQuality) { EXPECT_EQ(layer->paint_bounds(), (SkRect::MakeSize(layer_size) .makeOffset(layer_offset.fX, layer_offset.fY))); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(layer->needs_painting(paint_context())); layer->Paint(paint_context()); EXPECT_EQ(mock_texture->paint_calls(), std::vector({MockTexture::PaintCall{ mock_canvas(), layer->paint_bounds(), false, nullptr, - kLow_SkFilterQuality}})); + SkSamplingOptions(SkFilterMode::kLinear)}})); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 8fe5dd32e1e85..aff4710c23f37 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -26,6 +26,24 @@ TransformLayer::TransformLayer(const SkMatrix& transform) } } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +void TransformLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + auto* prev = static_cast(old_layer); + if (!context->IsSubtreeDirty()) { + FML_DCHECK(prev); + if (transform_ != prev->transform_) { + context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer)); + } + } + context->PushTransform(transform_); + DiffChildren(context, prev); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "TransformLayer::Preroll"); @@ -52,25 +70,9 @@ void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->mutators_stack.Pop(); } -#if defined(LEGACY_FUCHSIA_EMBEDDER) - -void TransformLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "TransformLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); - - std::optional transform; - if (!transform_.isIdentity()) { - transform.emplace(context, transform_); - } - - UpdateSceneChildren(context); -} - -#endif - void TransformLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "TransformLayer::Paint"); - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->concat(transform_); diff --git a/flow/layers/transform_layer.h b/flow/layers/transform_layer.h index 63a212f9236de..69757975df122 100644 --- a/flow/layers/transform_layer.h +++ b/flow/layers/transform_layer.h @@ -15,14 +15,16 @@ class TransformLayer : public ContainerLayer { public: TransformLayer(const SkMatrix& transform); +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + void Diff(DiffContext* context, const Layer* old_layer) override; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context) override; -#endif - private: SkMatrix transform_; diff --git a/flow/layers/transform_layer_unittests.cc b/flow/layers/transform_layer_unittests.cc index 5820e021055c5..2cb1adf35f91b 100644 --- a/flow/layers/transform_layer_unittests.cc +++ b/flow/layers/transform_layer_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/flow/layers/transform_layer.h" +#include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" @@ -20,13 +21,13 @@ TEST_F(TransformLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); - EXPECT_FALSE(layer->needs_painting()); + EXPECT_FALSE(layer->needs_painting(paint_context())); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } -TEST_F(TransformLayerTest, PaintBeforePreollDies) { +TEST_F(TransformLayerTest, PaintBeforePrerollDies) { SkPath child_path; child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); auto mock_layer = std::make_shared(child_path, SkPaint()); @@ -34,7 +35,7 @@ TEST_F(TransformLayerTest, PaintBeforePreollDies) { layer->Add(mock_layer); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -50,8 +51,8 @@ TEST_F(TransformLayerTest, Identity) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity EXPECT_EQ(mock_layer->parent_cull_rect(), cull_rect); EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(SkMatrix())})); @@ -80,8 +81,8 @@ TEST_F(TransformLayerTest, Simple) { EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer->paint_bounds(), layer_transform.mapRect(mock_layer->paint_bounds())); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix::Concat(initial_transform, layer_transform)); EXPECT_EQ(mock_layer->parent_cull_rect(), @@ -94,7 +95,7 @@ TEST_F(TransformLayerTest, Simple) { mock_canvas().draw_calls(), std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer_transform}}, + 1, MockCanvas::ConcatMatrixData{SkM44(layer_transform)}}, MockCanvas::DrawCall{ 1, MockCanvas::DrawPathData{child_path, SkPaint()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); @@ -124,9 +125,9 @@ TEST_F(TransformLayerTest, Nested) { layer2_transform.mapRect(mock_layer->paint_bounds())); EXPECT_EQ(layer1->paint_bounds(), layer1_transform.mapRect(layer2->paint_bounds())); - EXPECT_TRUE(mock_layer->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(mock_layer->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); EXPECT_EQ( mock_layer->parent_matrix(), SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), @@ -139,18 +140,18 @@ TEST_F(TransformLayerTest, Nested) { std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer1_transform}}, - MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{layer2_transform}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, SkPaint()}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer1_transform)}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{SkM44(layer2_transform)}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, SkPaint()}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(TransformLayerTest, NestedSeparated) { @@ -184,10 +185,10 @@ TEST_F(TransformLayerTest, NestedSeparated) { layer2_transform.mapRect(mock_layer2->paint_bounds())); EXPECT_EQ(mock_layer1->paint_bounds(), child_path.getBounds()); EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds); - EXPECT_TRUE(mock_layer2->needs_painting()); - EXPECT_TRUE(layer2->needs_painting()); - EXPECT_TRUE(mock_layer1->needs_painting()); - EXPECT_TRUE(layer1->needs_painting()); + EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); + EXPECT_TRUE(layer2->needs_painting(paint_context())); + EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); + EXPECT_TRUE(layer1->needs_painting(paint_context())); EXPECT_EQ(mock_layer1->parent_matrix(), SkMatrix::Concat(initial_transform, layer1_transform)); EXPECT_EQ( @@ -206,23 +207,133 @@ TEST_F(TransformLayerTest, NestedSeparated) { std::vector({Mutator(layer2_transform), Mutator(layer1_transform)})); layer1->Paint(paint_context()); - EXPECT_EQ( - mock_canvas().draw_calls(), - std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{ - 1, MockCanvas::ConcatMatrixData{layer1_transform}}, - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPathData{child_path, - SkPaint(SkColors::kBlue)}}, - MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::ConcatMatrixData{layer2_transform}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, - SkPaint(SkColors::kGreen)}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ(mock_canvas().draw_calls(), + std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer1_transform)}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path, + SkPaint(SkColors::kBlue)}}, + MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, + MockCanvas::DrawCall{ + 2, MockCanvas::ConcatMatrixData{SkM44(layer2_transform)}}, + MockCanvas::DrawCall{ + 2, MockCanvas::DrawPathData{child_path, + SkPaint(SkColors::kGreen)}}, + MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using TransformLayerLayerDiffTest = DiffContextTest; + +TEST_F(TransformLayerLayerDiffTest, Transform) { + auto path1 = SkPath().addRect(SkRect::MakeLTRB(0, 0, 50, 50)); + auto m1 = std::make_shared(path1); + + auto transform1 = + std::make_shared(SkMatrix::Translate(10, 10)); + transform1->Add(m1); + + MockLayerTree t1; + t1.root()->Add(transform1); + + auto damage = DiffLayerTree(t1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + auto transform2 = + std::make_shared(SkMatrix::Translate(20, 20)); + transform2->Add(m1); + transform2->AssignOldLayer(transform1.get()); + + MockLayerTree t2; + t2.root()->Add(transform2); + + damage = DiffLayerTree(t2, t1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 70, 70)); + + auto transform3 = + std::make_shared(SkMatrix::Translate(20, 20)); + transform3->Add(m1); + transform3->AssignOldLayer(transform2.get()); + + MockLayerTree t3; + t3.root()->Add(transform3); + + damage = DiffLayerTree(t3, t2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeEmpty()); +} + +TEST_F(TransformLayerLayerDiffTest, TransformNested) { + auto path1 = SkPath().addRect(SkRect::MakeLTRB(0, 0, 50, 50)); + auto m1 = CreateContainerLayer(std::make_shared(path1)); + auto m2 = CreateContainerLayer(std::make_shared(path1)); + auto m3 = CreateContainerLayer(std::make_shared(path1)); + + auto transform1 = std::make_shared(SkMatrix::Scale(2.0, 2.0)); + + auto transform1_1 = + std::make_shared(SkMatrix::Translate(10, 10)); + transform1_1->Add(m1); + transform1->Add(transform1_1); + + auto transform1_2 = + std::make_shared(SkMatrix::Translate(100, 100)); + transform1_2->Add(m2); + transform1->Add(transform1_2); + + auto transform1_3 = + std::make_shared(SkMatrix::Translate(200, 200)); + transform1_3->Add(m3); + transform1->Add(transform1_3); + + MockLayerTree l1; + l1.root()->Add(transform1); + + auto damage = DiffLayerTree(l1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 500, 500)); + + auto transform2 = std::make_shared(SkMatrix::Scale(2.0, 2.0)); + + auto transform2_1 = + std::make_shared(SkMatrix::Translate(10, 10)); + transform2_1->Add(m1); + transform2_1->AssignOldLayer(transform1_1.get()); + transform2->Add(transform2_1); + + // Offset 1px from transform1_2 so that they're not the same + auto transform2_2 = + std::make_shared(SkMatrix::Translate(100, 101)); + transform2_2->Add(m2); + transform2_2->AssignOldLayer(transform1_2.get()); + transform2->Add(transform2_2); + + auto transform2_3 = + std::make_shared(SkMatrix::Translate(200, 200)); + transform2_3->Add(m3); + transform2_3->AssignOldLayer(transform1_3.get()); + transform2->Add(transform2_3); + + MockLayerTree l2; + l2.root()->Add(transform2); + + damage = DiffLayerTree(l2, l1); + + // transform2 has not transform1 assigned as old layer, so it should be + // invalidated completely + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 500, 500)); + + // now diff the tree properly, the only difference being transform2_2 and + // transform_2_1 + transform2->AssignOldLayer(transform1.get()); + damage = DiffLayerTree(l2, l1); + + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 200, 300, 302)); +} + +#endif + } // namespace testing } // namespace flutter diff --git a/flow/paint_region.cc b/flow/paint_region.cc new file mode 100644 index 0000000000000..760259a12c422 --- /dev/null +++ b/flow/paint_region.cc @@ -0,0 +1,21 @@ +// 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/flow/paint_region.h" + +namespace flutter { + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +SkRect PaintRegion::ComputeBounds() const { + SkRect res = SkRect::MakeEmpty(); + for (const auto& r : *this) { + res.join(r); + } + return res; +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +} // namespace flutter diff --git a/flow/paint_region.h b/flow/paint_region.h new file mode 100644 index 0000000000000..0750e5f7ffa62 --- /dev/null +++ b/flow/paint_region.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "flutter/fml/logging.h" +#include "third_party/skia/include/core/SkRect.h" + +namespace flutter { + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +// Corresponds to area on the screen where the layer subtree has painted to. +// +// The area is used when adding damage of removed or dirty layer to overall +// damage. +// +// Because there is a PaintRegion for each layer, it must be able to represent +// the area with minimal overhead. This is accomplished by having one +// vector shared between all paint regions, and each paint region +// keeping begin and end index of rects relevant to particular subtree. +// +// All rects are in screen coordinates. +class PaintRegion { + public: + PaintRegion() = default; + PaintRegion(std::shared_ptr> rects, + size_t from, + size_t to, + bool has_readback) + : rects_(rects), from_(from), to_(to), has_readback_(has_readback) {} + + std::vector::const_iterator begin() const { + FML_DCHECK(is_valid()); + return rects_->begin() + from_; + } + + std::vector::const_iterator end() const { + FML_DCHECK(is_valid()); + return rects_->begin() + to_; + } + + // Compute bounds for this region + SkRect ComputeBounds() const; + + bool is_valid() const { return rects_ != nullptr; } + + // Returns true if there is a layer in subtree represented by this region + // that performs readback + bool has_readback() const { return has_readback_; } + + private: + std::shared_ptr> rects_; + size_t from_ = 0; + size_t to_ = 0; + bool has_readback_ = false; +}; + +#endif + +} // namespace flutter diff --git a/flow/paint_utils.cc b/flow/paint_utils.cc index 38fc17979a0c3..647ca3da0bae4 100644 --- a/flow/paint_utils.cc +++ b/flow/paint_utils.cc @@ -20,7 +20,8 @@ sk_sp CreateCheckerboardShader(SkColor c1, SkColor c2, int size) { bm.eraseColor(c1); bm.eraseArea(SkIRect::MakeLTRB(0, 0, size, size), c2); bm.eraseArea(SkIRect::MakeLTRB(size, size, 2 * size, 2 * size), c2); - return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); + return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, + SkSamplingOptions()); } } // anonymous namespace diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 6db5a8103f30b..d1671a1c1ab13 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -32,7 +32,8 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { std::abs(bounds.size().width() - image_->dimensions().width()) <= 1 && std::abs(bounds.size().height() - image_->dimensions().height()) <= 1); canvas.resetMatrix(); - canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint); + canvas.drawImage(image_, bounds.fLeft, bounds.fTop, SkSamplingOptions(), + paint); } RasterCache::RasterCache(size_t access_threshold, @@ -55,6 +56,28 @@ static bool CanRasterizePicture(SkPicture* picture) { if (!cull_rect.isFinite()) { // Cannot attempt to rasterize into an infinitely large surface. + FML_LOG(INFO) << "Attempted to raster cache non-finite picture"; + return false; + } + + return true; +} + +static bool CanRasterizeDisplayList(DisplayList* display_list) { + if (display_list == nullptr) { + return false; + } + + const SkRect cull_rect = display_list->bounds(); + + if (cull_rect.isEmpty()) { + // No point in ever rasterizing an empty display list. + return false; + } + + if (!cull_rect.isFinite()) { + // Cannot attempt to rasterize into an infinitely large surface. + FML_LOG(INFO) << "Attempted to raster cache non-finite display list"; return false; } @@ -87,6 +110,32 @@ static bool IsPictureWorthRasterizing(SkPicture* picture, return picture->approximateOpCount() > 5; } +static bool IsDisplayListWorthRasterizing(DisplayList* display_list, + bool will_change, + bool is_complex) { + if (will_change) { + // If the display list is going to change in the future, there is no point + // in doing to extra work to rasterize. + return false; + } + + if (!CanRasterizeDisplayList(display_list)) { + // No point in deciding whether the display list is worth rasterizing if it + // cannot be rasterized at all. + return false; + } + + if (is_complex) { + // The caller seems to have extra information about the display list and + // thinks the display list is always worth rasterizing. + return true; + } + + // TODO(abarth): We should find a better heuristic here that lets us avoid + // wasting memory on trivial layers that are easy to re-rasterize every frame. + return display_list->op_count() > 5; +} + /// @note Procedure doesn't copy all closures. static std::unique_ptr Rasterize( GrDirectContext* context, @@ -135,6 +184,17 @@ std::unique_ptr RasterCache::RasterizePicture( [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); } +std::unique_ptr RasterCache::RasterizeDisplayList( + DisplayList* display_list, + GrDirectContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const { + return Rasterize(context, ctm, dst_color_space, checkerboard, + display_list->bounds(), + [=](SkCanvas* canvas) { display_list->RenderTo(canvas); }); +} + void RasterCache::Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm) { @@ -158,6 +218,7 @@ std::unique_ptr RasterCache::RasterizeLayer( SkISize canvas_size = canvas->getBaseLayerSize(); SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.setMatrix(canvas->getTotalMatrix()); internal_nodes_canvas.addCanvas(canvas); Layer::PaintContext paintContext = { /* internal_nodes_canvas= */ static_cast( @@ -171,7 +232,7 @@ std::unique_ptr RasterCache::RasterizeLayer( context->has_platform_view ? nullptr : context->raster_cache, context->checkerboard_offscreen_layers, context->frame_device_pixel_ratio}; - if (layer->needs_painting()) { + if (layer->needs_painting(paintContext)) { layer->Paint(paintContext); } }); @@ -221,6 +282,52 @@ bool RasterCache::Prepare(GrDirectContext* context, return true; } +bool RasterCache::Prepare(GrDirectContext* context, + DisplayList* display_list, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change) { + // Disabling caching when access_threshold is zero is historic behavior. + if (access_threshold_ == 0) { + return false; + } + if (picture_cached_this_frame_ >= picture_cache_limit_per_frame_) { + return false; + } + if (!IsDisplayListWorthRasterizing(display_list, will_change, is_complex)) { + // We only deal with display lists that are worthy of rasterization. + return false; + } + + // Decompose the matrix (once) for all subsequent operations. We want to make + // sure to avoid volumetric distortions while accounting for scaling. + const MatrixDecomposition matrix(transformation_matrix); + + if (!matrix.IsValid()) { + // The matrix was singular. No point in going further. + return false; + } + + DisplayListRasterCacheKey cache_key(display_list->unique_id(), + transformation_matrix); + + // Creates an entry, if not present prior. + Entry& entry = display_list_cache_[cache_key]; + if (entry.access_count < access_threshold_) { + // Frame threshold has not yet been reached. + return false; + } + + if (!entry.image) { + entry.image = + RasterizeDisplayList(display_list, context, transformation_matrix, + dst_color_space, checkerboard_images_); + picture_cached_this_frame_++; + } + return true; +} + bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix()); auto it = picture_cache_.find(cache_key); @@ -240,6 +347,27 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { return false; } +bool RasterCache::Draw(const DisplayList& display_list, + SkCanvas& canvas) const { + DisplayListRasterCacheKey cache_key(display_list.unique_id(), + canvas.getTotalMatrix()); + auto it = display_list_cache_.find(cache_key); + if (it == display_list_cache_.end()) { + return false; + } + + Entry& entry = it->second; + entry.access_count++; + entry.used_this_frame = true; + + if (entry.image) { + entry.image->draw(canvas, nullptr); + return true; + } + + return false; +} + bool RasterCache::Draw(const Layer* layer, SkCanvas& canvas, SkPaint* paint) const { @@ -263,6 +391,7 @@ bool RasterCache::Draw(const Layer* layer, void RasterCache::SweepAfterFrame() { SweepOneCacheAfterFrame(picture_cache_); + SweepOneCacheAfterFrame(display_list_cache_); SweepOneCacheAfterFrame(layer_cache_); picture_cached_this_frame_ = 0; TraceStatsToTimeline(); @@ -270,11 +399,13 @@ void RasterCache::SweepAfterFrame() { void RasterCache::Clear() { picture_cache_.clear(); + display_list_cache_.clear(); layer_cache_.clear(); } size_t RasterCache::GetCachedEntriesCount() const { - return layer_cache_.size() + picture_cache_.size(); + return layer_cache_.size() + picture_cache_.size() + + display_list_cache_.size(); } size_t RasterCache::GetLayerCachedEntriesCount() const { @@ -285,6 +416,10 @@ size_t RasterCache::GetPictureCachedEntriesCount() const { return picture_cache_.size(); } +size_t RasterCache::GetDisplayListCachedEntriesCount() const { + return display_list_cache_.size(); +} + void RasterCache::SetCheckboardCacheImages(bool checkerboard) { if (checkerboard_images_ == checkerboard) { return; @@ -303,7 +438,10 @@ void RasterCache::TraceStatsToTimeline() const { "LayerCount", layer_cache_.size(), "LayerMBytes", EstimateLayerCacheByteSize() / kMegaByteSizeInBytes, "PictureCount", picture_cache_.size(), "PictureMBytes", - EstimatePictureCacheByteSize() / kMegaByteSizeInBytes); + EstimatePictureCacheByteSize() / kMegaByteSizeInBytes, + "DisplayListCount", display_list_cache_.size(), + "DisplayListMBytes", + EstimateDisplayListCacheByteSize() / kMegaByteSizeInBytes); #endif // !FLUTTER_RELEASE } @@ -328,4 +466,14 @@ size_t RasterCache::EstimatePictureCacheByteSize() const { return picture_cache_bytes; } +size_t RasterCache::EstimateDisplayListCacheByteSize() const { + size_t display_list_cache_bytes = 0; + for (const auto& item : display_list_cache_) { + if (item.second.image) { + display_list_cache_bytes += item.second.image->image_bytes(); + } + } + return display_list_cache_bytes; +} + } // namespace flutter diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 297a58482f056..a440ade7919d9 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -8,6 +8,7 @@ #include #include +#include "flutter/flow/display_list.h" #include "flutter/flow/raster_cache_key.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" @@ -74,6 +75,12 @@ class RasterCache { const SkMatrix& ctm, SkColorSpace* dst_color_space, bool checkerboard) const; + virtual std::unique_ptr RasterizeDisplayList( + DisplayList* display_list, + GrDirectContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const; /** * @brief Rasterize an engine Layer and produce a RasterCacheResult @@ -138,6 +145,12 @@ class RasterCache { SkColorSpace* dst_color_space, bool is_complex, bool will_change); + bool Prepare(GrDirectContext* context, + DisplayList* display_list, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change); void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); @@ -146,10 +159,15 @@ class RasterCache { // Return true if it's found and drawn. bool Draw(const SkPicture& picture, SkCanvas& canvas) const; + // Find the raster cache for the display list and draw it to the canvas. + // + // Return true if it's found and drawn. + bool Draw(const DisplayList& display_list, SkCanvas& canvas) const; + // Find the raster cache for the layer and draw it to the canvas. // - // Addional paint can be given to change how the raster cache is drawn (e.g., - // draw the raster cache with some opacity). + // Additional paint can be given to change how the raster cache is drawn + // (e.g., draw the raster cache with some opacity). // // Return true if the layer raster cache is found and drawn. bool Draw(const Layer* layer, @@ -168,6 +186,8 @@ class RasterCache { size_t GetPictureCachedEntriesCount() const; + size_t GetDisplayListCachedEntriesCount() const; + /** * @brief Estimate how much memory is used by picture raster cache entries in * bytes. @@ -178,6 +198,16 @@ class RasterCache { */ size_t EstimatePictureCacheByteSize() const; + /** + * @brief Estimate how much memory is used by display list raster cache + * entries in bytes. + * + * Only SkImage's memory usage is counted as other objects are often much + * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to + * estimate the SkImage memory usage. + */ + size_t EstimateDisplayListCacheByteSize() const; + /** * @brief Estimate how much memory is used by layer raster cache entries in * bytes. @@ -216,6 +246,7 @@ class RasterCache { const size_t picture_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; mutable PictureRasterCacheKey::Map picture_cache_; + mutable DisplayListRasterCacheKey::Map display_list_cache_; mutable LayerRasterCacheKey::Map layer_cache_; bool checkerboard_images_; diff --git a/flow/raster_cache_key.h b/flow/raster_cache_key.h index cc4c5fe2b0c7d..21d6bfe3dfdcb 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -52,6 +52,9 @@ class RasterCacheKey { // The ID is the uint32_t picture uniqueID using PictureRasterCacheKey = RasterCacheKey; +// The ID is the uint32_t DisplayList uniqueID +using DisplayListRasterCacheKey = RasterCacheKey; + class Layer; // The ID is the uint64_t layer unique_id diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc deleted file mode 100644 index 3edd9c4d9fe26..0000000000000 --- a/flow/scene_update_context.cc +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/scene_update_context.h" - -#include -#include - -#include "flutter/flow/layers/layer.h" -#include "flutter/flow/matrix_decomposition.h" -#include "flutter/flow/view_holder.h" -#include "flutter/fml/trace_event.h" -#include "include/core/SkColor.h" - -namespace flutter { -namespace { - -void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node, - const SkRect& bounds) { - const float top = bounds.top(); - const float bottom = bounds.bottom(); - const float left = bounds.left(); - const float right = bounds.right(); - - // We will generate 4 oriented planes, one for each edge of the bounding rect. - std::vector clip_planes; - clip_planes.resize(4); - - // Top plane. - clip_planes[0].dist = top; - clip_planes[0].dir.x = 0.f; - clip_planes[0].dir.y = 1.f; - clip_planes[0].dir.z = 0.f; - - // Bottom plane. - clip_planes[1].dist = -bottom; - clip_planes[1].dir.x = 0.f; - clip_planes[1].dir.y = -1.f; - clip_planes[1].dir.z = 0.f; - - // Left plane. - clip_planes[2].dist = left; - clip_planes[2].dir.x = 1.f; - clip_planes[2].dir.y = 0.f; - clip_planes[2].dir.z = 0.f; - - // Right plane. - clip_planes[3].dist = -right; - clip_planes[3].dir.x = -1.f; - clip_planes[3].dir.y = 0.f; - clip_planes[3].dir.z = 0.f; - - entity_node.SetClipPlanes(std::move(clip_planes)); -} - -void SetMaterialColor(scenic::Material& material, - SkColor color, - SkAlpha opacity) { - const SkAlpha color_alpha = static_cast( - ((float)SkColorGetA(color) * (float)opacity) / 255.0f); - material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), - color_alpha); -} - -} // namespace - -SceneUpdateContext::SceneUpdateContext(std::string debug_label, - fuchsia::ui::views::ViewToken view_token, - scenic::ViewRefPair view_ref_pair, - SessionWrapper& session) - : session_(session), - root_view_(session_.get(), - std::move(view_token), - std::move(view_ref_pair.control_ref), - std::move(view_ref_pair.view_ref), - debug_label), - root_node_(session_.get()) { - root_view_.AddChild(root_node_); - root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask); - - session_.Present(); -} - -std::vector SceneUpdateContext::GetPaintTasks() { - std::vector frame_paint_tasks = std::move(paint_tasks_); - - paint_tasks_.clear(); - - return frame_paint_tasks; -} - -void SceneUpdateContext::EnableWireframe(bool enable) { - session_.get()->Enqueue( - scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable)); -} - -void SceneUpdateContext::Reset() { - paint_tasks_.clear(); - top_entity_ = nullptr; - top_scale_x_ = 1.f; - top_scale_y_ = 1.f; - top_elevation_ = 0.f; - next_elevation_ = 0.f; - alpha_ = 1.f; - - // We are going to be sending down a fresh node hierarchy every frame. So just - // enqueue a detach op on the imported root node. - session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id())); -} - -void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, - const SkRRect& rrect, - SkColor color, - SkAlpha opacity, - const SkRect& paint_bounds, - std::vector paint_layers) { - // We don't need a shape if the frame is zero size. - if (rrect.isEmpty()) - return; - - // Frames always clip their children. - SkRect shape_bounds = rrect.getBounds(); - SetEntityNodeClipPlanes(entity_node, shape_bounds); - - // TODO(SCN-137): Need to be able to express the radii as vectors. - scenic::ShapeNode shape_node(session_.get()); - scenic::Rectangle shape(session_.get(), rrect.width(), rrect.height()); - shape_node.SetShape(shape); - shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(), - shape_bounds.height() * 0.5f + shape_bounds.top(), - 0.f); - - // Check whether the painted layers will be visible. - if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds)) - paint_layers.clear(); - - scenic::Material material(session_.get()); - shape_node.SetMaterial(material); - entity_node.AddChild(shape_node); - - // Check whether a solid color will suffice. - if (paint_layers.empty()) { - SetMaterialColor(material, color, opacity); - } else { - // The final shape's color is material_color * texture_color. The passed in - // material color was already used as a background when generating the - // texture, so set the model color to |SK_ColorWHITE| in order to allow - // using the texture's color unmodified. - SetMaterialColor(material, SK_ColorWHITE, opacity); - - // Enqueue a paint task for these layers, to apply a texture to the whole - // shape. - // - // The task uses the |shape_bounds| as its rendering bounds instead of the - // |paint_bounds|. If the paint_bounds is large than the shape_bounds it - // will be clipped. - paint_tasks_.emplace_back(PaintTask{.paint_bounds = shape_bounds, - .scale_x = top_scale_x_, - .scale_y = top_scale_y_, - .background_color = color, - .material = std::move(material), - .layers = std::move(paint_layers)}); - } -} - -void SceneUpdateContext::UpdateView(int64_t view_id, - const SkPoint& offset, - const SkSize& size, - std::optional override_hit_testable) { - auto* view_holder = ViewHolder::FromId(view_id); - FML_DCHECK(view_holder); - - if (size.width() > 0.f && size.height() > 0.f) { - view_holder->SetProperties(size.width(), size.height(), 0, 0, 0, 0, - view_holder->focusable()); - } - - bool hit_testable = override_hit_testable.has_value() - ? *override_hit_testable - : view_holder->hit_testable(); - view_holder->UpdateScene(session_.get(), top_entity_->embedder_node(), offset, - size, SkScalarRoundToInt(alphaf() * 255), - hit_testable); - - // Assume embedded views are 10 "layers" wide. - next_elevation_ += 10 * kScenicZElevationBetweenLayers; -} - -void SceneUpdateContext::CreateView(int64_t view_id, - bool hit_testable, - bool focusable) { - zx_handle_t handle = (zx_handle_t)view_id; - flutter::ViewHolder::Create(handle, nullptr, - scenic::ToViewHolderToken(zx::eventpair(handle)), - nullptr); - auto* view_holder = ViewHolder::FromId(view_id); - FML_DCHECK(view_holder); - - view_holder->set_hit_testable(hit_testable); - view_holder->set_focusable(focusable); -} - -void SceneUpdateContext::UpdateView(int64_t view_id, - bool hit_testable, - bool focusable) { - auto* view_holder = ViewHolder::FromId(view_id); - FML_DCHECK(view_holder); - - view_holder->set_hit_testable(hit_testable); - view_holder->set_focusable(focusable); -} - -void SceneUpdateContext::DestroyView(int64_t view_id) { - ViewHolder::Destroy(view_id); -} - -SceneUpdateContext::Entity::Entity(SceneUpdateContext& context) - : context_(context), - previous_entity_(context.top_entity_), - entity_node_(context.session_.get()) { - context.top_entity_ = this; -} - -SceneUpdateContext::Entity::~Entity() { - if (previous_entity_) { - previous_entity_->embedder_node().AddChild(entity_node_); - } else { - context_.root_node_.AddChild(entity_node_); - } - - FML_DCHECK(context_.top_entity_ == this); - context_.top_entity_ = previous_entity_; -} - -SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, - const SkMatrix& transform) - : Entity(context), - previous_scale_x_(context.top_scale_x_), - previous_scale_y_(context.top_scale_y_) { - entity_node().SetLabel("flutter::Transform"); - if (!transform.isIdentity()) { - // TODO(SCN-192): The perspective and shear components in the matrix - // are not handled correctly. - MatrixDecomposition decomposition(transform); - if (decomposition.IsValid()) { - // Don't allow clients to control the z dimension; we control that - // instead to make sure layers appear in proper order. - entity_node().SetTranslation(decomposition.translation().x, // - decomposition.translation().y, // - 0.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; - - entity_node().SetRotation(decomposition.rotation().x, // - decomposition.rotation().y, // - decomposition.rotation().z, // - decomposition.rotation().w // - ); - } - } -} - -SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, - float scale_x, - float scale_y, - float scale_z) - : Entity(context), - previous_scale_x_(context.top_scale_x_), - previous_scale_y_(context.top_scale_y_) { - entity_node().SetLabel("flutter::Transform"); - if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) { - entity_node().SetScale(scale_x, scale_y, scale_z); - context.top_scale_x_ *= scale_x; - context.top_scale_y_ *= scale_y; - } -} - -SceneUpdateContext::Transform::~Transform() { - context().top_scale_x_ = previous_scale_x_; - context().top_scale_y_ = previous_scale_y_; -} - -SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, - const SkRRect& rrect, - SkColor color, - SkAlpha opacity, - std::string label) - : Entity(context), - previous_elevation_(context.top_elevation_), - rrect_(rrect), - color_(color), - opacity_(opacity), - opacity_node_(context.session_.get()), - paint_bounds_(SkRect::MakeEmpty()) { - // Increment elevation trackers before calculating any local elevation. - // |UpdateView| can modify context.next_elevation_, which is why it is - // neccesary to track this addtional state. - context.top_elevation_ += kScenicZElevationBetweenLayers; - context.next_elevation_ += kScenicZElevationBetweenLayers; - - float local_elevation = context.next_elevation_ - previous_elevation_; - entity_node().SetTranslation(0.f, 0.f, -local_elevation); - entity_node().SetLabel(label); - entity_node().AddChild(opacity_node_); - - // Scenic currently lacks an API to enable rendering of alpha channel; alpha - // channels are only rendered if there is a OpacityNode higher in the tree - // with opacity != 1. For now, clamp to a infinitesimally smaller value than - // 1, which does not cause visual problems in practice. - opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f)); -} - -SceneUpdateContext::Frame::~Frame() { - context().top_elevation_ = previous_elevation_; - - // Add a part which represents the frame's geometry for clipping purposes - context().CreateFrame(entity_node(), rrect_, color_, opacity_, paint_bounds_, - std::move(paint_layers_)); -} - -void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { - FML_DCHECK(layer->needs_painting()); - paint_layers_.push_back(layer); - paint_bounds_.join(layer->paint_bounds()); -} - -SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, - const SkRect& shape_bounds) - : Entity(context) { - entity_node().SetLabel("flutter::Clip"); - SetEntityNodeClipPlanes(entity_node(), shape_bounds); -} - -} // namespace flutter diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h deleted file mode 100644 index f15c59fa7f6ea..0000000000000 --- a/flow/scene_update_context.h +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ -#define FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "flutter/flow/embedded_views.h" -#include "flutter/fml/logging.h" -#include "flutter/fml/macros.h" -#include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/core/SkSurface.h" - -namespace flutter { - -class Layer; - -// Scenic currently lacks an API to enable rendering of alpha channel; this only -// happens if there is a OpacityNode higher in the tree with opacity != 1. For -// now, clamp to a infinitesimally smaller value than 1, which does not cause -// visual problems in practice. -constexpr float kOneMinusEpsilon = 1 - FLT_EPSILON; - -// How much layers are separated in Scenic z elevation. -constexpr float kScenicZElevationBetweenLayers = 10.f; - -class SessionWrapper { - public: - virtual ~SessionWrapper() {} - - virtual scenic::Session* get() = 0; - virtual void Present() = 0; -}; - -class SceneUpdateContext : public flutter::ExternalViewEmbedder { - public: - class Entity { - public: - Entity(SceneUpdateContext& context); - virtual ~Entity(); - - SceneUpdateContext& context() { return context_; } - scenic::EntityNode& entity_node() { return entity_node_; } - virtual scenic::ContainerNode& embedder_node() { return entity_node_; } - - private: - SceneUpdateContext& context_; - Entity* const previous_entity_; - - scenic::EntityNode entity_node_; - }; - - class Transform : public Entity { - public: - Transform(SceneUpdateContext& context, const SkMatrix& transform); - Transform(SceneUpdateContext& context, - float scale_x, - float scale_y, - float scale_z); - virtual ~Transform(); - - private: - float const previous_scale_x_; - float const previous_scale_y_; - }; - - class Frame : public Entity { - public: - // When layer is not nullptr, the frame is associated with a layer subtree - // rooted with that layer. The frame may then create a surface that will be - // retained for that layer. - Frame(SceneUpdateContext& context, - const SkRRect& rrect, - SkColor color, - SkAlpha opacity, - std::string label); - virtual ~Frame(); - - scenic::ContainerNode& embedder_node() override { return opacity_node_; } - - void AddPaintLayer(Layer* layer); - - private: - const float previous_elevation_; - - const SkRRect rrect_; - SkColor const color_; - SkAlpha const opacity_; - - scenic::OpacityNodeHACK opacity_node_; - std::vector paint_layers_; - SkRect paint_bounds_; - }; - - class Clip : public Entity { - public: - Clip(SceneUpdateContext& context, const SkRect& shape_bounds); - ~Clip() = default; - }; - - struct PaintTask { - SkRect paint_bounds; - SkScalar scale_x; - SkScalar scale_y; - SkColor background_color; - scenic::Material material; - std::vector layers; - }; - - SceneUpdateContext(std::string debug_label, - fuchsia::ui::views::ViewToken view_token, - scenic::ViewRefPair view_ref_pair, - SessionWrapper& session); - ~SceneUpdateContext() = default; - - scenic::ContainerNode& root_node() { return root_node_; } - - // The cumulative alpha value based on all the parent OpacityLayers. - void set_alphaf(float alpha) { alpha_ = alpha; } - float alphaf() { return alpha_; } - - // Returns all `PaintTask`s generated for the current frame. - std::vector GetPaintTasks(); - - // Enable/disable wireframe rendering around the root view bounds. - void EnableWireframe(bool enable); - - // Reset state for a new frame. - void Reset(); - - // |ExternalViewEmbedder| - SkCanvas* GetRootCanvas() override { return nullptr; } - - // |ExternalViewEmbedder| - void CancelFrame() override {} - - // |ExternalViewEmbedder| - void BeginFrame( - SkISize frame_size, - GrDirectContext* context, - double device_pixel_ratio, - fml::RefPtr raster_thread_merger) override {} - - // |ExternalViewEmbedder| - void PrerollCompositeEmbeddedView( - int view_id, - std::unique_ptr params) override {} - - // |ExternalViewEmbedder| - std::vector GetCurrentCanvases() override { - return std::vector(); - } - - // |ExternalViewEmbedder| - virtual SkCanvas* CompositeEmbeddedView(int view_id) override { - return nullptr; - } - - void CreateView(int64_t view_id, bool hit_testable, bool focusable); - void UpdateView(int64_t view_id, bool hit_testable, bool focusable); - void DestroyView(int64_t view_id); - void UpdateView(int64_t view_id, - const SkPoint& offset, - const SkSize& size, - std::optional override_hit_testable = std::nullopt); - - private: - void CreateFrame(scenic::EntityNode& entity_node, - const SkRRect& rrect, - SkColor color, - SkAlpha opacity, - const SkRect& paint_bounds, - std::vector paint_layers); - - SessionWrapper& session_; - - scenic::View root_view_; - scenic::EntityNode root_node_; - - std::vector paint_tasks_; - - Entity* top_entity_ = nullptr; - float top_scale_x_ = 1.f; - float top_scale_y_ = 1.f; - float top_elevation_ = 0.f; - - float next_elevation_ = 0.f; - float alpha_ = 1.f; - - FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext); -}; - -} // namespace flutter - -#endif // FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 662d560fc9eff..38d29dcacdaf5 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -69,7 +69,7 @@ class SkiaGPUObject { SkiaGPUObject& operator=(SkiaGPUObject&&) = default; - sk_sp get() const { return object_; } + sk_sp skia_object() const { return object_; } void reset() { if (object_ && queue_) { diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index 3f6761dfdaf16..faf0f3acec0ab 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -106,7 +106,7 @@ TEST_F(SkiaGpuObjectTest, ObjectReset) { sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); // Verify that explicitly resetting the GPU object queues and unref. sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); + ASSERT_EQ(sk_object.skia_object(), nullptr); latch->Wait(); ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); } @@ -119,9 +119,9 @@ TEST_F(SkiaGpuObjectTest, ObjectResetTwice) { sk_make_sp(latch, &dtor_task_queue_id), unref_queue()); sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); + ASSERT_EQ(sk_object.skia_object(), nullptr); sk_object.reset(); - ASSERT_EQ(sk_object.get(), nullptr); + ASSERT_EQ(sk_object.skia_object(), nullptr); latch->Wait(); ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); diff --git a/flow/surface.cc b/flow/surface.cc index 7dbdfb06753cf..7dbe56c1e8e78 100644 --- a/flow/surface.cc +++ b/flow/surface.cc @@ -10,10 +10,6 @@ Surface::Surface() = default; Surface::~Surface() = default; -flutter::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { - return nullptr; -} - std::unique_ptr Surface::MakeRenderContextCurrent() { return std::make_unique(true); } diff --git a/flow/surface.h b/flow/surface.h index 610cc43232bba..21b248c074d4c 100644 --- a/flow/surface.h +++ b/flow/surface.h @@ -7,8 +7,8 @@ #include +#include "flutter/common/graphics/gl_context_switch.h" #include "flutter/flow/embedded_views.h" -#include "flutter/flow/gl_context_switch.h" #include "flutter/flow/surface_frame.h" #include "flutter/fml/macros.h" @@ -29,8 +29,6 @@ class Surface { virtual GrDirectContext* GetContext() = 0; - virtual flutter::ExternalViewEmbedder* GetExternalViewEmbedder(); - virtual std::unique_ptr MakeRenderContextCurrent(); virtual bool ClearRenderContext(); diff --git a/flow/surface_frame.h b/flow/surface_frame.h index d2326508c9c84..ad4b3c95a95d2 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -7,7 +7,7 @@ #include -#include "flutter/flow/gl_context_switch.h" +#include "flutter/common/graphics/gl_context_switch.h" #include "flutter/fml/macros.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc new file mode 100644 index 0000000000000..74f26f780c659 --- /dev/null +++ b/flow/testing/diff_context_test.cc @@ -0,0 +1,71 @@ +// 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 "diff_context_test.h" + +namespace flutter { +namespace testing { + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +DiffContextTest::DiffContextTest() + : unref_queue_(fml::MakeRefCounted( + GetCurrentTaskRunner(), + fml::TimeDelta::FromSeconds(0))) {} + +Damage DiffContextTest::DiffLayerTree(MockLayerTree& layer_tree, + const MockLayerTree& old_layer_tree, + const SkIRect& additional_damage) { + FML_CHECK(layer_tree.size() == old_layer_tree.size()); + + DiffContext dc(layer_tree.size(), 1, layer_tree.paint_region_map(), + old_layer_tree.paint_region_map()); + dc.PushCullRect( + SkRect::MakeIWH(layer_tree.size().width(), layer_tree.size().height())); + layer_tree.root()->Diff(&dc, old_layer_tree.root()); + return dc.ComputeDamage(additional_damage); +} + +sk_sp DiffContextTest::CreatePicture(const SkRect& bounds, + uint32_t color) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = recorder.beginRecording(bounds); + recording_canvas->drawRect(bounds, SkPaint(SkColor4f::FromBytes_RGBA(color))); + return recorder.finishRecordingAsPicture(); +} + +std::shared_ptr DiffContextTest::CreatePictureLayer( + sk_sp picture, + const SkPoint& offset) { + return std::make_shared( + offset, SkiaGPUObject(picture, unref_queue()), false, false); +} + +sk_sp DiffContextTest::CreateDisplayList(const SkRect& bounds, + SkColor color) { + DisplayListBuilder builder; + builder.setColor(color); + builder.drawRect(bounds); + return builder.Build(); +} + +std::shared_ptr DiffContextTest::CreateDisplayListLayer( + sk_sp display_list, + const SkPoint& offset) { + return std::make_shared(offset, display_list, false, false); +} + +std::shared_ptr DiffContextTest::CreateContainerLayer( + std::initializer_list> layers) { + auto res = std::make_shared(); + for (const auto& l : layers) { + res->Add(l); + } + return res; +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/diff_context_test.h b/flow/testing/diff_context_test.h new file mode 100644 index 0000000000000..75341146d98e6 --- /dev/null +++ b/flow/testing/diff_context_test.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/display_list_layer.h" +#include "flutter/flow/layers/picture_layer.h" +#include "flutter/flow/testing/skia_gpu_object_layer_test.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" + +namespace flutter { +namespace testing { + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +class MockLayerTree { + public: + explicit MockLayerTree(SkISize size = SkISize::Make(1000, 1000)) + : root_(std::make_shared()), size_(size) {} + + ContainerLayer* root() { return root_.get(); } + const ContainerLayer* root() const { return root_.get(); } + + PaintRegionMap& paint_region_map() { return paint_region_map_; } + const PaintRegionMap& paint_region_map() const { return paint_region_map_; } + + const SkISize& size() const { return size_; } + + private: + std::shared_ptr root_; + PaintRegionMap paint_region_map_; + SkISize size_; +}; + +class DiffContextTest : public ThreadTest { + public: + DiffContextTest(); + + Damage DiffLayerTree(MockLayerTree& layer_tree, + const MockLayerTree& old_layer_tree, + const SkIRect& additional_damage = SkIRect::MakeEmpty()); + + // Create picture consisting of filled rect with given color; Being able + // to specify different color is useful to test deep comparison of pictures + sk_sp CreatePicture(const SkRect& bounds, uint32_t color); + + std::shared_ptr CreatePictureLayer( + sk_sp picture, + const SkPoint& offset = SkPoint::Make(0, 0)); + + // Create display list consisting of filled rect with given color; Being able + // to specify different color is useful to test deep comparison of pictures + sk_sp CreateDisplayList(const SkRect& bounds, uint32_t color); + + std::shared_ptr CreateDisplayListLayer( + sk_sp display_list, + const SkPoint& offset = SkPoint::Make(0, 0)); + + std::shared_ptr CreateContainerLayer( + std::initializer_list> layers); + + std::shared_ptr CreateContainerLayer( + std::shared_ptr l) { + return CreateContainerLayer({l}); + } + + fml::RefPtr unref_queue() { return unref_queue_; } + + private: + fml::RefPtr unref_queue_; +}; + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/gl_context_switch_test.h b/flow/testing/gl_context_switch_test.h index f65b475d021e9..e2cd8b96c0c35 100644 --- a/flow/testing/gl_context_switch_test.h +++ b/flow/testing/gl_context_switch_test.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_SHELL_RENDERER_CONTEXT_TEST_H_ #define FLUTTER_SHELL_RENDERER_CONTEXT_TEST_H_ -#include "flutter/flow/gl_context_switch.h" +#include "flutter/common/graphics/gl_context_switch.h" #include "gtest/gtest.h" namespace flutter { diff --git a/flow/testing/mock_embedder.cc b/flow/testing/mock_embedder.cc new file mode 100644 index 0000000000000..11d34ee80beda --- /dev/null +++ b/flow/testing/mock_embedder.cc @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/testing/mock_embedder.h" + +namespace flutter { +namespace testing { + +MockViewEmbedder::MockViewEmbedder() = default; + +MockViewEmbedder::~MockViewEmbedder() = default; + +// |ExternalViewEmbedder| +SkCanvas* MockViewEmbedder::GetRootCanvas() { + return nullptr; +} + +// |ExternalViewEmbedder| +void MockViewEmbedder::CancelFrame() {} + +// |ExternalViewEmbedder| +void MockViewEmbedder::BeginFrame( + SkISize frame_size, + GrDirectContext* context, + double device_pixel_ratio, + fml::RefPtr raster_thread_merger) {} + +// |ExternalViewEmbedder| +void MockViewEmbedder::PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) {} + +// |ExternalViewEmbedder| +std::vector MockViewEmbedder::GetCurrentCanvases() { + return std::vector({}); +} + +// |ExternalViewEmbedder| +SkCanvas* MockViewEmbedder::CompositeEmbeddedView(int view_id) { + return nullptr; +} + +} // namespace testing +} // namespace flutter diff --git a/flow/testing/mock_embedder.h b/flow/testing/mock_embedder.h new file mode 100644 index 0000000000000..2de6ddb51c7b7 --- /dev/null +++ b/flow/testing/mock_embedder.h @@ -0,0 +1,47 @@ +// 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 FLOW_TESTING_MOCK_EMBEDDER_H_ +#define FLOW_TESTING_MOCK_EMBEDDER_H_ + +#include "flutter/flow/embedded_views.h" + +namespace flutter { +namespace testing { + +class MockViewEmbedder : public ExternalViewEmbedder { + public: + MockViewEmbedder(); + + ~MockViewEmbedder(); + + // |ExternalViewEmbedder| + SkCanvas* GetRootCanvas() override; + + // |ExternalViewEmbedder| + void CancelFrame() override; + + // |ExternalViewEmbedder| + void BeginFrame( + SkISize frame_size, + GrDirectContext* context, + double device_pixel_ratio, + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + void PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) override; + + // |ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + + // |ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id) override; +}; + +} // namespace testing +} // namespace flutter + +#endif // FLOW_TESTING_MOCK_EMBEDDER_H_ diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc index b32bdb23abc09..0c6ef36e899c6 100644 --- a/flow/testing/mock_layer.cc +++ b/flow/testing/mock_layer.cc @@ -10,14 +10,31 @@ namespace testing { MockLayer::MockLayer(SkPath path, SkPaint paint, bool fake_has_platform_view, - bool fake_needs_system_composite, bool fake_reads_surface) : fake_paint_path_(path), fake_paint_(paint), fake_has_platform_view_(fake_has_platform_view), - fake_needs_system_composite_(fake_needs_system_composite), fake_reads_surface_(fake_reads_surface) {} +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +bool MockLayer::IsReplacing(DiffContext* context, const Layer* layer) const { + // Similar to PictureLayer, only return true for identical mock layers; + // That way ContainerLayer::DiffChildren can properly detect mock layer + // insertion + auto mock_layer = layer->as_mock_layer(); + return mock_layer && mock_layer->fake_paint_ == fake_paint_ && + mock_layer->fake_paint_path_ == fake_paint_path_; +} + +void MockLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + context->AddLayerBounds(fake_paint_path_.getBounds()); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +#endif + void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { parent_mutators_ = context->mutators_stack; parent_matrix_ = matrix; @@ -26,14 +43,13 @@ void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->has_platform_view = fake_has_platform_view_; set_paint_bounds(fake_paint_path_.getBounds()); - set_needs_system_composite(fake_needs_system_composite_); if (fake_reads_surface_) { context->surface_needs_readback = true; } } void MockLayer::Paint(PaintContext& context) const { - FML_DCHECK(needs_painting()); + FML_DCHECK(needs_painting(context)); context.leaf_nodes_canvas->drawPath(fake_paint_path_, fake_paint_); } diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h index b92583f581209..aa41fe59faed1 100644 --- a/flow/testing/mock_layer.h +++ b/flow/testing/mock_layer.h @@ -19,7 +19,6 @@ class MockLayer : public Layer { MockLayer(SkPath path, SkPaint paint = SkPaint(), bool fake_has_platform_view = false, - bool fake_needs_system_composite = false, bool fake_reads_surface = false); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; @@ -30,6 +29,14 @@ class MockLayer : public Layer { const SkRect& parent_cull_rect() { return parent_cull_rect_; } bool parent_has_platform_view() { return parent_has_platform_view_; } +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + bool IsReplacing(DiffContext* context, const Layer* layer) const override; + void Diff(DiffContext* context, const Layer* old_layer) override; + const MockLayer* as_mock_layer() const override { return this; } + +#endif + private: MutatorsStack parent_mutators_; SkMatrix parent_matrix_; @@ -38,7 +45,6 @@ class MockLayer : public Layer { SkPaint fake_paint_; bool parent_has_platform_view_ = false; bool fake_has_platform_view_ = false; - bool fake_needs_system_composite_ = false; bool fake_reads_surface_ = false; FML_DISALLOW_COPY_AND_ASSIGN(MockLayer); diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc index ebb837ca8b8dd..d1df446c258e4 100644 --- a/flow/testing/mock_layer_unittests.cc +++ b/flow/testing/mock_layer_unittests.cc @@ -14,12 +14,12 @@ namespace testing { using MockLayerTest = LayerTest; #ifndef NDEBUG -TEST_F(MockLayerTest, PaintBeforePreollDies) { +TEST_F(MockLayerTest, PaintBeforePrerollDies) { SkPath path = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); auto layer = std::make_shared(path, SkPaint()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } TEST_F(MockLayerTest, PaintingEmptyLayerDies) { @@ -29,7 +29,7 @@ TEST_F(MockLayerTest, PaintingEmptyLayerDies) { EXPECT_EQ(layer->paint_bounds(), SkPath().getBounds()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), - "needs_painting\\(\\)"); + "needs_painting\\(context\\)"); } #endif @@ -48,8 +48,7 @@ TEST_F(MockLayerTest, SimpleParams) { layer->Preroll(preroll_context(), start_matrix); EXPECT_EQ(preroll_context()->has_platform_view, false); EXPECT_EQ(layer->paint_bounds(), path.getBounds()); - EXPECT_TRUE(layer->needs_painting()); - EXPECT_FALSE(layer->needs_system_composite()); + EXPECT_TRUE(layer->needs_painting(paint_context())); EXPECT_EQ(layer->parent_mutators(), std::vector{Mutator(scale_matrix)}); EXPECT_EQ(layer->parent_matrix(), start_matrix); EXPECT_EQ(layer->parent_cull_rect(), cull_rect); @@ -70,15 +69,5 @@ TEST_F(MockLayerTest, FakePlatformView) { EXPECT_EQ(preroll_context()->has_platform_view, true); } -TEST_F(MockLayerTest, FakeSystemComposite) { - auto layer = std::make_shared( - SkPath(), SkPaint(), false /* fake_has_platform_view */, - true /* fake_needs_system_composite */); - EXPECT_EQ(layer->needs_system_composite(), false); - - layer->Preroll(preroll_context(), SkMatrix()); - EXPECT_EQ(layer->needs_system_composite(), true); -} - } // namespace testing } // namespace flutter diff --git a/flow/testing/mock_texture.cc b/flow/testing/mock_texture.cc index fddb6e8586f5c..cb5b92c8caaf8 100644 --- a/flow/testing/mock_texture.cc +++ b/flow/testing/mock_texture.cc @@ -13,21 +13,21 @@ void MockTexture::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) { + const SkSamplingOptions& sampling) { paint_calls_.emplace_back( - PaintCall{canvas, bounds, freeze, context, filter_quality}); + PaintCall{canvas, bounds, freeze, context, sampling}); } bool operator==(const MockTexture::PaintCall& a, const MockTexture::PaintCall& b) { return &a.canvas == &b.canvas && a.bounds == b.bounds && a.context == b.context && a.freeze == b.freeze && - a.filter_quality == b.filter_quality; + a.sampling == b.sampling; } std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) { return os << &data.canvas << " " << data.bounds << " " << data.context << " " - << data.freeze << " " << data.filter_quality; + << data.freeze << " " << data.sampling; } } // namespace testing diff --git a/flow/testing/mock_texture.h b/flow/testing/mock_texture.h index fe45292e4e4bb..09ce4c65422fb 100644 --- a/flow/testing/mock_texture.h +++ b/flow/testing/mock_texture.h @@ -5,7 +5,7 @@ #include #include -#include "flutter/flow/texture.h" +#include "flutter/common/graphics/texture.h" #include "flutter/testing/assertions_skia.h" namespace flutter { @@ -21,7 +21,7 @@ class MockTexture : public Texture { SkRect bounds; bool freeze; GrDirectContext* context; - SkFilterQuality filter_quality; + SkSamplingOptions sampling; }; explicit MockTexture(int64_t textureId); @@ -31,7 +31,7 @@ class MockTexture : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override; + const SkSamplingOptions& sampling) override; void OnGrContextCreated() override { gr_context_created_ = true; } void OnGrContextDestroyed() override { gr_context_destroyed_ = true; } diff --git a/flow/testing/mock_texture_unittests.cc b/flow/testing/mock_texture_unittests.cc index 9b53892aafe27..5968928b6cd2c 100644 --- a/flow/testing/mock_texture_unittests.cc +++ b/flow/testing/mock_texture_unittests.cc @@ -29,31 +29,29 @@ TEST(MockTextureTest, PaintCalls) { SkCanvas canvas; const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); - const auto expected_paint_calls = - std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, - kNone_SkFilterQuality}, - MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, - kNone_SkFilterQuality}}; + const SkSamplingOptions sampling; + const auto expected_paint_calls = std::vector{ + MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, sampling}, + MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, sampling}}; auto texture = std::make_shared(0); - texture->Paint(canvas, paint_bounds1, false, nullptr, kNone_SkFilterQuality); - texture->Paint(canvas, paint_bounds2, true, nullptr, kNone_SkFilterQuality); + texture->Paint(canvas, paint_bounds1, false, nullptr, sampling); + texture->Paint(canvas, paint_bounds2, true, nullptr, sampling); EXPECT_EQ(texture->paint_calls(), expected_paint_calls); } -TEST(MockTextureTest, PaintCallsWithLowFilterQuality) { +TEST(MockTextureTest, PaintCallsWithLinearSampling) { SkCanvas canvas; const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); - const auto expected_paint_calls = - std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, - kLow_SkFilterQuality}, - MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, - kLow_SkFilterQuality}}; + const auto sampling = SkSamplingOptions(SkFilterMode::kLinear); + const auto expected_paint_calls = std::vector{ + MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, sampling}, + MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, sampling}}; auto texture = std::make_shared(0); - texture->Paint(canvas, paint_bounds1, false, nullptr, kLow_SkFilterQuality); - texture->Paint(canvas, paint_bounds2, true, nullptr, kLow_SkFilterQuality); + texture->Paint(canvas, paint_bounds1, false, nullptr, sampling); + texture->Paint(canvas, paint_bounds2, true, nullptr, sampling); EXPECT_EQ(texture->paint_calls(), expected_paint_calls); } diff --git a/flow/texture_unittests.cc b/flow/texture_unittests.cc index ecf5acbc39223..bb39dbfb39347 100644 --- a/flow/texture_unittests.cc +++ b/flow/texture_unittests.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/flow/texture.h" +#include "flutter/common/graphics/texture.h" #include "flutter/flow/testing/mock_texture.h" #include "gtest/gtest.h" diff --git a/flow/view_holder.cc b/flow/view_holder.cc deleted file mode 100644 index c2011825c9474..0000000000000 --- a/flow/view_holder.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/flow/view_holder.h" - -#include - -#include "flutter/fml/thread_local.h" - -namespace { - -using ViewHolderBindings = - std::unordered_map>; - -FML_THREAD_LOCAL fml::ThreadLocalUniquePtr - tls_view_holder_bindings; - -fuchsia::ui::gfx::ViewProperties ToViewProperties(float width, - float height, - float insetTop, - float insetRight, - float insetBottom, - float insetLeft, - bool focusable) { - return fuchsia::ui::gfx::ViewProperties({ - .bounding_box = fuchsia::ui::gfx::BoundingBox({ - .min = fuchsia::ui::gfx::vec3({ - .x = 0.f, - .y = 0.f, - .z = -1000.f, - }), - .max = fuchsia::ui::gfx::vec3({.x = width, .y = height, .z = 0.f}), - }), - .inset_from_min = fuchsia::ui::gfx::vec3({ - .x = insetLeft, - .y = insetTop, - .z = 0.f, - }), - .inset_from_max = fuchsia::ui::gfx::vec3({ - .x = insetRight, - .y = insetBottom, - .z = 0.f, - }), - .focus_change = focusable, - }); -} - -} // namespace - -namespace flutter { - -void ViewHolder::Create(zx_koid_t id, - fml::RefPtr ui_task_runner, - fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback) { - // This raster thread contains at least 1 ViewHolder. Initialize the - // per-thread bindings. - if (tls_view_holder_bindings.get() == nullptr) { - tls_view_holder_bindings.reset(new ViewHolderBindings()); - } - - auto* bindings = tls_view_holder_bindings.get(); - FML_DCHECK(bindings); - FML_DCHECK(bindings->find(id) == bindings->end()); - - auto view_holder = std::make_unique(std::move(ui_task_runner), - std::move(view_holder_token), - on_bind_callback); - bindings->emplace(id, std::move(view_holder)); -} - -void ViewHolder::Destroy(zx_koid_t id) { - auto* bindings = tls_view_holder_bindings.get(); - FML_DCHECK(bindings); - - bindings->erase(id); -} - -ViewHolder* ViewHolder::FromId(zx_koid_t id) { - auto* bindings = tls_view_holder_bindings.get(); - if (!bindings) { - return nullptr; - } - - auto binding = bindings->find(id); - if (binding == bindings->end()) { - return nullptr; - } - - return binding->second.get(); -} - -ViewHolder::ViewHolder(fml::RefPtr ui_task_runner, - fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback) - : ui_task_runner_(std::move(ui_task_runner)), - pending_view_holder_token_(std::move(view_holder_token)), - pending_bind_callback_(on_bind_callback) { - FML_DCHECK(pending_view_holder_token_.value); -} - -void ViewHolder::UpdateScene(scenic::Session* session, - scenic::ContainerNode& container_node, - const SkPoint& offset, - const SkSize& size, - SkAlpha opacity, - bool hit_testable) { - if (pending_view_holder_token_.value) { - entity_node_ = std::make_unique(session); - opacity_node_ = std::make_unique(session); - view_holder_ = std::make_unique( - session, std::move(pending_view_holder_token_), "Flutter SceneHost"); - opacity_node_->AddChild(*entity_node_); - opacity_node_->SetLabel("flutter::ViewHolder"); - entity_node_->Attach(*view_holder_); - if (ui_task_runner_ && pending_view_holder_token_.value) { - ui_task_runner_->PostTask( - [bind_callback = std::move(pending_bind_callback_), - view_holder_id = view_holder_->id()]() { - bind_callback(view_holder_id); - }); - } - } - FML_DCHECK(entity_node_); - FML_DCHECK(opacity_node_); - FML_DCHECK(view_holder_); - - container_node.AddChild(*opacity_node_); - opacity_node_->SetOpacity(opacity / 255.0f); - entity_node_->SetTranslation(offset.x(), offset.y(), -0.1f); - entity_node_->SetHitTestBehavior( - hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault - : fuchsia::ui::gfx::HitTestBehavior::kSuppress); - if (has_pending_properties_) { - // TODO(dworsham): This should be derived from size and elevation. We - // should be able to Z-limit the view's box but otherwise it uses all of the - // available airspace. - view_holder_->SetViewProperties(std::move(pending_properties_)); - - has_pending_properties_ = false; - } -} - -void ViewHolder::SetProperties(double width, - double height, - double insetTop, - double insetRight, - double insetBottom, - double insetLeft, - bool focusable) { - pending_properties_ = ToViewProperties(width, height, insetTop, insetRight, - insetBottom, insetLeft, focusable); - has_pending_properties_ = true; -} - -} // namespace flutter diff --git a/flow/view_holder.h b/flow/view_holder.h deleted file mode 100644 index f25b205c7823c..0000000000000 --- a/flow/view_holder.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FLOW_VIEW_HOLDER_H_ -#define FLUTTER_FLOW_VIEW_HOLDER_H_ - -#include -#include -#include -#include -#include -#include - -#include - -#include "flutter/fml/macros.h" -#include "flutter/fml/memory/ref_counted.h" -#include "flutter/fml/task_runner.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkSize.h" - -namespace flutter { - -// Represents a Scenic |ViewHolder| resource that imports a |View| from another -// session. -// -// This object is created and destroyed on the |Rasterizer|'s thread. -class ViewHolder { - public: - using BindCallback = std::function; - - static void Create(zx_koid_t id, - fml::RefPtr ui_task_runner, - fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback); - static void Destroy(zx_koid_t id); - static ViewHolder* FromId(zx_koid_t id); - - ViewHolder(fml::RefPtr ui_task_runner, - fuchsia::ui::views::ViewHolderToken view_holder_token, - const BindCallback& on_bind_callback); - ~ViewHolder() = default; - - // Sets the properties/opacity of the child view by issuing a Scenic command. - void SetProperties(double width, - double height, - double insetTop, - double insetRight, - double insetBottom, - double insetLeft, - bool focusable); - - // Creates or updates the contained ViewHolder resource using the specified - // |SceneUpdateContext|. - void UpdateScene(scenic::Session* session, - scenic::ContainerNode& container_node, - const SkPoint& offset, - const SkSize& size, - SkAlpha opacity, - bool hit_testable); - - bool hit_testable() { return hit_testable_; } - void set_hit_testable(bool value) { hit_testable_ = value; } - - bool focusable() { return focusable_; } - void set_focusable(bool value) { focusable_ = value; } - - private: - fml::RefPtr ui_task_runner_; - - std::unique_ptr entity_node_; - std::unique_ptr opacity_node_; - std::unique_ptr view_holder_; - - fuchsia::ui::views::ViewHolderToken pending_view_holder_token_; - BindCallback pending_bind_callback_; - - bool hit_testable_ = true; - bool focusable_ = true; - - fuchsia::ui::gfx::ViewProperties pending_properties_; - bool has_pending_properties_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(ViewHolder); -}; - -} // namespace flutter - -#endif // FLUTTER_FLOW_VIEW_HOLDER_H_ diff --git a/flutter_frontend_server/BUILD.gn b/flutter_frontend_server/BUILD.gn index f477c194fe8bc..7e3d3a9e68101 100644 --- a/flutter_frontend_server/BUILD.gn +++ b/flutter_frontend_server/BUILD.gn @@ -23,6 +23,8 @@ frontend_server_files += application_snapshot("frontend_server") { main_dart = "bin/starter.dart" deps = [ "//flutter/lib/snapshot:kernel_platform_files" ] + pool = "//flutter/build/dart:dart_pool" + dot_packages = rebase_path(".dart_tool/package_config.json") flutter_patched_sdk = rebase_path("$root_out_dir/flutter_patched_sdk") training_args = [ diff --git a/flutter_frontend_server/bin/starter.dart b/flutter_frontend_server/bin/starter.dart index 27037aac5a61c..88101360d42cd 100644 --- a/flutter_frontend_server/bin/starter.dart +++ b/flutter_frontend_server/bin/starter.dart @@ -9,7 +9,7 @@ import 'dart:io'; import 'package:flutter_frontend_server/server.dart'; -void main(List args) async { +Future main(List args) async { final int exitCode = await starter(args); if (exitCode != 0) { exit(exitCode); diff --git a/flutter_frontend_server/lib/server.dart b/flutter_frontend_server/lib/server.dart index 861ad11c572e5..c62131e09afd7 100644 --- a/flutter_frontend_server/lib/server.dart +++ b/flutter_frontend_server/lib/server.dart @@ -15,91 +15,10 @@ import 'package:frontend_server/frontend_server.dart' as frontend CompilerInterface, listenAndCompile, argParser, - usage, - ProgramTransformer; -import 'package:kernel/ast.dart'; + usage; import 'package:path/path.dart' as path; -import 'package:vm/incremental_compiler.dart'; -/// Wrapper around [FrontendCompiler] that adds [widgetCreatorTracker] kernel -/// transformation to the compilation. -class _FlutterFrontendCompiler implements frontend.CompilerInterface { - final frontend.CompilerInterface _compiler; - - _FlutterFrontendCompiler(StringSink output, - {bool unsafePackageSerialization, - bool useDebuggerModuleNames, - bool emitDebugMetadata, - frontend.ProgramTransformer transformer}) - : _compiler = frontend.FrontendCompiler(output, - transformer: transformer, - useDebuggerModuleNames: useDebuggerModuleNames, - emitDebugMetadata: emitDebugMetadata, - unsafePackageSerialization: unsafePackageSerialization); - - @override - Future compile(String filename, ArgResults options, - {IncrementalCompiler generator}) async { - return _compiler.compile(filename, options, generator: generator); - } - - @override - Future recompileDelta({String entryPoint}) async { - return _compiler.recompileDelta(entryPoint: entryPoint); - } - - @override - void acceptLastDelta() { - _compiler.acceptLastDelta(); - } - - @override - Future rejectLastDelta() async { - return _compiler.rejectLastDelta(); - } - - @override - void invalidate(Uri uri) { - _compiler.invalidate(uri); - } - - @override - Future compileExpression( - String expression, - List definitions, - List typeDefinitions, - String libraryUri, - String klass, - bool isStatic) { - return _compiler.compileExpression( - expression, definitions, typeDefinitions, libraryUri, klass, isStatic); - } - - @override - Future compileExpressionToJs( - String libraryUri, - int line, - int column, - Map jsModules, - Map jsFrameValues, - String moduleName, - String expression) { - return _compiler.compileExpressionToJs(libraryUri, line, column, jsModules, - jsFrameValues, moduleName, expression); - } - - @override - void reportError(String msg) { - _compiler.reportError(msg); - } - - @override - void resetIncrementalCompiler() { - _compiler.resetIncrementalCompiler(); - } -} - -/// Entry point for this module, that creates `_FrontendCompiler` instance and +/// Entry point for this module, that creates `FrontendCompiler` instance and /// processes user input. /// `compiler` is an optional parameter so it can be replaced with mocked /// version for testing. @@ -108,16 +27,8 @@ Future starter( frontend.CompilerInterface compiler, Stream> input, StringSink output, - frontend.ProgramTransformer transformer, }) async { ArgResults options; - frontend.argParser.addMultiOption( - 'delete-tostring-package-uri', - help: 'Replaces implementations of `toString` with `super.toString()` for ' - 'specified package', - valueHelp: 'dart:ui', - defaultsTo: const [], - ); try { options = frontend.argParser.parse(args); } catch (error) { @@ -126,8 +37,6 @@ Future starter( return 1; } - final Set deleteToStringPackageUris = (options['delete-tostring-package-uri'] as List).toSet(); - if (options['train'] as bool) { if (!options.rest.isNotEmpty) { throw Exception('Must specify input.dart'); @@ -147,13 +56,8 @@ Future starter( '--target=flutter', '--track-widget-creation', '--enable-asserts', - '--gen-bytecode', - '--bytecode-options=source-positions,local-var-info,debugger-stops,instance-field-initializers,keep-unreachable-code,avoid-closure-call-instructions', ]); - compiler ??= _FlutterFrontendCompiler( - output, - transformer: ToStringTransformer(null, deleteToStringPackageUris), - ); + compiler ??= frontend.FrontendCompiler(output); await compiler.compile(input, options); compiler.acceptLastDelta(); @@ -171,8 +75,7 @@ Future starter( } } - compiler ??= _FlutterFrontendCompiler(output, - transformer: ToStringTransformer(transformer, deleteToStringPackageUris), + compiler ??= frontend.FrontendCompiler(output, useDebuggerModuleNames: options['debugger-module-names'] as bool, emitDebugMetadata: options['experimental-emit-debug-metadata'] as bool, unsafePackageSerialization: @@ -186,86 +89,3 @@ Future starter( frontend.listenAndCompile(compiler, input ?? stdin, options, completer); return completer.future; } - -// Transformer/visitor for toString -// If we add any more of these, they really should go into a separate library. - -/// A [RecursiveVisitor] that replaces [Object.toString] overrides with -/// `super.toString()`. -class ToStringVisitor extends RecursiveVisitor { - /// The [packageUris] must not be null. - ToStringVisitor(this._packageUris) : assert(_packageUris != null); - - /// A set of package URIs to apply this transformer to, e.g. 'dart:ui' and - /// 'package:flutter/foundation.dart'. - final Set _packageUris; - - /// Turn 'dart:ui' into 'dart:ui', or - /// 'package:flutter/src/semantics_event.dart' into 'package:flutter'. - String _importUriToPackage(Uri importUri) => '${importUri.scheme}:${importUri.pathSegments.first}'; - - bool _isInTargetPackage(Procedure node) { - return _packageUris.contains(_importUriToPackage(node.enclosingLibrary.importUri)); - } - - bool _hasKeepAnnotation(Procedure node) { - for (ConstantExpression expression in node.annotations.whereType()) { - if (expression.constant is! InstanceConstant) { - continue; - } - final InstanceConstant constant = expression.constant as InstanceConstant; - if (constant.classNode.name == '_KeepToString' && constant.classNode.enclosingLibrary.importUri.toString() == 'dart:ui') { - return true; - } - } - return false; - } - - @override - void visitProcedure(Procedure node) { - if ( - node.name.text == 'toString' && - node.enclosingClass != null && - node.enclosingLibrary != null && - !node.isStatic && - !node.isAbstract && - !node.enclosingClass.isEnum && - _isInTargetPackage(node) && - !_hasKeepAnnotation(node) - ) { - node.function.body.replaceWith( - ReturnStatement( - SuperMethodInvocation( - node.name, - Arguments([]), - ), - ), - ); - } - } - - @override - void defaultMember(Member node) {} -} - -/// Replaces [Object.toString] overrides with calls to super for the specified -/// [packageUris]. -class ToStringTransformer extends frontend.ProgramTransformer { - /// The [packageUris] parameter must not be null, but may be empty. - ToStringTransformer(this._child, this._packageUris) : assert(_packageUris != null); - - final frontend.ProgramTransformer _child; - - /// A set of package URIs to apply this transformer to, e.g. 'dart:ui' and - /// 'package:flutter/foundation.dart'. - final Set _packageUris; - - @override - void transform(Component component) { - assert(_child is! ToStringTransformer); - if (_packageUris.isNotEmpty) { - component.visitChildren(ToStringVisitor(_packageUris)); - } - _child?.transform(component); - } -} diff --git a/flutter_frontend_server/pubspec.yaml b/flutter_frontend_server/pubspec.yaml index 8bc51c8d62d0e..69b1aedeffbe8 100644 --- a/flutter_frontend_server/pubspec.yaml +++ b/flutter_frontend_server/pubspec.yaml @@ -1,11 +1,23 @@ -name: frontend_server +# 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. + +name: flutter_frontend_server +publish_to: none version: 0.1.1-dev description: Communication pipe to Dart Frontend -homepage: http://flutter.io -author: Flutter Authors +homepage: https://flutter.dev + +# Do not add any dependencies that require more than what is provided in +# //third_party/pkg, //third_party/dart/pkg or +# //third_party/dart/third_party/pkg. In particular, package:test is not usable +# here. + +# If you do add packages here, make sure you can run `pub get --offline`, and +# check the .packages and .package_config to make sure all the paths are +# relative to this directory into //third_party/dart environment: - # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite. sdk: ">=2.2.2 <3.0.0" dependencies: @@ -16,13 +28,102 @@ dependencies: convert: any crypto: any front_end: any + frontend_server: any kernel: any logging: any meta: any - quiver: any package_config: any path: any source_span: any typed_data: any usage: any vm: any + +dev_dependencies: + litetest: any + +dependency_overrides: + _fe_analyzer_shared: + path: ../../third_party/dart/pkg/_fe_analyzer_shared + _js_interop_checks: + path: ../../third_party/dart/pkg/_js_interop_checks + args: + path: ../../third_party/dart/third_party/pkg/args + async: + path: ../../third_party/dart/third_party/pkg/async + async_helper: + path: ../../third_party/dart/pkg/async_helper + bazel_worker: + path: ../../third_party/dart/third_party/pkg/bazel_worker + build_integration: + path: ../../third_party/dart/pkg/build_integration + charcode: + path: ../../third_party/dart/third_party/pkg/charcode + cli_util: + path: ../../third_party/dart/third_party/pkg/cli_util + collection: + path: ../../third_party/dart/third_party/pkg/collection + compiler: + path: ../../third_party/dart/pkg/compiler + convert: + path: ../../third_party/dart/third_party/pkg/convert + crypto: + path: ../../third_party/dart/third_party/pkg/crypto + dart2js_info: + path: ../../third_party/dart/pkg/dart2js_info + dev_compiler: + path: ../../third_party/dart/pkg/dev_compiler + expect: + path: ../../third_party/dart/pkg/expect + fixnum: + path: ../../third_party/dart/third_party/pkg/fixnum + front_end: + path: ../../third_party/dart/pkg/front_end + frontend_server: + path: ../../third_party/dart/pkg/frontend_server + http_parser: + path: ../../third_party/dart/third_party/pkg/http_parser + kernel: + path: ../../third_party/dart/pkg/kernel + litetest: + path: ../testing/litetest + logging: + path: ../../third_party/dart/third_party/pkg/logging + matcher: + path: ../../third_party/dart/third_party/pkg/matcher + meta: + path: ../../third_party/dart/pkg/meta + mime: + path: ../../third_party/dart/third_party/pkg/mime + package_config: + path: ../../third_party/dart/third_party/pkg_tested/package_config + path: + path: ../../third_party/dart/third_party/pkg/path + pedantic: + path: ../../third_party/dart/third_party/pkg/pedantic + protobuf: + path: ../../third_party/dart/third_party/pkg/protobuf/protobuf + shelf: + path: ../../third_party/dart/third_party/pkg/shelf + shelf_static: + path: ../../third_party/dart/third_party/pkg/shelf_static + source_maps: + path: ../../third_party/dart/third_party/pkg/source_maps + source_span: + path: ../../third_party/dart/third_party/pkg/source_span + stack_trace: + path: ../../third_party/dart/third_party/pkg/stack_trace + stream_channel: + path: ../../third_party/dart/third_party/pkg/stream_channel + string_scanner: + path: ../../third_party/dart/third_party/pkg/string_scanner + term_glyph: + path: ../../third_party/dart/third_party/pkg/term_glyph + typed_data: + path: ../../third_party/dart/third_party/pkg/typed_data + usage: + path: ../../third_party/dart/third_party/pkg/usage + vm: + path: ../../third_party/dart/pkg/vm + yaml: + path: ../../third_party/dart/third_party/pkg/yaml diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart index 868667c9b98bd..b42f86fcd07f3 100644 --- a/flutter_frontend_server/test/to_string_test.dart +++ b/flutter_frontend_server/test/to_string_test.dart @@ -4,311 +4,78 @@ import 'dart:io'; -import 'package:flutter_frontend_server/server.dart'; -import 'package:frontend_server/frontend_server.dart' as frontend show ProgramTransformer; -import 'package:kernel/kernel.dart'; -import 'package:mockito/mockito.dart'; +import 'package:litetest/litetest.dart'; import 'package:path/path.dart' as path; -import 'package:test/test.dart'; - -void main(List args) async { +Future main(List args) async { if (args.length != 2) { - stderr.writeln('The first argument must be the path to the forntend server dill.'); + stderr.writeln('The first argument must be the path to the frontend server dill.'); stderr.writeln('The second argument must be the path to the flutter_patched_sdk'); exit(-1); } - const Set uiAndFlutter = { - 'dart:ui', - 'package:flutter', - }; - - test('No packages', () { - final ToStringTransformer transformer = ToStringTransformer(null, {}); - - final MockComponent component = MockComponent(); - transformer.transform(component); - verifyNever(component.visitChildren(any)); - }); - - test('dart:ui package', () { - final ToStringTransformer transformer = ToStringTransformer(null, uiAndFlutter); - - final MockComponent component = MockComponent(); - transformer.transform(component); - verify(component.visitChildren(any)).called(1); - }); - - test('Child transformer', () { - final MockTransformer childTransformer = MockTransformer(); - final ToStringTransformer transformer = ToStringTransformer(childTransformer, {}); - - final MockComponent component = MockComponent(); - transformer.transform(component); - verifyNever(component.visitChildren(any)); - verify(childTransformer.transform(component)).called(1); - }); - - test('ToStringVisitor ignores non-toString procedures', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - when(procedure.name).thenReturn(Name('main')); - when(procedure.annotations).thenReturn(const []); - - visitor.visitProcedure(procedure); - verifyNever(procedure.enclosingLibrary); - }); - - test('ToStringVisitor ignores top level toString', () { - // i.e. a `toString` method specified at the top of a library, like: - // - // void main() {} - // String toString() => 'why?'; - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(Name('toString')); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(null); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - verifyNever(statement.replaceWith(any)); - }); - - test('ToStringVisitor ignores abstract toString', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(Name('toString')); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()); - when(procedure.isAbstract).thenReturn(true); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - verifyNever(statement.replaceWith(any)); - }); - - test('ToStringVisitor ignores static toString', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(Name('toString')); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(true); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - verifyNever(statement.replaceWith(any)); - }); - - test('ToStringVisitor ignores enum toString', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(Name('toString')); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()..isEnum = true); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - verifyNever(statement.replaceWith(any)); - }); - - test('ToStringVisitor ignores non-specified libraries', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(Name('toString')); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - verifyNever(statement.replaceWith(any)); - }); - - test('ToStringVisitor ignores @keepToString', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('dart:ui')); - final Name name = Name('toString'); - final Class annotation = Class(name: '_KeepToString')..parent = Library(Uri.parse('dart:ui')); - - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(name); - when(procedure.annotations).thenReturn([ - ConstantExpression( - InstanceConstant( - Reference()..node = annotation, - [], - {}, - ), - ), - ]); - - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - verifyNever(statement.replaceWith(any)); - }); - - void _validateReplacement(MockStatement body) { - final ReturnStatement replacement = verify(body.replaceWith(captureAny)).captured.single as ReturnStatement; - expect(replacement.expression, isA()); - final SuperMethodInvocation superMethodInvocation = replacement.expression as SuperMethodInvocation; - expect(superMethodInvocation.name.text, 'toString'); + final String dart = Platform.resolvedExecutable; + final String frontendServer = args[0]; + final String sdkRoot = args[1]; + final String basePath = path.canonicalize(path.join(path.dirname(Platform.script.path), '..')); + final String fixtures = path.join(basePath, 'test', 'fixtures'); + final String mainDart = path.join(fixtures, 'lib', 'main.dart'); + final String packageConfig = path.join(fixtures, '.dart_tool', 'package_config.json'); + final String regularDill = path.join(fixtures, 'toString.dill'); + final String transformedDill = path.join(fixtures, 'toStringTransformed.dill'); + + + void _checkProcessResult(ProcessResult result) { + if (result.exitCode != 0) { + stdout.writeln(result.stdout); + stderr.writeln(result.stderr); + } + expect(result.exitCode, 0); } - test('ToStringVisitor replaces toString in specified libraries (dart:ui)', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('dart:ui')); - final Name name = Name('toString'); - - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(name); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - _validateReplacement(statement); - }); - - test('ToStringVisitor replaces toString in specified libraries (package:flutter)', () { - final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); - final MockProcedure procedure = MockProcedure(); - final MockFunctionNode function = MockFunctionNode(); - final MockStatement statement = MockStatement(); - final Library library = Library(Uri.parse('package:flutter/src/foundation.dart')); - final Name name = Name('toString'); - - when(procedure.function).thenReturn(function); - when(procedure.name).thenReturn(name); - when(procedure.annotations).thenReturn(const []); - when(procedure.enclosingLibrary).thenReturn(library); - when(procedure.enclosingClass).thenReturn(Class()); - when(procedure.isAbstract).thenReturn(false); - when(procedure.isStatic).thenReturn(false); - when(function.body).thenReturn(statement); - - visitor.visitProcedure(procedure); - _validateReplacement(statement); - }); - - group('Integration tests', () { - final String dart = Platform.resolvedExecutable; - final String frontendServer = args[0]; - final String sdkRoot = args[1]; - final String basePath = path.canonicalize(path.join(path.dirname(Platform.script.path), '..')); - final String fixtures = path.join(basePath, 'test', 'fixtures'); - final String mainDart = path.join(fixtures, 'lib', 'main.dart'); - final String packageConfig = path.join(fixtures, '.dart_tool', 'package_config.json'); - final String regularDill = path.join(fixtures, 'toString.dill'); - final String transformedDill = path.join(fixtures, 'toStringTransformed.dill'); - - - void _checkProcessResult(ProcessResult result) { - if (result.exitCode != 0) { - stdout.writeln(result.stdout); - stderr.writeln(result.stderr); - } - expect(result.exitCode, 0); + test('Without flag', () { + _checkProcessResult(Process.runSync(dart, [ + frontendServer, + '--sdk-root=$sdkRoot', + '--target=flutter', + '--packages=$packageConfig', + '--output-dill=$regularDill', + mainDart, + ])); + final ProcessResult runResult = Process.runSync(dart, [regularDill]); + _checkProcessResult(runResult); + String paintString = '"Paint.toString":"Paint(Color(0xffffffff))"'; + if (const bool.fromEnvironment('dart.vm.product', defaultValue: false)) { + paintString = '"Paint.toString":"Instance of \'Paint\'"'; } - test('Without flag', () async { - _checkProcessResult(Process.runSync(dart, [ - frontendServer, - '--sdk-root=$sdkRoot', - '--target=flutter', - '--packages=$packageConfig', - '--output-dill=$regularDill', - mainDart, - ])); - final ProcessResult runResult = Process.runSync(dart, [regularDill]); - _checkProcessResult(runResult); - String paintString = '"Paint.toString":"Paint(Color(0xffffffff))"'; - if (const bool.fromEnvironment('dart.vm.product', defaultValue: false)) { - paintString = '"Paint.toString":"Instance of \'Paint\'"'; - } - expect( - runResult.stdout.trim(), - '{$paintString,' - '"Brightness.toString":"Brightness.dark",' - '"Foo.toString":"I am a Foo",' - '"Keep.toString":"I am a Keep"}', - ); - }); + final String expectedStdout = '{$paintString,' + '"Brightness.toString":"Brightness.dark",' + '"Foo.toString":"I am a Foo",' + '"Keep.toString":"I am a Keep"}'; + final String actualStdout = runResult.stdout.trim() as String; + expect(actualStdout, equals(expectedStdout)); + }); - test('With flag', () async { - _checkProcessResult(Process.runSync(dart, [ - frontendServer, - '--sdk-root=$sdkRoot', - '--target=flutter', - '--packages=$packageConfig', - '--output-dill=$transformedDill', - '--delete-tostring-package-uri', 'dart:ui', - '--delete-tostring-package-uri', 'package:flutter_frontend_fixtures', - mainDart, - ])); - final ProcessResult runResult = Process.runSync(dart, [transformedDill]); - _checkProcessResult(runResult); - expect( - runResult.stdout.trim(), - '{"Paint.toString":"Instance of \'Paint\'",' - '"Brightness.toString":"Brightness.dark",' - '"Foo.toString":"Instance of \'Foo\'",' - '"Keep.toString":"I am a Keep"}', - ); - }); + test('With flag', () { + _checkProcessResult(Process.runSync(dart, [ + frontendServer, + '--sdk-root=$sdkRoot', + '--target=flutter', + '--packages=$packageConfig', + '--output-dill=$transformedDill', + '--delete-tostring-package-uri', 'dart:ui', + '--delete-tostring-package-uri', 'package:flutter_frontend_fixtures', + mainDart, + ])); + final ProcessResult runResult = Process.runSync(dart, [transformedDill]); + _checkProcessResult(runResult); + + const String expectedStdout = '{"Paint.toString":"Instance of \'Paint\'",' + '"Brightness.toString":"Brightness.dark",' + '"Foo.toString":"Instance of \'Foo\'",' + '"Keep.toString":"I am a Keep"}'; + final String actualStdout = runResult.stdout.trim() as String; + expect(actualStdout, equals(expectedStdout)); }); } - -class MockComponent extends Mock implements Component {} -class MockTransformer extends Mock implements frontend.ProgramTransformer {} -class MockProcedure extends Mock implements Procedure {} -class MockFunctionNode extends Mock implements FunctionNode {} -class MockStatement extends Mock implements Statement {} diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 358fbee5483ff..5c8432e827bff 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -15,8 +15,6 @@ source_set("fml") { "base32.h", "build_config.h", "closure.h", - "command_line.cc", - "command_line.h", "compiler_specific.h", "concurrent_message_loop.cc", "concurrent_message_loop.h", @@ -47,8 +45,6 @@ source_set("fml") { "memory/weak_ptr.h", "memory/weak_ptr_internal.cc", "memory/weak_ptr_internal.h", - "message.cc", - "message.h", "message_loop.cc", "message_loop.h", "message_loop_impl.cc", @@ -61,6 +57,8 @@ source_set("fml") { "posix_wrappers.h", "raster_thread_merger.cc", "raster_thread_merger.h", + "shared_thread_merger.cc", + "shared_thread_merger.h", "size.h", "synchronization/atomic_object.h", "synchronization/count_down_latch.cc", @@ -72,15 +70,21 @@ source_set("fml") { "synchronization/sync_switch.h", "synchronization/waitable_event.cc", "synchronization/waitable_event.h", + "task_queue_id.h", "task_runner.cc", "task_runner.h", + "task_source.cc", + "task_source.h", "thread.cc", "thread.h", "thread_local.cc", "thread_local.h", + "time/dart_timestamp_provider.cc", + "time/dart_timestamp_provider.h", "time/time_delta.h", "time/time_point.cc", "time/time_point.h", + "time/timestamp_provider.h", "trace_event.cc", "trace_event.h", "unique_fd.cc", @@ -95,9 +99,10 @@ source_set("fml") { sources += [ "backtrace_stub.cc" ] } - public_deps = [] + public_deps = [ ":command_line" ] deps = [ + "//third_party/abseil-cpp/absl/debugging:symbolize", "//third_party/dart/runtime:dart_api", # These need to be in sync with the Fuchsia buildroot. @@ -139,7 +144,7 @@ source_set("fml") { "platform/darwin/string_range_sanitization.mm", ] - libs += [ "Foundation.framework" ] + frameworks = [ "Foundation.framework" ] } if (is_android) { @@ -181,12 +186,15 @@ source_set("fml") { "platform/fuchsia/message_loop_fuchsia.cc", "platform/fuchsia/message_loop_fuchsia.h", "platform/fuchsia/paths_fuchsia.cc", + "platform/fuchsia/task_observers.cc", + "platform/fuchsia/task_observers.h", ] public_deps += [ "$fuchsia_sdk_root/pkg:async-cpp", "$fuchsia_sdk_root/pkg:async-loop-cpp", "$fuchsia_sdk_root/pkg:async-loop-default", + "$fuchsia_sdk_root/pkg:syslog", "$fuchsia_sdk_root/pkg:trace", "$fuchsia_sdk_root/pkg:trace-engine", "$fuchsia_sdk_root/pkg:zx", @@ -222,6 +230,18 @@ source_set("fml") { } } +source_set("command_line") { + sources = [ + "command_line.cc", + "command_line.h", + ] + + public_configs = [ + "//flutter:config", + "//flutter/common:flutter_config", + ] +} + if (enable_unittests) { test_fixtures("fml_fixtures") { fixtures = [] @@ -249,21 +269,25 @@ if (enable_unittests) { "command_line_unittest.cc", "file_unittest.cc", "hash_combine_unittests.cc", + "logging_unittests.cc", + "mapping_unittests.cc", "memory/ref_counted_unittest.cc", "memory/task_runner_checker_unittest.cc", "memory/weak_ptr_unittest.cc", "message_loop_task_queues_merge_unmerge_unittests.cc", "message_loop_task_queues_unittests.cc", "message_loop_unittests.cc", - "message_unittests.cc", "paths_unittests.cc", "raster_thread_merger_unittests.cc", "synchronization/count_down_latch_unittests.cc", "synchronization/semaphore_unittest.cc", "synchronization/sync_switch_unittest.cc", "synchronization/waitable_event_unittest.cc", + "task_source_unittests.cc", "thread_local_unittests.cc", "thread_unittests.cc", + "time/chrono_timestamp_provider.cc", + "time/chrono_timestamp_provider.h", "time/time_delta_unittest.cc", "time/time_point_unittest.cc", "time/time_unittest.cc", diff --git a/fml/backtrace.cc b/fml/backtrace.cc index bcf9ca7ea1594..1a9fbdd4aee9f 100644 --- a/fml/backtrace.cc +++ b/fml/backtrace.cc @@ -17,6 +17,8 @@ #endif #include "flutter/fml/logging.h" +#include "flutter/fml/paths.h" +#include "third_party/abseil-cpp/absl/debugging/symbolize.h" namespace fml { @@ -45,13 +47,12 @@ static std::string DemangleSymbolName(const std::string& mangled) { } static std::string GetSymbolName(void* symbol) { - Dl_info info = {}; - - if (::dladdr(symbol, &info) == 0) { + char name[1024]; + if (!absl::Symbolize(symbol, name, sizeof(name))) { return kKUnknownFrameName; } - return DemangleSymbolName({info.dli_sname}); + return DemangleSymbolName({name}); } std::string BacktraceHere(size_t offset) { @@ -137,6 +138,10 @@ void InstallCrashHandler() { _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); } #endif + auto exe_path = fml::paths::GetExecutablePath(); + if (exe_path.first) { + absl::InitializeSymbolizer(exe_path.second.c_str()); + } ToggleSignalHandlers(true); } diff --git a/fml/concurrent_message_loop.cc b/fml/concurrent_message_loop.cc index 3d4ca470e41fe..b58431b7df3d2 100644 --- a/fml/concurrent_message_loop.cc +++ b/fml/concurrent_message_loop.cc @@ -22,7 +22,7 @@ ConcurrentMessageLoop::ConcurrentMessageLoop(size_t worker_count) for (size_t i = 0; i < worker_count_; ++i) { workers_.emplace_back([i, this]() { fml::Thread::SetCurrentThreadName( - std::string{"io.flutter.worker." + std::to_string(i + 1)}); + std::string{"io.worker." + std::to_string(i + 1)}); WorkerMain(); }); } diff --git a/fml/concurrent_message_loop.h b/fml/concurrent_message_loop.h index 6071f45cafa82..ebd8de983508d 100644 --- a/fml/concurrent_message_loop.h +++ b/fml/concurrent_message_loop.h @@ -12,6 +12,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" +#include "flutter/fml/task_runner.h" namespace fml { @@ -58,13 +59,13 @@ class ConcurrentMessageLoop FML_DISALLOW_COPY_AND_ASSIGN(ConcurrentMessageLoop); }; -class ConcurrentTaskRunner { +class ConcurrentTaskRunner : public BasicTaskRunner { public: ConcurrentTaskRunner(std::weak_ptr weak_loop); - ~ConcurrentTaskRunner(); + virtual ~ConcurrentTaskRunner(); - void PostTask(const fml::closure& task); + void PostTask(const fml::closure& task) override; private: friend ConcurrentMessageLoop; diff --git a/fml/dart/dart_converter.h b/fml/dart/dart_converter.h index 159ec44e25c31..ccdd2422abed6 100644 --- a/fml/dart/dart_converter.h +++ b/fml/dart/dart_converter.h @@ -23,7 +23,11 @@ struct DartConverter { return Dart_Null(); } - auto dart_list_handle = Dart_NewListOf(Dart_CoreType_Int, val->GetSize()); + auto dart_list_handle = Dart_NewListOfTypeFilled( + ToDartTypeHandle(), // type + CreateZeroInitializedDartObject(), // sentinel + val->GetSize() // size + ); if (Dart_IsError(dart_list_handle)) { FML_LOG(ERROR) << "Error while attempting to allocate a list: " diff --git a/fml/delayed_task.cc b/fml/delayed_task.cc index 997176d7694e0..bb72daa7cb78e 100644 --- a/fml/delayed_task.cc +++ b/fml/delayed_task.cc @@ -10,13 +10,17 @@ namespace fml { DelayedTask::DelayedTask(size_t order, const fml::closure& task, - fml::TimePoint target_time) - : order_(order), task_(task), target_time_(target_time) {} - -DelayedTask::DelayedTask(const DelayedTask& other) = default; + fml::TimePoint target_time, + fml::TaskSourceGrade task_source_grade) + : order_(order), + task_(task), + target_time_(target_time), + task_source_grade_(task_source_grade) {} DelayedTask::~DelayedTask() = default; +DelayedTask::DelayedTask(const DelayedTask& other) = default; + const fml::closure& DelayedTask::GetTask() const { return task_; } @@ -25,6 +29,10 @@ fml::TimePoint DelayedTask::GetTargetTime() const { return target_time_; } +fml::TaskSourceGrade DelayedTask::GetTaskSourceGrade() const { + return task_source_grade_; +} + bool DelayedTask::operator>(const DelayedTask& other) const { if (target_time_ == other.target_time_) { return order_ > other.order_; diff --git a/fml/delayed_task.h b/fml/delayed_task.h index ba6fc8cfe4c9d..6552a68652918 100644 --- a/fml/delayed_task.h +++ b/fml/delayed_task.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/closure.h" +#include "flutter/fml/task_source_grade.h" #include "flutter/fml/time/time_point.h" namespace fml { @@ -16,7 +17,8 @@ class DelayedTask { public: DelayedTask(size_t order, const fml::closure& task, - fml::TimePoint target_time); + fml::TimePoint target_time, + fml::TaskSourceGrade task_source_grade); DelayedTask(const DelayedTask& other); @@ -26,12 +28,15 @@ class DelayedTask { fml::TimePoint GetTargetTime() const; + fml::TaskSourceGrade GetTaskSourceGrade() const; + bool operator>(const DelayedTask& other) const; private: size_t order_; fml::closure task_; fml::TimePoint target_time_; + fml::TaskSourceGrade task_source_grade_; }; using DelayedTaskQueue = std::priority_queue -constexpr std::size_t HashCombine(Type... args) { +[[nodiscard]] constexpr std::size_t HashCombine(Type... args) { std::size_t seed = HashCombine(); HashCombineSeed(seed, args...); return seed; diff --git a/fml/log_settings.cc b/fml/log_settings.cc index aa1e54e36fb04..4388a1a277f5c 100644 --- a/fml/log_settings.cc +++ b/fml/log_settings.cc @@ -10,8 +10,13 @@ #include #include +#include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" +#if defined(OS_FUCHSIA) +#include +#endif + namespace fml { namespace state { @@ -24,6 +29,10 @@ void SetLogSettings(const LogSettings& settings) { // Validate the new settings as we set them. state::g_log_settings.min_log_level = std::min(LOG_FATAL, settings.min_log_level); +#if defined(OS_FUCHSIA) + // Syslog should accept all logs, since filtering by severity is done by fml. + FX_LOG_SET_SEVERITY(ALL); +#endif } LogSettings GetLogSettings() { diff --git a/fml/logging.cc b/fml/logging.cc index d4273b9381da7..e3a57df4bbccc 100644 --- a/fml/logging.cc +++ b/fml/logging.cc @@ -13,11 +13,15 @@ #include #elif defined(OS_IOS) #include +#elif defined(OS_FUCHSIA) +#include #endif namespace fml { + namespace { +#if !defined(OS_FUCHSIA) const char* const kLogSeverityNames[LOG_NUM_SEVERITIES] = {"INFO", "WARNING", "ERROR", "FATAL"}; @@ -42,6 +46,7 @@ const char* StripPath(const char* path) { } return path; } +#endif } // namespace @@ -50,6 +55,7 @@ LogMessage::LogMessage(LogSeverity severity, int line, const char* condition) : severity_(severity), file_(file), line_(line) { +#if !defined(OS_FUCHSIA) stream_ << "["; if (severity >= LOG_INFO) { stream_ << GetNameForLogSeverity(severity); @@ -58,6 +64,7 @@ LogMessage::LogMessage(LogSeverity severity, } stream_ << ":" << (severity > LOG_INFO ? StripDots(file_) : StripPath(file_)) << "(" << line_ << ")] "; +#endif if (condition) { stream_ << "Check failed: " << condition << ". "; @@ -65,7 +72,9 @@ LogMessage::LogMessage(LogSeverity severity, } LogMessage::~LogMessage() { +#if !defined(OS_FUCHSIA) stream_ << std::endl; +#endif #if defined(OS_ANDROID) android_LogPriority priority = @@ -87,13 +96,38 @@ LogMessage::~LogMessage() { __android_log_write(priority, "flutter", stream_.str().c_str()); #elif defined(OS_IOS) syslog(LOG_ALERT, "%s", stream_.str().c_str()); +#elif defined(OS_FUCHSIA) + fx_log_severity_t fx_severity; + switch (severity_) { + case LOG_INFO: + fx_severity = FX_LOG_INFO; + break; + case LOG_WARNING: + fx_severity = FX_LOG_WARNING; + break; + case LOG_ERROR: + fx_severity = FX_LOG_ERROR; + break; + case LOG_FATAL: + fx_severity = FX_LOG_FATAL; + break; + default: + if (severity_ < 0) { + fx_severity = fx_log_severity_from_verbosity(-severity_); + } else { + // Unknown severity. Use INFO. + fx_severity = FX_LOG_INFO; + } + } + fx_logger_log_with_source(fx_log_get_logger(), fx_severity, nullptr, file_, + line_, stream_.str().c_str()); #else std::cerr << stream_.str(); std::cerr.flush(); #endif if (severity_ >= LOG_FATAL) { - abort(); + KillProcess(); } } @@ -105,4 +139,8 @@ bool ShouldCreateLogMessage(LogSeverity severity) { return severity >= GetMinLogLevel(); } +void KillProcess() { + abort(); +} + } // namespace fml diff --git a/fml/logging.h b/fml/logging.h index 20cb887cb20df..0b732ee818a0a 100644 --- a/fml/logging.h +++ b/fml/logging.h @@ -43,6 +43,8 @@ int GetVlogVerbosity(); // LOG_FATAL and above is always true. bool ShouldCreateLogMessage(LogSeverity severity); +[[noreturn]] void KillProcess(); + } // namespace fml #define FML_LOG_STREAM(severity) \ @@ -87,9 +89,10 @@ bool ShouldCreateLogMessage(LogSeverity severity); #define FML_DCHECK(condition) FML_EAT_STREAM_PARAMETERS(condition) #endif -#define FML_NOTREACHED() FML_DCHECK(false) - -#define FML_NOTIMPLEMENTED() \ - FML_LOG(ERROR) << "Not implemented in: " << __PRETTY_FUNCTION__ +#define FML_UNREACHABLE() \ + { \ + FML_LOG(ERROR) << "Reached unreachable code."; \ + ::fml::KillProcess(); \ + } #endif // FLUTTER_FML_LOGGING_H_ diff --git a/fml/logging_unittests.cc b/fml/logging_unittests.cc new file mode 100644 index 0000000000000..18ac1ad8fa8c6 --- /dev/null +++ b/fml/logging_unittests.cc @@ -0,0 +1,134 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/build_config.h" +#include "flutter/fml/log_settings.h" +#include "flutter/fml/logging.h" +#include "gtest/gtest.h" + +#if defined(OS_FUCHSIA) +#include +#include +#include +#include + +#include "gmock/gmock.h" +#endif + +namespace fml { +namespace testing { + +int UnreachableScopeWithoutReturnDoesNotMakeCompilerMad() { + KillProcess(); + // return 0; <--- Missing but compiler is fine. +} + +int UnreachableScopeWithMacroWithoutReturnDoesNotMakeCompilerMad() { + FML_UNREACHABLE(); + // return 0; <--- Missing but compiler is fine. +} + +TEST(LoggingTest, UnreachableKillProcess) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + ASSERT_DEATH(KillProcess(), ""); +} + +TEST(LoggingTest, UnreachableKillProcessWithMacro) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + ASSERT_DEATH({ FML_UNREACHABLE(); }, ""); +} + +#if defined(OS_FUCHSIA) + +struct LogPacket { + fx_log_metadata_t metadata; + std::vector tags; + std::string message; +}; + +class LoggingSocketTest : public ::testing::Test { + protected: + void SetUp() override { + zx::socket local; + ASSERT_EQ(ZX_OK, zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &socket_)); + + fx_logger_config_t config = {.min_severity = FX_LOG_INFO, + .console_fd = -1, + .log_service_channel = local.release(), + .tags = nullptr, + .num_tags = 0}; + + fx_log_reconfigure(&config); + } + + LogPacket ReadPacket() { + LogPacket result; + fx_log_packet_t packet; + zx_status_t res = socket_.read(0, &packet, sizeof(packet), nullptr); + EXPECT_EQ(ZX_OK, res); + result.metadata = packet.metadata; + int pos = 0; + while (packet.data[pos]) { + int tag_len = packet.data[pos++]; + result.tags.emplace_back(packet.data + pos, tag_len); + pos += tag_len; + } + result.message.append(packet.data + pos + 1); + return result; + } + + void ReadPacketAndCompare(fx_log_severity_t severity, + const std::string& message, + const std::vector& tags = {}) { + LogPacket packet = ReadPacket(); + EXPECT_EQ(severity, packet.metadata.severity); + EXPECT_THAT(packet.message, ::testing::EndsWith(message)); + EXPECT_EQ(tags, packet.tags); + } + + void CheckSocketEmpty() { + zx_info_socket_t info = {}; + zx_status_t status = + socket_.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr); + ASSERT_EQ(ZX_OK, status); + EXPECT_EQ(0u, info.rx_buf_available); + } + + zx::socket socket_; +}; + +TEST_F(LoggingSocketTest, UseSyslogOnFuchsia) { + const char* msg1 = "test message"; + const char* msg2 = "hello"; + const char* msg3 = "logging"; + const char* msg4 = "Another message"; + const char* msg5 = "Foo"; + + fml::SetLogSettings({.min_log_level = -1}); + + FML_LOG(INFO) << msg1; + ReadPacketAndCompare(FX_LOG_INFO, msg1); + CheckSocketEmpty(); + + FML_LOG(WARNING) << msg2; + ReadPacketAndCompare(FX_LOG_WARNING, msg2); + CheckSocketEmpty(); + + FML_LOG(ERROR) << msg3; + ReadPacketAndCompare(FX_LOG_ERROR, msg3); + CheckSocketEmpty(); + + FML_VLOG(1) << msg4; + ReadPacketAndCompare(fx_log_severity_from_verbosity(1), msg4); + CheckSocketEmpty(); + + // VLOG(2) is not enabled so the log gets dropped. + FML_VLOG(2) << msg5; + CheckSocketEmpty(); +} + +#endif + +} // namespace testing +} // namespace fml diff --git a/fml/mapping.cc b/fml/mapping.cc index 64d09e93464fc..254ccafc441ea 100644 --- a/fml/mapping.cc +++ b/fml/mapping.cc @@ -82,7 +82,6 @@ const uint8_t* DataMapping::GetMapping() const { } // NonOwnedMapping - NonOwnedMapping::NonOwnedMapping(const uint8_t* data, size_t size, const ReleaseProc& release_proc) @@ -102,6 +101,46 @@ const uint8_t* NonOwnedMapping::GetMapping() const { return data_; } +// MallocMapping +MallocMapping::MallocMapping() : data_(nullptr), size_(0) {} + +MallocMapping::MallocMapping(uint8_t* data, size_t size) + : data_(data), size_(size) {} + +MallocMapping::MallocMapping(fml::MallocMapping&& mapping) + : data_(mapping.data_), size_(mapping.size_) { + mapping.data_ = nullptr; + mapping.size_ = 0; +} + +MallocMapping::~MallocMapping() { + free(data_); + data_ = nullptr; +} + +MallocMapping MallocMapping::Copy(const void* begin, size_t length) { + auto result = + MallocMapping(reinterpret_cast(malloc(length)), length); + FML_CHECK(result.GetMapping() != nullptr); + memcpy(const_cast(result.GetMapping()), begin, length); + return result; +} + +size_t MallocMapping::GetSize() const { + return size_; +} + +const uint8_t* MallocMapping::GetMapping() const { + return data_; +} + +uint8_t* MallocMapping::Release() { + uint8_t* result = data_; + data_ = nullptr; + size_ = 0; + return result; +} + // Symbol Mapping SymbolMapping::SymbolMapping(fml::RefPtr native_library, diff --git a/fml/mapping.h b/fml/mapping.h index f6b54ed7bbb8a..cd1be8e47d73d 100644 --- a/fml/mapping.h +++ b/fml/mapping.h @@ -125,6 +125,54 @@ class NonOwnedMapping final : public Mapping { FML_DISALLOW_COPY_AND_ASSIGN(NonOwnedMapping); }; +/// A Mapping like NonOwnedMapping, but uses Free as its release proc. +class MallocMapping final : public Mapping { + public: + MallocMapping(); + + /// Creates a MallocMapping for a region of memory (without copying it). + /// The function will `abort()` if the malloc fails. + /// @param data The starting address of the mapping. + /// @param size The size of the mapping in bytes. + MallocMapping(uint8_t* data, size_t size); + + MallocMapping(fml::MallocMapping&& mapping); + + ~MallocMapping() override; + + /// Copies the data from `begin` to `end`. + /// It's templated since void* arithemetic isn't allowed and we want support + /// for `uint8_t` and `char`. + template + static MallocMapping Copy(const T* begin, const T* end) { + FML_DCHECK(end > begin); + size_t length = end - begin; + return Copy(begin, length); + } + + /// Copies a region of memory into a MallocMapping. + /// The function will `abort()` if the malloc fails. + /// @param begin The starting address of where we will copy. + /// @param length The length of the region to copy in bytes. + static MallocMapping Copy(const void* begin, size_t length); + + // |Mapping| + size_t GetSize() const override; + + // |Mapping| + const uint8_t* GetMapping() const override; + + /// Removes ownership of the data buffer. + /// After this is called; the mapping will point to nullptr. + [[nodiscard]] uint8_t* Release(); + + private: + uint8_t* data_; + size_t size_; + + FML_DISALLOW_COPY_AND_ASSIGN(MallocMapping); +}; + class SymbolMapping final : public Mapping { public: SymbolMapping(fml::RefPtr native_library, diff --git a/fml/mapping_unittests.cc b/fml/mapping_unittests.cc new file mode 100644 index 0000000000000..0a7309ccf3c4c --- /dev/null +++ b/fml/mapping_unittests.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/fml/mapping.h" +#include "flutter/testing/testing.h" + +namespace fml { + +TEST(MallocMapping, EmptyContructor) { + MallocMapping mapping; + ASSERT_EQ(nullptr, mapping.GetMapping()); + ASSERT_EQ(0u, mapping.GetSize()); +} + +TEST(MallocMapping, NotEmptyContructor) { + size_t length = 10; + MallocMapping mapping(reinterpret_cast(malloc(length)), length); + ASSERT_NE(nullptr, mapping.GetMapping()); + ASSERT_EQ(length, mapping.GetSize()); +} + +TEST(MallocMapping, MoveConstructor) { + size_t length = 10; + MallocMapping mapping(reinterpret_cast(malloc(length)), length); + MallocMapping moved = std::move(mapping); + + ASSERT_EQ(nullptr, mapping.GetMapping()); + ASSERT_EQ(0u, mapping.GetSize()); + ASSERT_NE(nullptr, moved.GetMapping()); + ASSERT_EQ(length, moved.GetSize()); +} + +TEST(MallocMapping, Copy) { + size_t length = 10; + MallocMapping mapping(reinterpret_cast(malloc(length)), length); + memset(const_cast(mapping.GetMapping()), 0xac, mapping.GetSize()); + MallocMapping copied = + MallocMapping::Copy(mapping.GetMapping(), mapping.GetSize()); + + ASSERT_NE(mapping.GetMapping(), copied.GetMapping()); + ASSERT_EQ(mapping.GetSize(), copied.GetSize()); + ASSERT_EQ( + 0, memcmp(mapping.GetMapping(), copied.GetMapping(), mapping.GetSize())); +} + +TEST(MallocMapping, Release) { + size_t length = 10; + MallocMapping mapping(reinterpret_cast(malloc(length)), length); + free(const_cast(mapping.Release())); + ASSERT_EQ(nullptr, mapping.GetMapping()); + ASSERT_EQ(0u, mapping.GetSize()); +} + +} // namespace fml diff --git a/fml/memory/ref_counted_unittest.cc b/fml/memory/ref_counted_unittest.cc index 4cefb8b5ff5f9..75685cce0144a 100644 --- a/fml/memory/ref_counted_unittest.cc +++ b/fml/memory/ref_counted_unittest.cc @@ -472,13 +472,15 @@ TEST(RefCountedTest, SelfAssignment) { TEST(RefCountedTest, Swap) { MyClass* created1 = nullptr; - bool was_destroyed1 = false; + static bool was_destroyed1; + was_destroyed1 = false; RefPtr r1(MakeRefCounted(&created1, &was_destroyed1)); EXPECT_TRUE(created1); EXPECT_EQ(created1, r1.get()); MyClass* created2 = nullptr; - bool was_destroyed2 = false; + static bool was_destroyed2; + was_destroyed2 = false; RefPtr r2(MakeRefCounted(&created2, &was_destroyed2)); EXPECT_TRUE(created2); EXPECT_EQ(created2, r2.get()); diff --git a/fml/memory/task_runner_checker.cc b/fml/memory/task_runner_checker.cc index dae642dfea8d5..5320c49153b4d 100644 --- a/fml/memory/task_runner_checker.cc +++ b/fml/memory/task_runner_checker.cc @@ -7,14 +7,25 @@ namespace fml { TaskRunnerChecker::TaskRunnerChecker() - : initialized_queue_id_(InitTaskQueueId()){}; + : initialized_queue_id_(InitTaskQueueId()), + subsumed_queue_ids_( + MessageLoopTaskQueues::GetInstance()->GetSubsumedTaskQueueId( + initialized_queue_id_)){}; TaskRunnerChecker::~TaskRunnerChecker() = default; bool TaskRunnerChecker::RunsOnCreationTaskRunner() const { FML_CHECK(fml::MessageLoop::IsInitializedForCurrentThread()); const auto current_queue_id = MessageLoop::GetCurrentTaskQueueId(); - return RunsOnTheSameThread(current_queue_id, initialized_queue_id_); + if (RunsOnTheSameThread(current_queue_id, initialized_queue_id_)) { + return true; + } + for (auto& subsumed : subsumed_queue_ids_) { + if (RunsOnTheSameThread(current_queue_id, subsumed)) { + return true; + } + } + return false; }; bool TaskRunnerChecker::RunsOnTheSameThread(TaskQueueId queue_a, diff --git a/fml/memory/task_runner_checker.h b/fml/memory/task_runner_checker.h index cb36dadcff201..42dbd60cf411d 100644 --- a/fml/memory/task_runner_checker.h +++ b/fml/memory/task_runner_checker.h @@ -22,6 +22,7 @@ class TaskRunnerChecker final { private: TaskQueueId initialized_queue_id_; + std::set subsumed_queue_ids_; TaskQueueId InitTaskQueueId(); }; diff --git a/fml/memory/task_runner_checker_unittest.cc b/fml/memory/task_runner_checker_unittest.cc index fbbc6e4934550..6196384869a5a 100644 --- a/fml/memory/task_runner_checker_unittest.cc +++ b/fml/memory/task_runner_checker_unittest.cc @@ -43,6 +43,7 @@ TEST(TaskRunnerCheckerTests, FailsTheCheckIfOnDifferentTaskRunner) { } TEST(TaskRunnerCheckerTests, SameTaskRunnerRunsOnTheSameThread) { + fml::MessageLoop::EnsureInitializedForCurrentThread(); fml::MessageLoop& loop1 = fml::MessageLoop::GetCurrent(); fml::MessageLoop& loop2 = fml::MessageLoop::GetCurrent(); TaskQueueId a = loop1.GetTaskRunner()->GetTaskQueueId(); @@ -51,6 +52,7 @@ TEST(TaskRunnerCheckerTests, SameTaskRunnerRunsOnTheSameThread) { } TEST(TaskRunnerCheckerTests, RunsOnDifferentThreadsReturnsFalse) { + fml::MessageLoop::EnsureInitializedForCurrentThread(); fml::MessageLoop& loop1 = fml::MessageLoop::GetCurrent(); TaskQueueId a = loop1.GetTaskRunner()->GetTaskQueueId(); fml::AutoResetWaitableEvent latch; @@ -92,14 +94,14 @@ TEST(TaskRunnerCheckerTests, MergedTaskRunnersRunsOnTheSameThread) { fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); const auto raster_thread_merger_ = fml::MakeRefCounted(qid1, qid2); - const int kNumFramesMerged = 5; + const size_t kNumFramesMerged = 5; raster_thread_merger_->MergeWithLease(kNumFramesMerged); // merged, running on the same thread EXPECT_EQ(TaskRunnerChecker::RunsOnTheSameThread(qid1, qid2), true); - for (int i = 0; i < kNumFramesMerged; i++) { + for (size_t i = 0; i < kNumFramesMerged; i++) { ASSERT_TRUE(raster_thread_merger_->IsMerged()); raster_thread_merger_->DecrementLease(); } @@ -115,5 +117,57 @@ TEST(TaskRunnerCheckerTests, MergedTaskRunnersRunsOnTheSameThread) { thread2.join(); } +TEST(TaskRunnerCheckerTests, + PassesRunsOnCreationTaskRunnerIfOnDifferentTaskRunner) { + fml::MessageLoop* loop1 = nullptr; + fml::AutoResetWaitableEvent latch1; + std::thread thread1([&]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + loop1 = &fml::MessageLoop::GetCurrent(); + latch1.Signal(); + loop1->Run(); + }); + + fml::MessageLoop* loop2 = nullptr; + fml::AutoResetWaitableEvent latch2; + std::thread thread2([&]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + loop2 = &fml::MessageLoop::GetCurrent(); + latch2.Signal(); + loop2->Run(); + }); + + latch1.Wait(); + latch2.Wait(); + + fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); + fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->Merge(qid1, qid2); + + std::unique_ptr checker; + + fml::AutoResetWaitableEvent latch3; + loop2->GetTaskRunner()->PostTask([&]() { + checker = std::make_unique(); + EXPECT_EQ(checker->RunsOnCreationTaskRunner(), true); + latch3.Signal(); + }); + latch3.Wait(); + + fml::MessageLoopTaskQueues::GetInstance()->Unmerge(qid1, qid2); + + fml::AutoResetWaitableEvent latch4; + loop2->GetTaskRunner()->PostTask([&]() { + EXPECT_EQ(checker->RunsOnCreationTaskRunner(), true); + latch4.Signal(); + }); + latch4.Wait(); + + loop1->Terminate(); + loop2->Terminate(); + thread1.join(); + thread2.join(); +} + } // namespace testing } // namespace fml diff --git a/fml/memory/thread_checker.h b/fml/memory/thread_checker.h index 5290fc2ba8115..3de97f2d2188d 100644 --- a/fml/memory/thread_checker.h +++ b/fml/memory/thread_checker.h @@ -57,7 +57,7 @@ class ThreadChecker final { char actual_thread[buffer_length]; if (0 == pthread_getname_np(current_thread, actual_thread, buffer_length) && - 0 == pthread_getname_np(self_, actual_thread, buffer_length)) { + 0 == pthread_getname_np(self_, expected_thread, buffer_length)) { FML_DLOG(ERROR) << "IsCreationThreadCurrent expected thread: '" << expected_thread << "' actual thread:'" << actual_thread << "'"; diff --git a/fml/memory/weak_ptr_unittest.cc b/fml/memory/weak_ptr_unittest.cc index e055ad9095409..2ed0b9ae008c5 100644 --- a/fml/memory/weak_ptr_unittest.cc +++ b/fml/memory/weak_ptr_unittest.cc @@ -212,14 +212,14 @@ TEST(TaskRunnerAffineWeakPtrTest, ShouldNotCrashIfRunningOnTheSameTaskRunner) { fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); const auto raster_thread_merger_ = fml::MakeRefCounted(qid1, qid2); - const int kNumFramesMerged = 5; + const size_t kNumFramesMerged = 5; raster_thread_merger_->MergeWithLease(kNumFramesMerged); loop2_task_start_latch.Signal(); loop2_task_finish_latch.Wait(); - for (int i = 0; i < kNumFramesMerged; i++) { + for (size_t i = 0; i < kNumFramesMerged; i++) { ASSERT_TRUE(raster_thread_merger_->IsMerged()); raster_thread_merger_->DecrementLease(); } diff --git a/fml/message.cc b/fml/message.cc deleted file mode 100644 index 4ef7ab3b32274..0000000000000 --- a/fml/message.cc +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/fml/message.h" - -#include "flutter/fml/logging.h" - -namespace fml { - -size_t MessageSerializable::GetSerializableTag() const { - return 0; -}; - -Message::Message() = default; - -Message::~Message() = default; - -static uint32_t NextPowerOfTwoSize(uint32_t x) { - if (x == 0) { - return 1; - } - - --x; - - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - - return x + 1; -} - -const uint8_t* Message::GetBuffer() const { - return buffer_; -} - -size_t Message::GetBufferSize() const { - return buffer_length_; -} - -size_t Message::GetDataLength() const { - return data_length_; -} - -size_t Message::GetSizeRead() const { - return size_read_; -} - -bool Message::Reserve(size_t size) { - if (buffer_length_ >= size) { - return true; - } - return Resize(NextPowerOfTwoSize(size)); -} - -bool Message::Resize(size_t size) { - if (buffer_ == nullptr) { - // This is the initial resize where we have no previous buffer. - FML_DCHECK(buffer_length_ == 0); - - void* buffer = ::malloc(size); - const bool success = buffer != nullptr; - - if (success) { - buffer_ = static_cast(buffer); - buffer_length_ = size; - } - - return success; - } - - FML_DCHECK(size > buffer_length_); - - void* resized = ::realloc(buffer_, size); - - const bool success = resized != nullptr; - - // In case of failure, the input buffer to realloc is still valid. - if (success) { - buffer_ = static_cast(resized); - buffer_length_ = size; - } - - return success; -} - -uint8_t* Message::PrepareEncode(size_t size) { - if (!Reserve(data_length_ + size)) { - return nullptr; - } - - auto old_length = data_length_; - data_length_ += size; - return buffer_ + old_length; -} - -uint8_t* Message::PrepareDecode(size_t size) { - if ((size + size_read_) > buffer_length_) { - return nullptr; - } - auto* buffer = buffer_ + size_read_; - size_read_ += size; - return buffer; -} - -void Message::ResetRead() { - size_read_ = 0; -} - -} // namespace fml diff --git a/fml/message.h b/fml/message.h deleted file mode 100644 index 4b3b725d4c6e9..0000000000000 --- a/fml/message.h +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FML_MESSAGE_H_ -#define FLUTTER_FML_MESSAGE_H_ - -#include -#include -#include -#include -#include -#include - -#include "flutter/fml/compiler_specific.h" -#include "flutter/fml/macros.h" - -namespace fml { - -#define FML_SERIALIZE(message, value) \ - if (!message.Encode(value)) { \ - return false; \ - } - -#define FML_SERIALIZE_TRAITS(message, value, traits) \ - if (!message.Encode(value)) { \ - return false; \ - } - -#define FML_DESERIALIZE(message, value) \ - if (!message.Decode(value)) { \ - return false; \ - } - -#define FML_DESERIALIZE_TRAITS(message, value, traits) \ - if (!message.Decode(value)) { \ - return false; \ - } - -class Message; - -class MessageSerializable { - public: - virtual ~MessageSerializable() = default; - - virtual bool Serialize(Message& message) const = 0; - - virtual bool Deserialize(Message& message) = 0; - - virtual size_t GetSerializableTag() const; -}; - -// The traits passed to the encode/decode calls that accept traits should be -// something like the following. -// -// class MessageSerializableTraits { -// static size_t GetSerializableTag(const T&); -// static std::unique_ptr CreateForSerializableTag(size_t tag); -// }; - -template -struct Serializable : public std::integral_constant< - bool, - std::is_trivially_copyable::value || - std::is_base_of::value> { -}; - -// Utility class to encode and decode |Serializable| types to and from a buffer. -// Elements have to be read back into the same order they were written. -class Message { - public: - Message(); - - ~Message(); - - const uint8_t* GetBuffer() const; - - size_t GetBufferSize() const; - - size_t GetDataLength() const; - - size_t GetSizeRead() const; - - void ResetRead(); - - // Encoders. - - template ::value>> - [[nodiscard]] bool Encode(const T& value) { - if (auto* buffer = PrepareEncode(sizeof(T))) { - ::memcpy(buffer, &value, sizeof(T)); - return true; - } - return false; - } - - [[nodiscard]] bool Encode(const MessageSerializable& value) { - return value.Serialize(*this); - } - - template ::value>> - [[nodiscard]] bool Encode(const std::unique_ptr& value) { - // Encode if null. - if (!Encode(static_cast(value))) { - return false; - } - - if (!value) { - return true; - } - - // Encode the type. - if (!Encode(Traits::GetSerializableTag(*value.get()))) { - return false; - } - - // Encode the value. - if (!Encode(*value.get())) { - return false; - } - - return true; - } - - // Decoders. - - template ::value>> - [[nodiscard]] bool Decode(T& value) { - if (auto* buffer = PrepareDecode(sizeof(T))) { - ::memcpy(&value, buffer, sizeof(T)); - return true; - } - return false; - } - - [[nodiscard]] bool Decode(MessageSerializable& value) { - return value.Deserialize(*this); - } - - template ::value>> - [[nodiscard]] bool Decode(std::unique_ptr& value) { - // Decode if null. - bool is_null = false; - if (!Decode(is_null)) { - return false; - } - - if (is_null) { - return true; - } - - // Decode type. - size_t tag = 0; - if (!Decode(tag)) { - return false; - } - - std::unique_ptr new_value = Traits::CreateForSerializableTag(tag); - if (!new_value) { - return false; - } - - // Decode value. - if (!Decode(*new_value.get())) { - return false; - } - - std::swap(value, new_value); - - return true; - } - - private: - uint8_t* buffer_ = nullptr; - size_t buffer_length_ = 0; - size_t data_length_ = 0; - size_t size_read_ = 0; - - [[nodiscard]] bool Reserve(size_t size); - - [[nodiscard]] bool Resize(size_t size); - - [[nodiscard]] uint8_t* PrepareEncode(size_t size); - - [[nodiscard]] uint8_t* PrepareDecode(size_t size); - - FML_DISALLOW_COPY_AND_ASSIGN(Message); -}; - -} // namespace fml - -#endif // FLUTTER_FML_MESSAGE_H_ diff --git a/fml/message_loop.h b/fml/message_loop.h index 025f8059ec1aa..ccc1f70b93b8d 100644 --- a/fml/message_loop.h +++ b/fml/message_loop.h @@ -13,13 +13,23 @@ namespace fml { class TaskRunner; class MessageLoopImpl; +/// An event loop associated with a thread. +/// +/// This class is the generic front-end to the MessageLoop, differences in +/// implementation based on the running platform are in the subclasses of +/// flutter::MessageLoopImpl (ex flutter::MessageLoopAndroid). +/// +/// For scheduling events on the message loop see flutter::TaskRunner. +/// +/// \see fml::TaskRunner +/// \see fml::MessageLoopImpl +/// \see fml::MessageLoopTaskQueues +/// \see fml::Wakeable class MessageLoop { public: FML_EMBEDDER_ONLY static MessageLoop& GetCurrent(); - bool IsValid() const; - void Run(); void Terminate(); @@ -36,10 +46,15 @@ class MessageLoop { static void EnsureInitializedForCurrentThread(); + /// Returns true if \p EnsureInitializedForCurrentThread has been called on + /// this thread already. static bool IsInitializedForCurrentThread(); ~MessageLoop(); + /// Gets the unique identifier for the TaskQueue associated with the current + /// thread. + /// \see fml::MessageLoopTaskQueues static TaskQueueId GetCurrentTaskQueueId(); private: diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h index 055423fad8a85..23a2eceb15c71 100644 --- a/fml/message_loop_impl.h +++ b/fml/message_loop_impl.h @@ -23,6 +23,11 @@ namespace fml { +/// An abstract class that represents the differences in implementation of a \p +/// fml::MessageLoop depending on the platform. +/// \see fml::MessageLoop +/// \see fml::MessageLoopAndroid +/// \see fml::MessageLoopDarwin class MessageLoopImpl : public Wakeable, public fml::RefCountedThreadSafe { public: diff --git a/fml/message_loop_task_queues.cc b/fml/message_loop_task_queues.cc index 34ddab8819e54..e0146c6ef7c63 100644 --- a/fml/message_loop_task_queues.cc +++ b/fml/message_loop_task_queues.cc @@ -7,9 +7,12 @@ #include "flutter/fml/message_loop_task_queues.h" #include +#include +#include #include "flutter/fml/make_copyable.h" -#include "flutter/fml/message_loop_impl.h" +#include "flutter/fml/task_source.h" +#include "flutter/fml/thread_local.h" namespace fml { @@ -17,19 +20,39 @@ std::mutex MessageLoopTaskQueues::creation_mutex_; const size_t TaskQueueId::kUnmerged = ULONG_MAX; +// Guarded by creation_mutex_. fml::RefPtr MessageLoopTaskQueues::instance_; -TaskQueueEntry::TaskQueueEntry() - : owner_of(_kUnmerged), subsumed_by(_kUnmerged) { +namespace { + +// iOS prior to version 9 prevents c++11 thread_local and __thread specifier, +// having us resort to boxed enum containers. +class TaskSourceGradeHolder { + public: + TaskSourceGrade task_source_grade; + + explicit TaskSourceGradeHolder(TaskSourceGrade task_source_grade_arg) + : task_source_grade(task_source_grade_arg) {} +}; +} // namespace + +// Guarded by creation_mutex_. +FML_THREAD_LOCAL ThreadLocalUniquePtr + tls_task_source_grade; + +TaskQueueEntry::TaskQueueEntry(TaskQueueId created_for_arg) + : subsumed_by(_kUnmerged), created_for(created_for_arg) { wakeable = NULL; task_observers = TaskObservers(); - delayed_tasks = DelayedTaskQueue(); + task_source = std::make_unique(created_for); } fml::RefPtr MessageLoopTaskQueues::GetInstance() { std::scoped_lock creation(creation_mutex_); if (!instance_) { instance_ = fml::MakeRefCounted(); + tls_task_source_grade.reset( + new TaskSourceGradeHolder{TaskSourceGrade::kUnspecified}); } return instance_; } @@ -38,7 +61,7 @@ TaskQueueId MessageLoopTaskQueues::CreateTaskQueue() { std::lock_guard guard(queue_mutex_); TaskQueueId loop_id = TaskQueueId(task_queue_id_counter_); ++task_queue_id_counter_; - queue_entries_[loop_id] = std::make_unique(); + queue_entries_[loop_id] = std::make_unique(loop_id); return loop_id; } @@ -51,36 +74,49 @@ void MessageLoopTaskQueues::Dispose(TaskQueueId queue_id) { std::lock_guard guard(queue_mutex_); const auto& queue_entry = queue_entries_.at(queue_id); FML_DCHECK(queue_entry->subsumed_by == _kUnmerged); - TaskQueueId subsumed = queue_entry->owner_of; - queue_entries_.erase(queue_id); - if (subsumed != _kUnmerged) { + auto& subsumed_set = queue_entry->owner_of; + for (auto& subsumed : subsumed_set) { queue_entries_.erase(subsumed); } + // Erase owner queue_id at last to avoid &subsumed_set from being invalid + queue_entries_.erase(queue_id); } void MessageLoopTaskQueues::DisposeTasks(TaskQueueId queue_id) { std::lock_guard guard(queue_mutex_); const auto& queue_entry = queue_entries_.at(queue_id); FML_DCHECK(queue_entry->subsumed_by == _kUnmerged); - TaskQueueId subsumed = queue_entry->owner_of; - queue_entry->delayed_tasks = {}; - if (subsumed != _kUnmerged) { - queue_entries_.at(subsumed)->delayed_tasks = {}; + auto& subsumed_set = queue_entry->owner_of; + queue_entry->task_source->ShutDown(); + for (auto& subsumed : subsumed_set) { + queue_entries_.at(subsumed)->task_source->ShutDown(); } } -void MessageLoopTaskQueues::RegisterTask(TaskQueueId queue_id, - const fml::closure& task, - fml::TimePoint target_time) { +TaskSourceGrade MessageLoopTaskQueues::GetCurrentTaskSourceGrade() { + std::scoped_lock creation(creation_mutex_); + return tls_task_source_grade.get()->task_source_grade; +} + +void MessageLoopTaskQueues::RegisterTask( + TaskQueueId queue_id, + const fml::closure& task, + fml::TimePoint target_time, + fml::TaskSourceGrade task_source_grade) { std::lock_guard guard(queue_mutex_); size_t order = order_++; const auto& queue_entry = queue_entries_.at(queue_id); - queue_entry->delayed_tasks.push({order, task, target_time}); + queue_entry->task_source->RegisterTask( + {order, task, target_time, task_source_grade}); TaskQueueId loop_to_wake = queue_id; if (queue_entry->subsumed_by != _kUnmerged) { loop_to_wake = queue_entry->subsumed_by; } - WakeUpUnlocked(loop_to_wake, GetNextWakeTimeUnlocked(loop_to_wake)); + + // This can happen when the secondary tasks are paused. + if (HasPendingTasksUnlocked(loop_to_wake)) { + WakeUpUnlocked(loop_to_wake, GetNextWakeTimeUnlocked(loop_to_wake)); + } } bool MessageLoopTaskQueues::HasPendingTasks(TaskQueueId queue_id) const { @@ -94,8 +130,7 @@ fml::closure MessageLoopTaskQueues::GetNextTaskToRun(TaskQueueId queue_id, if (!HasPendingTasksUnlocked(queue_id)) { return nullptr; } - TaskQueueId top_queue = _kUnmerged; - const auto& top = PeekNextTaskUnlocked(queue_id, top_queue); + TaskSource::TopTask top = PeekNextTaskUnlocked(queue_id); if (!HasPendingTasksUnlocked(queue_id)) { WakeUpUnlocked(queue_id, fml::TimePoint::Max()); @@ -103,11 +138,17 @@ fml::closure MessageLoopTaskQueues::GetNextTaskToRun(TaskQueueId queue_id, WakeUpUnlocked(queue_id, GetNextWakeTimeUnlocked(queue_id)); } - if (top.GetTargetTime() > from_time) { + if (top.task.GetTargetTime() > from_time) { return nullptr; } - fml::closure invocation = top.GetTask(); - queue_entries_.at(top_queue)->delayed_tasks.pop(); + fml::closure invocation = top.task.GetTask(); + queue_entries_.at(top.task_queue_id) + ->task_source->PopTask(top.task.GetTaskSourceGrade()); + { + std::scoped_lock creation(creation_mutex_); + const auto task_source_grade = top.task.GetTaskSourceGrade(); + tls_task_source_grade.reset(new TaskSourceGradeHolder{task_source_grade}); + } return invocation; } @@ -126,12 +167,12 @@ size_t MessageLoopTaskQueues::GetNumPendingTasks(TaskQueueId queue_id) const { } size_t total_tasks = 0; - total_tasks += queue_entry->delayed_tasks.size(); + total_tasks += queue_entry->task_source->GetNumPendingTasks(); - TaskQueueId subsumed = queue_entry->owner_of; - if (subsumed != _kUnmerged) { + auto& subsumed_set = queue_entry->owner_of; + for (auto& subsumed : subsumed_set) { const auto& subsumed_entry = queue_entries_.at(subsumed); - total_tasks += subsumed_entry->delayed_tasks.size(); + total_tasks += subsumed_entry->task_source->GetNumPendingTasks(); } return total_tasks; } @@ -163,8 +204,8 @@ std::vector MessageLoopTaskQueues::GetObserversToNotify( observers.push_back(observer.second); } - TaskQueueId subsumed = queue_entries_.at(queue_id)->owner_of; - if (subsumed != _kUnmerged) { + auto& subsumed_set = queue_entries_.at(queue_id)->owner_of; + for (auto& subsumed : subsumed_set) { for (const auto& observer : queue_entries_.at(subsumed)->task_observers) { observers.push_back(observer.second); } @@ -188,22 +229,41 @@ bool MessageLoopTaskQueues::Merge(TaskQueueId owner, TaskQueueId subsumed) { std::lock_guard guard(queue_mutex_); auto& owner_entry = queue_entries_.at(owner); auto& subsumed_entry = queue_entries_.at(subsumed); - - if (owner_entry->owner_of == subsumed) { + auto& subsumed_set = owner_entry->owner_of; + if (subsumed_set.find(subsumed) != subsumed_set.end()) { return true; } - std::vector owner_subsumed_keys = { - owner_entry->owner_of, owner_entry->subsumed_by, subsumed_entry->owner_of, - subsumed_entry->subsumed_by}; + // Won't check owner_entry->owner_of, because it may contains items when + // merged with other different queues. - for (auto key : owner_subsumed_keys) { - if (key != _kUnmerged) { - return false; - } + // Ensure owner_entry->subsumed_by being _kUnmerged + if (owner_entry->subsumed_by != _kUnmerged) { + FML_LOG(WARNING) << "Thread merging failed: owner_entry was already " + "subsumed by others, owner=" + << owner << ", subsumed=" << subsumed + << ", owner->subsumed_by=" << owner_entry->subsumed_by; + return false; } - - owner_entry->owner_of = subsumed; + // Ensure subsumed_entry->owner_of being empty + if (!subsumed_entry->owner_of.empty()) { + FML_LOG(WARNING) + << "Thread merging failed: subsumed_entry already owns others, owner=" + << owner << ", subsumed=" << subsumed + << ", subsumed->owner_of.size()=" << subsumed_entry->owner_of.size(); + return false; + } + // Ensure subsumed_entry->subsumed_by being _kUnmerged + if (subsumed_entry->subsumed_by != _kUnmerged) { + FML_LOG(WARNING) << "Thread merging failed: subsumed_entry was already " + "subsumed by others, owner=" + << owner << ", subsumed=" << subsumed + << ", subsumed->subsumed_by=" + << subsumed_entry->subsumed_by; + return false; + } + // All checking is OK, set merged state. + owner_entry->owner_of.insert(subsumed); subsumed_entry->subsumed_by = owner; if (HasPendingTasksUnlocked(owner)) { @@ -213,16 +273,37 @@ bool MessageLoopTaskQueues::Merge(TaskQueueId owner, TaskQueueId subsumed) { return true; } -bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner) { +bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner, TaskQueueId subsumed) { std::lock_guard guard(queue_mutex_); const auto& owner_entry = queue_entries_.at(owner); - const TaskQueueId subsumed = owner_entry->owner_of; - if (subsumed == _kUnmerged) { + if (owner_entry->owner_of.empty()) { + FML_LOG(WARNING) + << "Thread unmerging failed: owner_entry doesn't own anyone, owner=" + << owner << ", subsumed=" << subsumed; + return false; + } + if (owner_entry->subsumed_by != _kUnmerged) { + FML_LOG(WARNING) + << "Thread unmerging failed: owner_entry was subsumed by others, owner=" + << owner << ", subsumed=" << subsumed + << ", owner_entry->subsumed_by=" << owner_entry->subsumed_by; + return false; + } + if (queue_entries_.at(subsumed)->subsumed_by == _kUnmerged) { + FML_LOG(WARNING) << "Thread unmerging failed: subsumed_entry wasn't " + "subsumed by others, owner=" + << owner << ", subsumed=" << subsumed; + return false; + } + if (owner_entry->owner_of.find(subsumed) == owner_entry->owner_of.end()) { + FML_LOG(WARNING) << "Thread unmerging failed: owner_entry didn't own the " + "given subsumed queue id, owner=" + << owner << ", subsumed=" << subsumed; return false; } queue_entries_.at(subsumed)->subsumed_by = _kUnmerged; - owner_entry->owner_of = _kUnmerged; + owner_entry->owner_of.erase(subsumed); if (HasPendingTasksUnlocked(owner)) { WakeUpUnlocked(owner, GetNextWakeTimeUnlocked(owner)); @@ -238,7 +319,31 @@ bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner) { bool MessageLoopTaskQueues::Owns(TaskQueueId owner, TaskQueueId subsumed) const { std::lock_guard guard(queue_mutex_); - return subsumed == queue_entries_.at(owner)->owner_of; + if (owner == _kUnmerged || subsumed == _kUnmerged) { + return false; + } + auto& subsumed_set = queue_entries_.at(owner)->owner_of; + return subsumed_set.find(subsumed) != subsumed_set.end(); +} + +std::set MessageLoopTaskQueues::GetSubsumedTaskQueueId( + TaskQueueId owner) const { + std::lock_guard guard(queue_mutex_); + return queue_entries_.at(owner)->owner_of; +} + +void MessageLoopTaskQueues::PauseSecondarySource(TaskQueueId queue_id) { + std::lock_guard guard(queue_mutex_); + queue_entries_.at(queue_id)->task_source->PauseSecondary(); +} + +void MessageLoopTaskQueues::ResumeSecondarySource(TaskQueueId queue_id) { + std::lock_guard guard(queue_mutex_); + queue_entries_.at(queue_id)->task_source->ResumeSecondary(); + // Schedule a wake as needed. + if (HasPendingTasksUnlocked(queue_id)) { + WakeUpUnlocked(queue_id, GetNextWakeTimeUnlocked(queue_id)); + } } // Subsumed queues will never have pending tasks. @@ -251,56 +356,55 @@ bool MessageLoopTaskQueues::HasPendingTasksUnlocked( return false; } - if (!entry->delayed_tasks.empty()) { + if (!entry->task_source->IsEmpty()) { return true; } - const TaskQueueId subsumed = entry->owner_of; - if (subsumed == _kUnmerged) { - // this is not an owner and queue is empty. - return false; - } else { - return !queue_entries_.at(subsumed)->delayed_tasks.empty(); - } + auto& subsumed_set = entry->owner_of; + return std::any_of( + subsumed_set.begin(), subsumed_set.end(), [&](const auto& subsumed) { + return !queue_entries_.at(subsumed)->task_source->IsEmpty(); + }); } fml::TimePoint MessageLoopTaskQueues::GetNextWakeTimeUnlocked( TaskQueueId queue_id) const { - TaskQueueId tmp = _kUnmerged; - return PeekNextTaskUnlocked(queue_id, tmp).GetTargetTime(); + return PeekNextTaskUnlocked(queue_id).task.GetTargetTime(); } -const DelayedTask& MessageLoopTaskQueues::PeekNextTaskUnlocked( - TaskQueueId owner, - TaskQueueId& top_queue_id) const { +TaskSource::TopTask MessageLoopTaskQueues::PeekNextTaskUnlocked( + TaskQueueId owner) const { FML_DCHECK(HasPendingTasksUnlocked(owner)); const auto& entry = queue_entries_.at(owner); - const TaskQueueId subsumed = entry->owner_of; - if (subsumed == _kUnmerged) { - top_queue_id = owner; - return entry->delayed_tasks.top(); - } - - const auto& owner_tasks = entry->delayed_tasks; - const auto& subsumed_tasks = queue_entries_.at(subsumed)->delayed_tasks; - - // we are owning another task queue - const bool subsumed_has_task = !subsumed_tasks.empty(); - const bool owner_has_task = !owner_tasks.empty(); - if (owner_has_task && subsumed_has_task) { - const auto owner_task = owner_tasks.top(); - const auto subsumed_task = subsumed_tasks.top(); - if (owner_task > subsumed_task) { - top_queue_id = subsumed; - } else { - top_queue_id = owner; - } - } else if (owner_has_task) { - top_queue_id = owner; - } else { - top_queue_id = subsumed; + if (entry->owner_of.empty()) { + FML_CHECK(!entry->task_source->IsEmpty()); + return entry->task_source->Top(); + } + + // Use optional for the memory of TopTask object. + std::optional top_task; + + std::function top_task_updater = + [&top_task](const TaskSource* source) { + if (source && !source->IsEmpty()) { + TaskSource::TopTask other_task = source->Top(); + if (!top_task.has_value() || top_task->task > other_task.task) { + top_task.emplace(other_task); + } + } + }; + + TaskSource* owner_tasks = entry->task_source.get(); + top_task_updater(owner_tasks); + + for (TaskQueueId subsumed : entry->owner_of) { + TaskSource* subsumed_tasks = queue_entries_.at(subsumed)->task_source.get(); + top_task_updater(subsumed_tasks); } - return queue_entries_.at(top_queue_id)->delayed_tasks.top(); + // At least one task at the top because PeekNextTaskUnlocked() is called after + // HasPendingTasksUnlocked() + FML_CHECK(top_task.has_value()); + return top_task.value(); } } // namespace fml diff --git a/fml/message_loop_task_queues.h b/fml/message_loop_task_queues.h index eb056d6935c03..703c6bcec3b58 100644 --- a/fml/message_loop_task_queues.h +++ b/fml/message_loop_task_queues.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "flutter/fml/closure.h" @@ -14,41 +15,37 @@ #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/synchronization/shared_mutex.h" +#include "flutter/fml/task_queue_id.h" +#include "flutter/fml/task_source.h" #include "flutter/fml/wakeable.h" namespace fml { -class TaskQueueId { - public: - static const size_t kUnmerged; - - explicit TaskQueueId(size_t value) : value_(value) {} - - operator int() const { return value_; } - - private: - size_t value_ = kUnmerged; -}; - static const TaskQueueId _kUnmerged = TaskQueueId(TaskQueueId::kUnmerged); -// This is keyed by the |TaskQueueId| and contains all the queue -// components that make up a single TaskQueue. +/// A collection of tasks and observers associated with one TaskQueue. +/// +/// Often a TaskQueue has a one-to-one relationship with a fml::MessageLoop, +/// this isn't the case when TaskQueues are merged via +/// \p fml::MessageLoopTaskQueues::Merge. class TaskQueueEntry { public: using TaskObservers = std::map; Wakeable* wakeable; TaskObservers task_observers; - DelayedTaskQueue delayed_tasks; + std::unique_ptr task_source; - // Note: Both of these can be _kUnmerged, which indicates that - // this queue has not been merged or subsumed. OR exactly one - // of these will be _kUnmerged, if owner_of is _kUnmerged, it means - // that the queue has been subsumed or else it owns another queue. - TaskQueueId owner_of; + /// Set of the TaskQueueIds which is owned by this TaskQueue. If the set is + /// empty, this TaskQueue does not own any other TaskQueues. + std::set owner_of; + + /// Identifies the TaskQueue that subsumes this TaskQueue. If it is _kUnmerged + /// it indicates that this TaskQueue is not owned by any other TaskQueue. TaskQueueId subsumed_by; - TaskQueueEntry(); + TaskQueueId created_for; + + explicit TaskQueueEntry(TaskQueueId created_for); private: FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TaskQueueEntry); @@ -59,9 +56,12 @@ enum class FlushType { kAll, }; -// This class keeps track of all the tasks and observers that -// need to be run on it's MessageLoopImpl. This also wakes up the -// loop at the required times. +/// A singleton container for all tasks and observers associated with all +/// fml::MessageLoops. +/// +/// This also wakes up the loop at the required times. +/// \see fml::MessageLoop +/// \see fml::Wakeable class MessageLoopTaskQueues : public fml::RefCountedThreadSafe { public: @@ -79,7 +79,9 @@ class MessageLoopTaskQueues void RegisterTask(TaskQueueId queue_id, const fml::closure& task, - fml::TimePoint target_time); + fml::TimePoint target_time, + fml::TaskSourceGrade task_source_grade = + fml::TaskSourceGrade::kUnspecified); bool HasPendingTasks(TaskQueueId queue_id) const; @@ -87,6 +89,8 @@ class MessageLoopTaskQueues size_t GetNumPendingTasks(TaskQueueId queue_id) const; + static TaskSourceGrade GetCurrentTaskSourceGrade(); + // Observers methods. void AddTaskObserver(TaskQueueId queue_id, @@ -106,22 +110,33 @@ class MessageLoopTaskQueues // to it. It is not aware of whether a queue is merged or not. Same with // task observers. // 2. When we get the tasks to run now, we look at both the queue_ids - // for the owner, subsumed will spin. - // 3. Each task queue can only be merged and subsumed once. + // for the owner and the subsumed task queues. + // 3. One TaskQueue can subsume multiple other TaskQueues. A TaskQueue can be + // in exactly one of the following three states: + // a. Be an owner of multiple other TaskQueues. + // b. Be subsumed by a TaskQueue (an owner can never be subsumed). + // c. Be independent, i.e, neither owner nor be subsumed. // // Methods currently aware of the merged state of the queues: // HasPendingTasks, GetNextTaskToRun, GetNumPendingTasks - - // This method returns false if either the owner or subsumed has already been - // merged with something else. bool Merge(TaskQueueId owner, TaskQueueId subsumed); - // Will return false if the owner has not been merged before. - bool Unmerge(TaskQueueId owner); + // Will return false if the owner has not been merged before, or owner was + // subsumed by others, or subsumed wasn't subsumed by others, or owner + // didn't own the given subsumed queue id. + bool Unmerge(TaskQueueId owner, TaskQueueId subsumed); - // Returns true if owner owns the subsumed task queue. + /// Returns \p true if \p owner owns the \p subsumed task queue. bool Owns(TaskQueueId owner, TaskQueueId subsumed) const; + // Returns the subsumed task queue if any or |TaskQueueId::kUnmerged| + // otherwise. + std::set GetSubsumedTaskQueueId(TaskQueueId owner) const; + + void PauseSecondarySource(TaskQueueId queue_id); + + void ResumeSecondarySource(TaskQueueId queue_id); + private: class MergedQueuesRunner; @@ -133,8 +148,7 @@ class MessageLoopTaskQueues bool HasPendingTasksUnlocked(TaskQueueId queue_id) const; - const DelayedTask& PeekNextTaskUnlocked(TaskQueueId owner, - TaskQueueId& top_queue_id) const; + TaskSource::TopTask PeekNextTaskUnlocked(TaskQueueId owner) const; fml::TimePoint GetNextWakeTimeUnlocked(TaskQueueId queue_id) const; diff --git a/fml/message_loop_task_queues_merge_unmerge_unittests.cc b/fml/message_loop_task_queues_merge_unmerge_unittests.cc index 0c928eba41157..238028430f16a 100644 --- a/fml/message_loop_task_queues_merge_unmerge_unittests.cc +++ b/fml/message_loop_task_queues_merge_unmerge_unittests.cc @@ -9,6 +9,7 @@ #include "flutter/fml/message_loop_task_queues.h" #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/time/chrono_timestamp_provider.h" #include "gtest/gtest.h" namespace fml { @@ -29,7 +30,7 @@ class TestWakeable : public fml::Wakeable { static int CountRemainingTasks(fml::RefPtr task_queue, const TaskQueueId& queue_id, bool run_invocation = false) { - const auto now = fml::TimePoint::Now(); + const auto now = ChronoTicksSinceEpoch(); int count = 0; fml::closure invocation; do { @@ -53,12 +54,12 @@ TEST(MessageLoopTaskQueueMergeUnmerge, auto queue_id_2 = task_queue->CreateTaskQueue(); task_queue->RegisterTask( - queue_id_1, []() {}, fml::TimePoint::Now()); + queue_id_1, []() {}, ChronoTicksSinceEpoch()); ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); task_queue->Merge(queue_id_1, queue_id_2); task_queue->RegisterTask( - queue_id_1, []() {}, fml::TimePoint::Now()); + queue_id_1, []() {}, ChronoTicksSinceEpoch()); ASSERT_EQ(2u, task_queue->GetNumPendingTasks(queue_id_1)); ASSERT_EQ(0u, task_queue->GetNumPendingTasks(queue_id_2)); @@ -72,7 +73,7 @@ TEST(MessageLoopTaskQueueMergeUnmerge, auto queue_id_2 = task_queue->CreateTaskQueue(); task_queue->RegisterTask( - queue_id_2, []() {}, fml::TimePoint::Now()); + queue_id_2, []() {}, ChronoTicksSinceEpoch()); ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_2)); task_queue->Merge(queue_id_1, queue_id_2); @@ -87,9 +88,9 @@ TEST(MessageLoopTaskQueueMergeUnmerge, MergeUnmergeTasksPreserved) { auto queue_id_2 = task_queue->CreateTaskQueue(); task_queue->RegisterTask( - queue_id_1, []() {}, fml::TimePoint::Now()); + queue_id_1, []() {}, ChronoTicksSinceEpoch()); task_queue->RegisterTask( - queue_id_2, []() {}, fml::TimePoint::Now()); + queue_id_2, []() {}, ChronoTicksSinceEpoch()); ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_2)); @@ -99,34 +100,87 @@ TEST(MessageLoopTaskQueueMergeUnmerge, MergeUnmergeTasksPreserved) { ASSERT_EQ(2u, task_queue->GetNumPendingTasks(queue_id_1)); ASSERT_EQ(0u, task_queue->GetNumPendingTasks(queue_id_2)); - task_queue->Unmerge(queue_id_1); + task_queue->Unmerge(queue_id_1, queue_id_2); ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_1)); ASSERT_EQ(1u, task_queue->GetNumPendingTasks(queue_id_2)); } -TEST(MessageLoopTaskQueueMergeUnmerge, MergeFailIfAlreadyMergedOrSubsumed) { +/// Multiple standalone engines scene +TEST(MessageLoopTaskQueueMergeUnmerge, + OneCanOwnMultipleQueuesAndUnmergeIndependently) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); - auto queue_id_1 = task_queue->CreateTaskQueue(); auto queue_id_2 = task_queue->CreateTaskQueue(); auto queue_id_3 = task_queue->CreateTaskQueue(); - task_queue->Merge(queue_id_1, queue_id_2); + // merge + ASSERT_TRUE(task_queue->Merge(queue_id_1, queue_id_2)); + ASSERT_TRUE(task_queue->Owns(queue_id_1, queue_id_2)); + ASSERT_FALSE(task_queue->Owns(queue_id_1, queue_id_3)); + + ASSERT_TRUE(task_queue->Merge(queue_id_1, queue_id_3)); + ASSERT_TRUE(task_queue->Owns(queue_id_1, queue_id_2)); + ASSERT_TRUE(task_queue->Owns(queue_id_1, queue_id_3)); + + // unmerge + ASSERT_TRUE(task_queue->Unmerge(queue_id_1, queue_id_2)); + ASSERT_FALSE(task_queue->Owns(queue_id_1, queue_id_2)); + ASSERT_TRUE(task_queue->Owns(queue_id_1, queue_id_3)); + + ASSERT_TRUE(task_queue->Unmerge(queue_id_1, queue_id_3)); + ASSERT_FALSE(task_queue->Owns(queue_id_1, queue_id_2)); + ASSERT_FALSE(task_queue->Owns(queue_id_1, queue_id_3)); +} + +TEST(MessageLoopTaskQueueMergeUnmerge, + CannotMergeSameQueueToTwoDifferentOwners) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + auto queue = task_queue->CreateTaskQueue(); + auto owner_1 = task_queue->CreateTaskQueue(); + auto owner_2 = task_queue->CreateTaskQueue(); + + ASSERT_TRUE(task_queue->Merge(owner_1, queue)); + ASSERT_FALSE(task_queue->Merge(owner_2, queue)); +} + +TEST(MessageLoopTaskQueueMergeUnmerge, MergeFailIfAlreadySubsumed) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); - ASSERT_FALSE(task_queue->Merge(queue_id_1, queue_id_3)); + auto queue_id_1 = task_queue->CreateTaskQueue(); + auto queue_id_2 = task_queue->CreateTaskQueue(); + auto queue_id_3 = task_queue->CreateTaskQueue(); + + ASSERT_TRUE(task_queue->Merge(queue_id_1, queue_id_2)); ASSERT_FALSE(task_queue->Merge(queue_id_2, queue_id_3)); + ASSERT_FALSE(task_queue->Merge(queue_id_2, queue_id_1)); } -TEST(MessageLoopTaskQueueMergeUnmerge, UnmergeFailsOnSubsumed) { +TEST(MessageLoopTaskQueueMergeUnmerge, + MergeFailIfAlreadyOwnsButTryToBeSubsumed) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); auto queue_id_1 = task_queue->CreateTaskQueue(); auto queue_id_2 = task_queue->CreateTaskQueue(); + auto queue_id_3 = task_queue->CreateTaskQueue(); task_queue->Merge(queue_id_1, queue_id_2); + // A recursively linked merging will fail + ASSERT_FALSE(task_queue->Merge(queue_id_3, queue_id_1)); +} - ASSERT_FALSE(task_queue->Unmerge(queue_id_2)); +TEST(MessageLoopTaskQueueMergeUnmerge, UnmergeFailsOnSubsumedOrNeverMerged) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + + auto queue_id_1 = task_queue->CreateTaskQueue(); + auto queue_id_2 = task_queue->CreateTaskQueue(); + auto queue_id_3 = task_queue->CreateTaskQueue(); + + task_queue->Merge(queue_id_1, queue_id_2); + ASSERT_FALSE(task_queue->Unmerge(queue_id_2, queue_id_3)); + ASSERT_FALSE(task_queue->Unmerge(queue_id_1, queue_id_3)); + ASSERT_FALSE(task_queue->Unmerge(queue_id_3, queue_id_1)); + ASSERT_FALSE(task_queue->Unmerge(queue_id_2, queue_id_1)); } TEST(MessageLoopTaskQueueMergeUnmerge, MergeInvokesBothWakeables) { @@ -145,7 +199,7 @@ TEST(MessageLoopTaskQueueMergeUnmerge, MergeInvokesBothWakeables) { new TestWakeable([&](fml::TimePoint wake_time) { latch.CountDown(); })); task_queue->RegisterTask( - queue_id_1, []() {}, fml::TimePoint::Now()); + queue_id_1, []() {}, ChronoTicksSinceEpoch()); task_queue->Merge(queue_id_1, queue_id_2); @@ -171,12 +225,12 @@ TEST(MessageLoopTaskQueueMergeUnmerge, new TestWakeable([&](fml::TimePoint wake_time) { latch_2.Signal(); })); task_queue->RegisterTask( - queue_id_1, []() {}, fml::TimePoint::Now()); + queue_id_1, []() {}, ChronoTicksSinceEpoch()); task_queue->RegisterTask( - queue_id_2, []() {}, fml::TimePoint::Now()); + queue_id_2, []() {}, ChronoTicksSinceEpoch()); task_queue->Merge(queue_id_1, queue_id_2); - task_queue->Unmerge(queue_id_1); + task_queue->Unmerge(queue_id_1, queue_id_2); CountRemainingTasks(task_queue, queue_id_1); @@ -197,7 +251,7 @@ TEST(MessageLoopTaskQueueMergeUnmerge, GetTasksToRunNowBlocksMerge) { merge_end; task_queue->RegisterTask( - queue_id_1, []() {}, fml::TimePoint::Now()); + queue_id_1, []() {}, ChronoTicksSinceEpoch()); task_queue->SetWakeable(queue_id_1, new TestWakeable([&](fml::TimePoint wake_time) { wake_up_start.Signal(); @@ -246,10 +300,10 @@ TEST(MessageLoopTaskQueueMergeUnmerge, task_queue->RegisterTask( queue_id_2, [&]() { task_queue->Merge(queue_id_1, queue_id_2); }, - fml::TimePoint::Now()); + ChronoTicksSinceEpoch()); task_queue->RegisterTask( - queue_id_2, []() {}, fml::TimePoint::Now()); + queue_id_2, []() {}, ChronoTicksSinceEpoch()); ASSERT_EQ(CountRemainingTasks(task_queue, queue_id_2, true), 1); ASSERT_EQ(CountRemainingTasks(task_queue, queue_id_1, true), 1); diff --git a/fml/message_loop_task_queues_unittests.cc b/fml/message_loop_task_queues_unittests.cc index 052f70c2ae745..48f6fb871ef90 100644 --- a/fml/message_loop_task_queues_unittests.cc +++ b/fml/message_loop_task_queues_unittests.cc @@ -6,10 +6,13 @@ #include "flutter/fml/message_loop_task_queues.h" +#include +#include #include #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/time/chrono_timestamp_provider.h" #include "gtest/gtest.h" namespace fml { @@ -53,13 +56,42 @@ TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); auto queue_id = task_queue->CreateTaskQueue(); task_queue->RegisterTask( - queue_id, [] {}, fml::TimePoint::Now()); + queue_id, [] {}, ChronoTicksSinceEpoch()); task_queue->RegisterTask( queue_id, [] {}, fml::TimePoint::Max()); ASSERT_TRUE(task_queue->HasPendingTasks(queue_id)); ASSERT_TRUE(task_queue->GetNumPendingTasks(queue_id) == 2); } +TEST(MessageLoopTaskQueue, RegisterTasksOnMergedQueuesAndCount) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + auto platform_queue = task_queue->CreateTaskQueue(); + auto raster_queue = task_queue->CreateTaskQueue(); + // A task in platform_queue + task_queue->RegisterTask( + platform_queue, []() {}, fml::TimePoint::Now()); + // A task in raster_queue + task_queue->RegisterTask( + raster_queue, []() {}, fml::TimePoint::Now()); + ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1); + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1); + + ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue)); + task_queue->Merge(platform_queue, raster_queue); + ASSERT_TRUE(task_queue->Owns(platform_queue, raster_queue)); + + ASSERT_TRUE(task_queue->HasPendingTasks(platform_queue)); + ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 2); + // The task count of subsumed queue is 0 + ASSERT_FALSE(task_queue->HasPendingTasks(raster_queue)); + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 0); + + task_queue->Unmerge(platform_queue, raster_queue); + ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue)); + ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1); + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1); +} + TEST(MessageLoopTaskQueue, PreserveTaskOrdering) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); auto queue_id = task_queue->CreateTaskQueue(); @@ -67,15 +99,15 @@ TEST(MessageLoopTaskQueue, PreserveTaskOrdering) { // order: 0 task_queue->RegisterTask( - queue_id, [&test_val]() { test_val = 1; }, fml::TimePoint::Now()); + queue_id, [&test_val]() { test_val = 1; }, ChronoTicksSinceEpoch()); // order: 1 task_queue->RegisterTask( - queue_id, [&test_val]() { test_val = 2; }, fml::TimePoint::Now()); + queue_id, [&test_val]() { test_val = 2; }, ChronoTicksSinceEpoch()); - const auto now = fml::TimePoint::Now(); + const auto now = ChronoTicksSinceEpoch(); int expected_value = 1; - for (;;) { + while (true) { fml::closure invocation = task_queue->GetNextTaskToRun(queue_id, now); if (!invocation) { break; @@ -86,6 +118,123 @@ TEST(MessageLoopTaskQueue, PreserveTaskOrdering) { } } +TEST(MessageLoopTaskQueue, RegisterTasksOnMergedQueuesPreserveTaskOrdering) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + auto platform_queue = task_queue->CreateTaskQueue(); + auto raster1_queue = task_queue->CreateTaskQueue(); + auto raster2_queue = task_queue->CreateTaskQueue(); + int test_val = 0; + + // order 0 in raster1_queue + task_queue->RegisterTask( + raster1_queue, [&test_val]() { test_val = 0; }, fml::TimePoint::Now()); + + // order 1 in platform_queue + task_queue->RegisterTask( + platform_queue, [&test_val]() { test_val = 1; }, fml::TimePoint::Now()); + + // order 2 in raster2_queue + task_queue->RegisterTask( + raster2_queue, [&test_val]() { test_val = 2; }, fml::TimePoint::Now()); + + task_queue->Merge(platform_queue, raster1_queue); + ASSERT_TRUE(task_queue->Owns(platform_queue, raster1_queue)); + task_queue->Merge(platform_queue, raster2_queue); + ASSERT_TRUE(task_queue->Owns(platform_queue, raster2_queue)); + const auto now = fml::TimePoint::Now(); + int expected_value = 0; + // Right order: + // "test_val = 0" in raster1_queue + // "test_val = 1" in platform_queue + // "test_val = 2" in raster2_queue + while (true) { + fml::closure invocation = task_queue->GetNextTaskToRun(platform_queue, now); + if (!invocation) { + break; + } + invocation(); + ASSERT_TRUE(test_val == expected_value); + expected_value++; + } +} + +TEST(MessageLoopTaskQueue, UnmergeRespectTheOriginalTaskOrderingInQueues) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + auto platform_queue = task_queue->CreateTaskQueue(); + auto raster_queue = task_queue->CreateTaskQueue(); + int test_val = 0; + + // order 0 in platform_queue + task_queue->RegisterTask( + platform_queue, [&test_val]() { test_val = 0; }, fml::TimePoint::Now()); + // order 1 in platform_queue + task_queue->RegisterTask( + platform_queue, [&test_val]() { test_val = 1; }, fml::TimePoint::Now()); + // order 2 in raster_queue + task_queue->RegisterTask( + raster_queue, [&test_val]() { test_val = 2; }, fml::TimePoint::Now()); + // order 3 in raster_queue + task_queue->RegisterTask( + raster_queue, [&test_val]() { test_val = 3; }, fml::TimePoint::Now()); + // order 4 in platform_queue + task_queue->RegisterTask( + platform_queue, [&test_val]() { test_val = 4; }, fml::TimePoint::Now()); + // order 5 in raster_queue + task_queue->RegisterTask( + raster_queue, [&test_val]() { test_val = 5; }, fml::TimePoint::Now()); + + ASSERT_TRUE(task_queue->Merge(platform_queue, raster_queue)); + ASSERT_TRUE(task_queue->Owns(platform_queue, raster_queue)); + const auto now = fml::TimePoint::Now(); + // The right order after merged and consumed 3 tasks: + // "test_val = 0" in platform_queue + // "test_val = 1" in platform_queue + // "test_val = 2" in raster_queue (running on platform) + for (int i = 0; i < 3; i++) { + fml::closure invocation = task_queue->GetNextTaskToRun(platform_queue, now); + ASSERT_FALSE(!invocation); + invocation(); + ASSERT_TRUE(test_val == i); + } + ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 3); + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 0); + + ASSERT_TRUE(task_queue->Unmerge(platform_queue, raster_queue)); + ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue)); + + // The right order after unmerged and left 3 tasks: + // "test_val = 3" in raster_queue + // "test_val = 4" in platform_queue + // "test_val = 5" in raster_queue + + // platform_queue has 1 task left: "test_val = 4" + { + ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 1); + fml::closure invocation = task_queue->GetNextTaskToRun(platform_queue, now); + ASSERT_FALSE(!invocation); + invocation(); + ASSERT_TRUE(test_val == 4); + ASSERT_TRUE(task_queue->GetNumPendingTasks(platform_queue) == 0); + } + + // raster_queue has 2 tasks left: "test_val = 3" and "test_val = 5" + { + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 2); + fml::closure invocation = task_queue->GetNextTaskToRun(raster_queue, now); + ASSERT_FALSE(!invocation); + invocation(); + ASSERT_TRUE(test_val == 3); + } + { + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 1); + fml::closure invocation = task_queue->GetNextTaskToRun(raster_queue, now); + ASSERT_FALSE(!invocation); + invocation(); + ASSERT_TRUE(test_val == 5); + ASSERT_TRUE(task_queue->GetNumPendingTasks(raster_queue) == 0); + } +} + void TestNotifyObservers(fml::TaskQueueId queue_id) { auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); std::vector observers = @@ -122,7 +271,7 @@ TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) { [&num_wakes](fml::TimePoint wake_time) { ++num_wakes; })); task_queue->RegisterTask( - queue_id, []() {}, fml::TimePoint::Now()); + queue_id, []() {}, ChronoTicksSinceEpoch()); task_queue->RegisterTask( queue_id, []() {}, fml::TimePoint::Max()); @@ -145,7 +294,7 @@ TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) { task_queue->RegisterTask( queue_id, []() {}, fml::TimePoint::Max()); - const auto now = fml::TimePoint::Now(); + const auto now = ChronoTicksSinceEpoch(); expected = now; task_queue->RegisterTask( queue_id, []() {}, now); @@ -185,63 +334,81 @@ TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) { ASSERT_FALSE(task_queue->Owns(queue_id, queue_id)); } -// TODO(chunhtai): This unit-test is flaky and sometimes fails asynchronizely -// after the test has finished. -// https://github.com/flutter/flutter/issues/43858 -TEST(MessageLoopTaskQueue, DISABLED_ConcurrentQueueAndTaskCreatingCounts) { +TEST(MessageLoopTaskQueue, QueueDoNotOwnUnmergedTaskQueueId) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + ASSERT_FALSE(task_queue->Owns(task_queue->CreateTaskQueue(), _kUnmerged)); + ASSERT_FALSE(task_queue->Owns(_kUnmerged, task_queue->CreateTaskQueue())); + ASSERT_FALSE(task_queue->Owns(_kUnmerged, _kUnmerged)); +} + +TEST(MessageLoopTaskQueue, QueueOwnsMergedTaskQueueId) { + auto task_queue = fml::MessageLoopTaskQueues::GetInstance(); + auto platform_queue = task_queue->CreateTaskQueue(); + auto raster_queue = task_queue->CreateTaskQueue(); + ASSERT_FALSE(task_queue->Owns(platform_queue, raster_queue)); + ASSERT_FALSE(task_queue->Owns(raster_queue, platform_queue)); + task_queue->Merge(platform_queue, raster_queue); + ASSERT_TRUE(task_queue->Owns(platform_queue, raster_queue)); + ASSERT_FALSE(task_queue->Owns(raster_queue, platform_queue)); +} + +//------------------------------------------------------------------------------ +/// Verifies that tasks can be added to task queues concurrently. +/// +TEST(MessageLoopTaskQueue, ConcurrentQueueAndTaskCreatingCounts) { auto task_queues = fml::MessageLoopTaskQueues::GetInstance(); - const int base_queue_id = task_queues->CreateTaskQueue(); - const int num_queues = 100; - std::atomic_bool created[num_queues * 3]; - std::atomic_int num_tasks[num_queues * 3]; - std::mutex task_count_mutex[num_queues * 3]; - std::atomic_int done = 0; + // kThreadCount threads post kThreadTaskCount tasks each to kTaskQueuesCount + // task queues. Each thread picks a task queue randomly for each task. + constexpr size_t kThreadCount = 4; + constexpr size_t kTaskQueuesCount = 2; + constexpr size_t kThreadTaskCount = 500; - for (int i = 0; i < num_queues * 3; i++) { - num_tasks[i] = 0; - created[i] = false; + std::vector task_queue_ids; + for (size_t i = 0; i < kTaskQueuesCount; ++i) { + task_queue_ids.emplace_back(task_queues->CreateTaskQueue()); } - auto creation_func = [&] { - for (int i = 0; i < num_queues; i++) { - fml::TaskQueueId queue_id = task_queues->CreateTaskQueue(); - int limit = queue_id - base_queue_id; - created[limit] = true; - - for (int cur_q = 1; cur_q < limit; cur_q++) { - if (created[cur_q]) { - std::scoped_lock counter(task_count_mutex[cur_q]); - int cur_num_tasks = rand() % 10; - for (int k = 0; k < cur_num_tasks; k++) { - task_queues->RegisterTask( - fml::TaskQueueId(base_queue_id + cur_q), [] {}, - fml::TimePoint::Now()); - } - num_tasks[cur_q] += cur_num_tasks; - } - } + ASSERT_EQ(task_queue_ids.size(), kTaskQueuesCount); + + fml::CountDownLatch tasks_posted_latch(kThreadCount); + + auto thread_main = [&]() { + for (size_t i = 0; i < kThreadTaskCount; i++) { + const auto current_task_queue_id = + task_queue_ids[std::rand() % kTaskQueuesCount]; + const auto empty_task = []() {}; + // The timepoint doesn't matter as the queue is never going to be drained. + const auto task_timepoint = ChronoTicksSinceEpoch(); + + task_queues->RegisterTask(current_task_queue_id, empty_task, + task_timepoint); } - done++; + + tasks_posted_latch.CountDown(); }; - std::thread creation_1(creation_func); - std::thread creation_2(creation_func); - - while (done < 2) { - for (int i = 0; i < num_queues * 3; i++) { - if (created[i]) { - std::scoped_lock counter(task_count_mutex[i]); - int num_pending = task_queues->GetNumPendingTasks( - fml::TaskQueueId(base_queue_id + i)); - int num_added = num_tasks[i]; - ASSERT_EQ(num_pending, num_added); - } - } + std::vector threads; + + for (size_t i = 0; i < kThreadCount; i++) { + threads.emplace_back(std::thread{thread_main}); } - creation_1.join(); - creation_2.join(); + ASSERT_EQ(threads.size(), kThreadCount); + + for (size_t i = 0; i < kThreadCount; i++) { + threads[i].join(); + } + + // All tasks have been posted by now. Check that they are all pending. + + size_t pending_tasks = 0u; + std::for_each(task_queue_ids.begin(), task_queue_ids.end(), + [&](const auto& queue) { + pending_tasks += task_queues->GetNumPendingTasks(queue); + }); + + ASSERT_EQ(pending_tasks, kThreadCount * kThreadTaskCount); } TEST(MessageLoopTaskQueue, RegisterTaskWakesUpOwnerQueue) { @@ -262,8 +429,8 @@ TEST(MessageLoopTaskQueue, RegisterTaskWakesUpOwnerQueue) { ASSERT_FALSE(true); })); - auto time1 = fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(1); - auto time2 = fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(2); + auto time1 = ChronoTicksSinceEpoch() + fml::TimeDelta::FromMilliseconds(1); + auto time2 = ChronoTicksSinceEpoch() + fml::TimeDelta::FromMilliseconds(2); ASSERT_EQ(0UL, wakes.size()); diff --git a/fml/message_loop_unittests.cc b/fml/message_loop_unittests.cc index 02e798786a121..ab209ed1d642c 100644 --- a/fml/message_loop_unittests.cc +++ b/fml/message_loop_unittests.cc @@ -14,6 +14,7 @@ #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" +#include "flutter/fml/time/chrono_timestamp_provider.h" #include "gtest/gtest.h" #define TIMESENSITIVE(x) TimeSensitiveTest_##x @@ -116,7 +117,7 @@ TEST(MessageLoop, DelayedTasksAtSameTimeAreRunInOrder) { auto& loop = fml::MessageLoop::GetCurrent(); size_t current = 0; const auto now_plus_some = - fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(2); + fml::ChronoTicksSinceEpoch() + fml::TimeDelta::FromMilliseconds(2); for (size_t i = 0; i < count; i++) { loop.GetTaskRunner()->PostTaskForTime( PLATFORM_SPECIFIC_CAPTURE(&terminated, i, ¤t)() { @@ -159,10 +160,10 @@ TEST(MessageLoop, TIMESENSITIVE(SingleDelayedTaskByDelta)) { std::thread thread([&checked]() { fml::MessageLoop::EnsureInitializedForCurrentThread(); auto& loop = fml::MessageLoop::GetCurrent(); - auto begin = fml::TimePoint::Now(); + auto begin = fml::ChronoTicksSinceEpoch(); loop.GetTaskRunner()->PostDelayedTask( [begin, &checked]() { - auto delta = fml::TimePoint::Now() - begin; + auto delta = fml::ChronoTicksSinceEpoch() - begin; auto ms = delta.ToMillisecondsF(); ASSERT_GE(ms, 3); ASSERT_LE(ms, 7); @@ -181,17 +182,17 @@ TEST(MessageLoop, TIMESENSITIVE(SingleDelayedTaskForTime)) { std::thread thread([&checked]() { fml::MessageLoop::EnsureInitializedForCurrentThread(); auto& loop = fml::MessageLoop::GetCurrent(); - auto begin = fml::TimePoint::Now(); + auto begin = fml::ChronoTicksSinceEpoch(); loop.GetTaskRunner()->PostTaskForTime( [begin, &checked]() { - auto delta = fml::TimePoint::Now() - begin; + auto delta = fml::ChronoTicksSinceEpoch() - begin; auto ms = delta.ToMillisecondsF(); ASSERT_GE(ms, 3); ASSERT_LE(ms, 7); checked = true; fml::MessageLoop::GetCurrent().Terminate(); }, - fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(5)); + fml::ChronoTicksSinceEpoch() + fml::TimeDelta::FromMilliseconds(5)); loop.Run(); }); thread.join(); @@ -205,10 +206,10 @@ TEST(MessageLoop, TIMESENSITIVE(MultipleDelayedTasksWithIncreasingDeltas)) { fml::MessageLoop::EnsureInitializedForCurrentThread(); auto& loop = fml::MessageLoop::GetCurrent(); for (int target_ms = 0 + 2; target_ms < count + 2; target_ms++) { - auto begin = fml::TimePoint::Now(); + auto begin = fml::ChronoTicksSinceEpoch(); loop.GetTaskRunner()->PostDelayedTask( PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() { - auto delta = fml::TimePoint::Now() - begin; + auto delta = fml::ChronoTicksSinceEpoch() - begin; auto ms = delta.ToMillisecondsF(); ASSERT_GE(ms, target_ms - 2); ASSERT_LE(ms, target_ms + 2); @@ -232,10 +233,10 @@ TEST(MessageLoop, TIMESENSITIVE(MultipleDelayedTasksWithDecreasingDeltas)) { fml::MessageLoop::EnsureInitializedForCurrentThread(); auto& loop = fml::MessageLoop::GetCurrent(); for (int target_ms = count + 2; target_ms > 0 + 2; target_ms--) { - auto begin = fml::TimePoint::Now(); + auto begin = fml::ChronoTicksSinceEpoch(); loop.GetTaskRunner()->PostDelayedTask( PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() { - auto delta = fml::TimePoint::Now() - begin; + auto delta = fml::ChronoTicksSinceEpoch() - begin; auto ms = delta.ToMillisecondsF(); ASSERT_GE(ms, target_ms - 2); ASSERT_LE(ms, target_ms + 2); diff --git a/fml/message_unittests.cc b/fml/message_unittests.cc deleted file mode 100644 index aff9fc4cbd3be..0000000000000 --- a/fml/message_unittests.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/fml/message.h" - -#include "gtest/gtest.h" - -namespace fml { - -struct TestStruct { - int a = 12; - char b = 'x'; - float c = 99.0f; -}; - -TEST(MessageTest, CanEncodeTriviallyCopyableTypes) { - Message message; - ASSERT_TRUE(message.Encode(12)); - ASSERT_TRUE(message.Encode(11.0f)); - ASSERT_TRUE(message.Encode('a')); - - TestStruct s; - ASSERT_TRUE(message.Encode(s)); - ASSERT_GE(message.GetDataLength(), 0u); - ASSERT_GE(message.GetBufferSize(), 0u); - ASSERT_EQ(message.GetSizeRead(), 0u); -} - -TEST(MessageTest, CanDecodeTriviallyCopyableTypes) { - Message message; - ASSERT_TRUE(message.Encode(12)); - ASSERT_TRUE(message.Encode(11.0f)); - ASSERT_TRUE(message.Encode('a')); - TestStruct s; - s.a = 10; - s.b = 'y'; - s.c = 11.1f; - - ASSERT_TRUE(message.Encode(s)); - - ASSERT_GE(message.GetDataLength(), 0u); - ASSERT_GE(message.GetBufferSize(), 0u); - ASSERT_EQ(message.GetSizeRead(), 0u); - - int int1 = 0; - ASSERT_TRUE(message.Decode(int1)); - ASSERT_EQ(12, int1); - - float float1 = 0.0f; - ASSERT_TRUE(message.Decode(float1)); - ASSERT_EQ(float1, 11.0f); - - char char1 = 'x'; - ASSERT_TRUE(message.Decode(char1)); - ASSERT_EQ(char1, 'a'); - - TestStruct s1; - ASSERT_TRUE(message.Decode(s1)); - ASSERT_EQ(s1.a, 10); - ASSERT_EQ(s1.b, 'y'); - ASSERT_EQ(s1.c, 11.1f); - - ASSERT_NE(message.GetSizeRead(), 0u); - ASSERT_EQ(message.GetDataLength(), message.GetSizeRead()); -} - -} // namespace fml diff --git a/fml/paths.cc b/fml/paths.cc index 1566726f053d1..b55f447cb1f6f 100644 --- a/fml/paths.cc +++ b/fml/paths.cc @@ -52,5 +52,13 @@ std::string SanitizeURIEscapedCharacters(const std::string& str) { return result; } +std::pair GetExecutableDirectoryPath() { + auto path = GetExecutablePath(); + if (!path.first) { + return {false, ""}; + } + return {true, fml::paths::GetDirectoryName(path.second)}; +} + } // namespace paths } // namespace fml diff --git a/fml/paths.h b/fml/paths.h index 69408317a24b5..b692a7b9e7cc9 100644 --- a/fml/paths.h +++ b/fml/paths.h @@ -13,6 +13,7 @@ namespace fml { namespace paths { +std::pair GetExecutablePath(); std::pair GetExecutableDirectoryPath(); // Get the directory to the application's caches directory. diff --git a/fml/platform/android/jni_util.cc b/fml/platform/android/jni_util.cc index 7627fe3b2de3f..d5937c90af77b 100644 --- a/fml/platform/android/jni_util.cc +++ b/fml/platform/android/jni_util.cc @@ -10,6 +10,7 @@ #include #include "flutter/fml/logging.h" +#include "flutter/fml/thread_local.h" namespace fml { namespace jni { @@ -18,6 +19,13 @@ static JavaVM* g_jvm = nullptr; #define ASSERT_NO_EXCEPTION() FML_CHECK(env->ExceptionCheck() == JNI_FALSE); +struct JNIDetach { + ~JNIDetach() { DetachFromVM(); } +}; + +// Thread-local object that will detach from JNI during thread shutdown; +FML_THREAD_LOCAL fml::ThreadLocalUniquePtr tls_jni_detach; + void InitJavaVM(JavaVM* vm) { FML_DCHECK(g_jvm == nullptr); g_jvm = vm; @@ -26,7 +34,13 @@ void InitJavaVM(JavaVM* vm) { JNIEnv* AttachCurrentThread() { FML_DCHECK(g_jvm != nullptr) << "Trying to attach to current thread without calling InitJavaVM first."; + JNIEnv* env = nullptr; + if (g_jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4) == + JNI_OK) { + return env; + } + JavaVMAttachArgs args; args.version = JNI_VERSION_1_4; args.group = nullptr; @@ -40,6 +54,10 @@ JNIEnv* AttachCurrentThread() { } jint ret = g_jvm->AttachCurrentThread(&env, &args); FML_DCHECK(JNI_OK == ret); + + FML_DCHECK(tls_jni_detach.get() == nullptr); + tls_jni_detach.reset(new JNIDetach()); + return env; } @@ -114,14 +132,33 @@ ScopedJavaLocalRef VectorToStringArray( ScopedJavaLocalRef string_clazz(env, env->FindClass("java/lang/String")); FML_DCHECK(!string_clazz.is_null()); - jobjectArray joa = + jobjectArray java_array = env->NewObjectArray(vector.size(), string_clazz.obj(), NULL); ASSERT_NO_EXCEPTION(); for (size_t i = 0; i < vector.size(); ++i) { ScopedJavaLocalRef item = StringToJavaString(env, vector[i]); - env->SetObjectArrayElement(joa, i, item.obj()); + env->SetObjectArrayElement(java_array, i, item.obj()); } - return ScopedJavaLocalRef(env, joa); + return ScopedJavaLocalRef(env, java_array); +} + +ScopedJavaLocalRef VectorToBufferArray( + JNIEnv* env, + const std::vector>& vector) { + FML_DCHECK(env); + ScopedJavaLocalRef byte_buffer_clazz( + env, env->FindClass("java/nio/ByteBuffer")); + FML_DCHECK(!byte_buffer_clazz.is_null()); + jobjectArray java_array = + env->NewObjectArray(vector.size(), byte_buffer_clazz.obj(), NULL); + ASSERT_NO_EXCEPTION(); + for (size_t i = 0; i < vector.size(); ++i) { + ScopedJavaLocalRef item( + env, + env->NewDirectByteBuffer((void*)(vector[i].data()), vector[i].size())); + env->SetObjectArrayElement(java_array, i, item.obj()); + } + return ScopedJavaLocalRef(env, java_array); } bool HasException(JNIEnv* env) { @@ -136,6 +173,17 @@ bool ClearException(JNIEnv* env) { return true; } +bool CheckException(JNIEnv* env) { + if (!HasException(env)) + return true; + + jthrowable exception = env->ExceptionOccurred(); + env->ExceptionClear(); + FML_LOG(ERROR) << fml::jni::GetJavaExceptionInfo(env, exception); + env->DeleteLocalRef(exception); + return false; +} + std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { ScopedJavaLocalRef throwable_clazz( env, env->FindClass("java/lang/Throwable")); diff --git a/fml/platform/android/jni_util.h b/fml/platform/android/jni_util.h index 9fe32242c503c..da056f627bc2e 100644 --- a/fml/platform/android/jni_util.h +++ b/fml/platform/android/jni_util.h @@ -17,6 +17,8 @@ namespace jni { void InitJavaVM(JavaVM* vm); +// Returns a JNI environment for the current thread. +// Attaches the thread to JNI if needed. JNIEnv* AttachCurrentThread(); void DetachFromVM(); @@ -32,10 +34,15 @@ ScopedJavaLocalRef VectorToStringArray( JNIEnv* env, const std::vector& vector); +ScopedJavaLocalRef VectorToBufferArray( + JNIEnv* env, + const std::vector>& vector); + bool HasException(JNIEnv* env); bool ClearException(JNIEnv* env); +bool CheckException(JNIEnv* env); std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable); } // namespace jni diff --git a/fml/platform/android/message_loop_android.h b/fml/platform/android/message_loop_android.h index b73b06e3db2c7..6ff26be0fd373 100644 --- a/fml/platform/android/message_loop_android.h +++ b/fml/platform/android/message_loop_android.h @@ -21,6 +21,10 @@ struct UniqueLooperTraits { static void Free(ALooper* value) { ::ALooper_release(value); } }; +/// Android implementation of \p MessageLoopImpl. +/// +/// This implemenation wraps usage of Android's \p looper. +/// \see https://developer.android.com/ndk/reference/group/looper class MessageLoopAndroid : public MessageLoopImpl { private: fml::UniqueObject looper_; diff --git a/fml/platform/android/paths_android.cc b/fml/platform/android/paths_android.cc index 36f5844a15c3b..0df6d0ec44bde 100644 --- a/fml/platform/android/paths_android.cc +++ b/fml/platform/android/paths_android.cc @@ -9,7 +9,7 @@ namespace fml { namespace paths { -std::pair GetExecutableDirectoryPath() { +std::pair GetExecutablePath() { return {false, ""}; } diff --git a/fml/platform/darwin/cf_utils.h b/fml/platform/darwin/cf_utils.h index 00ed82d1f4411..0dc984ee89baf 100644 --- a/fml/platform/darwin/cf_utils.h +++ b/fml/platform/darwin/cf_utils.h @@ -41,9 +41,6 @@ class CFRef { } void Reset(T instance = nullptr) { - if (instance_ == instance) { - return; - } if (instance_ != nullptr) { CFRelease(instance_); } diff --git a/fml/platform/darwin/paths_darwin.mm b/fml/platform/darwin/paths_darwin.mm index 72e6f35a80949..1ce0ebfbae47b 100644 --- a/fml/platform/darwin/paths_darwin.mm +++ b/fml/platform/darwin/paths_darwin.mm @@ -11,9 +11,9 @@ namespace fml { namespace paths { -std::pair GetExecutableDirectoryPath() { +std::pair GetExecutablePath() { @autoreleasepool { - return {true, GetDirectoryName([NSBundle mainBundle].executablePath.UTF8String)}; + return {true, [NSBundle mainBundle].executablePath.UTF8String}; } } diff --git a/fml/platform/fuchsia/message_loop_fuchsia.cc b/fml/platform/fuchsia/message_loop_fuchsia.cc index 9a3c1c0ef8d32..0fac826a741d0 100644 --- a/fml/platform/fuchsia/message_loop_fuchsia.cc +++ b/fml/platform/fuchsia/message_loop_fuchsia.cc @@ -8,11 +8,25 @@ #include #include #include +#include "flutter/fml/platform/fuchsia/task_observers.h" namespace fml { -MessageLoopFuchsia::MessageLoopFuchsia() - : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) { +namespace { + +// See comment on `ExecuteAfterTaskObservers` for explanation. +static void LoopEpilogue(async_loop_t*, void*) { + ExecuteAfterTaskObservers(); +} + +constexpr async_loop_config_t kLoopConfig = { + .make_default_for_current_thread = false, + .epilogue = &LoopEpilogue, +}; + +} // namespace + +MessageLoopFuchsia::MessageLoopFuchsia() : loop_(&kLoopConfig) { async_set_default_dispatcher(loop_.dispatcher()); } diff --git a/fml/platform/fuchsia/paths_fuchsia.cc b/fml/platform/fuchsia/paths_fuchsia.cc index e26b32d8ce4c7..47c29b9792af4 100644 --- a/fml/platform/fuchsia/paths_fuchsia.cc +++ b/fml/platform/fuchsia/paths_fuchsia.cc @@ -8,7 +8,7 @@ namespace fml { namespace paths { -std::pair GetExecutableDirectoryPath() { +std::pair GetExecutablePath() { return {false, ""}; } diff --git a/shell/platform/fuchsia/flutter/task_observers.cc b/fml/platform/fuchsia/task_observers.cc similarity index 87% rename from shell/platform/fuchsia/flutter/task_observers.cc rename to fml/platform/fuchsia/task_observers.cc index 7a623684ff66e..d3a9ea1ede617 100644 --- a/shell/platform/fuchsia/flutter/task_observers.cc +++ b/fml/platform/fuchsia/task_observers.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "task_observers.h" +#include "flutter/fml/platform/fuchsia/task_observers.h" #include -namespace flutter_runner { +namespace fml { thread_local std::map tTaskObservers; @@ -27,4 +27,4 @@ void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key) { tTaskObservers.erase(key); } -} // namespace flutter_runner +} // namespace fml diff --git a/fml/platform/fuchsia/task_observers.h b/fml/platform/fuchsia/task_observers.h new file mode 100644 index 0000000000000..de3c2e47317fe --- /dev/null +++ b/fml/platform/fuchsia/task_observers.h @@ -0,0 +1,40 @@ +// 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_FUCHSIA_TASK_OBSERVERS_H_ +#define FLUTTER_SHELL_PLATFORM_FUCHSIA_TASK_OBSERVERS_H_ + +#include + +namespace fml { + +// Executes all closures that were registered via +// `CurrentMessageLoopAddAfterTaskObserver` on this thread. +// +// WARNING(fxbug.dev/77957): These task observers are distinct from the task +// observers that can be specified via `fml::MessageLoop::AddTaskObserver` and +// they behave differently! +// +// Task observers registered via `fml::MessageLoop::AddTaskObserver` only fire +// after work that was posted via the `fml::MessageLoop`'s `TaskRunner` +// completes. Work that is posted directly to the Fuchsia message loop (e.g. +// using `async::PostTask(async_get_default_dispatcher(), ...)`) is invisible to +// `fml::MessageLoop`, so the `fml::MessageLoop`'s task observers don't fire. +// +// The task observers registered with `CurrentMessageLoopAddAfterTaskObserver`, +// however, fire after _every_ work item is completed, regardless of whether it +// was posted to the Fuchsia message loop directly or via `fml::MessageLoop`. +// +// These two mechanisms are redundant and confusing, so we should fix it +// somehow. +void ExecuteAfterTaskObservers(); + +void CurrentMessageLoopAddAfterTaskObserver(intptr_t key, + fit::closure observer); + +void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key); + +} // namespace fml + +#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_TASK_OBSERVERS_H_ diff --git a/fml/platform/linux/paths_linux.cc b/fml/platform/linux/paths_linux.cc index 41983819b6f76..6d77eb07d3f8f 100644 --- a/fml/platform/linux/paths_linux.cc +++ b/fml/platform/linux/paths_linux.cc @@ -9,15 +9,14 @@ namespace fml { namespace paths { -std::pair GetExecutableDirectoryPath() { +std::pair GetExecutablePath() { const int path_size = 255; char path[path_size] = {0}; auto read_size = ::readlink("/proc/self/exe", path, path_size); if (read_size == -1) { return {false, ""}; } - return {true, fml::paths::GetDirectoryName( - std::string{path, static_cast(read_size)})}; + return {true, std::string{path, static_cast(read_size)}}; } fml::UniqueFD GetCachesDirectory() { diff --git a/fml/platform/linux/timerfd.cc b/fml/platform/linux/timerfd.cc index 0dbcb89ad3d25..166b861909663 100644 --- a/fml/platform/linux/timerfd.cc +++ b/fml/platform/linux/timerfd.cc @@ -8,6 +8,7 @@ #include #include "flutter/fml/eintr_wrapper.h" +#include "flutter/fml/logging.h" #if FML_TIMERFD_AVAILABLE == 0 @@ -48,6 +49,9 @@ bool TimerRearm(int fd, fml::TimePoint time_point) { spec.it_interval = spec.it_value; // single expiry. int result = ::timerfd_settime(fd, TFD_TIMER_ABSTIME, &spec, nullptr); + if (result != 0) { + FML_DLOG(ERROR) << "timerfd_settime err:" << strerror(errno); + } return result == 0; } diff --git a/fml/platform/posix/file_posix.cc b/fml/platform/posix/file_posix.cc index f6a90942a9391..018be839c1701 100644 --- a/fml/platform/posix/file_posix.cc +++ b/fml/platform/posix/file_posix.cc @@ -16,6 +16,7 @@ #include "flutter/fml/eintr_wrapper.h" #include "flutter/fml/logging.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/trace_event.h" #include "flutter/fml/unique_fd.h" namespace fml { @@ -72,6 +73,7 @@ fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, const char* path, bool create_if_necessary, FilePermission permission) { + TRACE_EVENT0("flutter", "fml::OpenFile"); if (path == nullptr) { return {}; } diff --git a/fml/platform/win/file_win.cc b/fml/platform/win/file_win.cc index 2815584ae4b82..7b0e785f40c0e 100644 --- a/fml/platform/win/file_win.cc +++ b/fml/platform/win/file_win.cc @@ -7,10 +7,14 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include #include "flutter/fml/build_config.h" @@ -21,6 +25,34 @@ namespace fml { static std::string GetFullHandlePath(const fml::UniqueFD& handle) { + // Although the documentation claims that GetFinalPathNameByHandle is + // supported for UWP apps, turns out it returns ACCESS_DENIED in this case + // hence the need to workaround it by maintaining a map of file handles to + // absolute paths populated by fml::OpenDirectory. +#ifdef WINUWP + std::optional found = + fml::internal::os_win::UniqueFDTraits::GetCacheEntry(handle.get()); + + if (found) { + FILE_ID_INFO info; + + BOOL result = GetFileInformationByHandleEx( + handle.get(), FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &info, + sizeof(FILE_ID_INFO)); + + // Assuming it was possible to retrieve fileinfo, compare the id field. The + // handle hasn't been reused if the file identifier is the same as when it + // was cached + if (result && memcmp(found.value().id.Identifier, info.FileId.Identifier, + sizeof(FILE_ID_INFO))) { + return WideStringToString(found.value().filename); + } else { + fml::internal::os_win::UniqueFDTraits::RemoveCacheEntry(handle.get()); + } + } + + return std::string(); +#else wchar_t buffer[MAX_PATH] = {0}; const DWORD buffer_size = ::GetFinalPathNameByHandle( handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED); @@ -30,6 +62,7 @@ static std::string GetFullHandlePath(const fml::UniqueFD& handle) { return {}; } return WideStringToString({buffer, buffer_size}); +#endif } static std::string GetAbsolutePath(const fml::UniqueFD& base_directory, @@ -223,6 +256,22 @@ fml::UniqueFD OpenDirectory(const char* path, return {}; } +#ifdef WINUWP + FILE_ID_INFO info; + + BOOL result = GetFileInformationByHandleEx( + handle, FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &info, + sizeof(FILE_ID_INFO)); + + // Only cache if it is possible to get valid a fileinformation to extract the + // fileid to ensure correct handle versioning. + if (result) { + fml::internal::os_win::DirCacheEntry fc{file_name, info.FileId}; + + fml::internal::os_win::UniqueFDTraits::StoreCacheEntry(handle, fc); + } +#endif + return fml::UniqueFD{handle}; } diff --git a/fml/platform/win/mapping_win.cc b/fml/platform/win/mapping_win.cc index 1497ddc3e4259..b32178f54d4e9 100644 --- a/fml/platform/win/mapping_win.cc +++ b/fml/platform/win/mapping_win.cc @@ -88,7 +88,7 @@ FileMapping::FileMapping(const fml::UniqueFD& fd, MapViewOfFile(mapping_handle_.get(), desired_access, 0, 0, mapping_size)); if (mapping == nullptr) { - FML_DLOG(ERROR) << "Could not setup file mapping. " + FML_DLOG(ERROR) << "Could not set up file mapping. " << GetLastErrorMessage(); return; } diff --git a/fml/platform/win/paths_win.cc b/fml/platform/win/paths_win.cc index 6787a56ee181e..4a94e5b6560b8 100644 --- a/fml/platform/win/paths_win.cc +++ b/fml/platform/win/paths_win.cc @@ -53,7 +53,7 @@ size_t LastSeparator(const std::string& path) { } // namespace -std::pair GetExecutableDirectoryPath() { +std::pair GetExecutablePath() { HMODULE module = GetModuleHandle(NULL); if (module == NULL) { return {false, ""}; @@ -63,7 +63,7 @@ std::pair GetExecutableDirectoryPath() { if (read_size == 0 || read_size == MAX_PATH) { return {false, ""}; } - return {true, GetDirectoryName(std::string{path, read_size})}; + return {true, std::string{path, read_size}}; } std::string AbsolutePath(const std::string& path) { diff --git a/fml/raster_thread_merger.cc b/fml/raster_thread_merger.cc index cfeeea3dcb527..f14d64ed8ac28 100644 --- a/fml/raster_thread_merger.cc +++ b/fml/raster_thread_merger.cc @@ -10,24 +10,48 @@ namespace fml { -const int RasterThreadMerger::kLeaseNotSet = -1; - RasterThreadMerger::RasterThreadMerger(fml::TaskQueueId platform_queue_id, fml::TaskQueueId gpu_queue_id) + : RasterThreadMerger( + MakeRefCounted(platform_queue_id, gpu_queue_id), + platform_queue_id, + gpu_queue_id) {} + +RasterThreadMerger::RasterThreadMerger( + fml::RefPtr shared_merger, + fml::TaskQueueId platform_queue_id, + fml::TaskQueueId gpu_queue_id) : platform_queue_id_(platform_queue_id), gpu_queue_id_(gpu_queue_id), - task_queues_(fml::MessageLoopTaskQueues::GetInstance()), - lease_term_(kLeaseNotSet), - enabled_(true) { - FML_CHECK(!task_queues_->Owns(platform_queue_id_, gpu_queue_id_)); -} + shared_merger_(shared_merger), + enabled_(true) {} void RasterThreadMerger::SetMergeUnmergeCallback(const fml::closure& callback) { merge_unmerge_callback_ = callback; } +const fml::RefPtr& +RasterThreadMerger::GetSharedRasterThreadMerger() const { + return shared_merger_; +} + +fml::RefPtr +RasterThreadMerger::CreateOrShareThreadMerger( + const fml::RefPtr& parent_merger, + TaskQueueId platform_id, + TaskQueueId raster_id) { + if (parent_merger && parent_merger->platform_queue_id_ == platform_id && + parent_merger->gpu_queue_id_ == raster_id) { + auto shared_merger = parent_merger->GetSharedRasterThreadMerger(); + return fml::MakeRefCounted(shared_merger, platform_id, + raster_id); + } else { + return fml::MakeRefCounted(platform_id, raster_id); + } +} + void RasterThreadMerger::MergeWithLease(size_t lease_term) { - std::scoped_lock lock(lease_term_mutex_); + std::scoped_lock lock(mutex_); if (TaskQueuesAreSame()) { return; } @@ -41,30 +65,27 @@ void RasterThreadMerger::MergeWithLease(size_t lease_term) { return; } - bool success = task_queues_->Merge(platform_queue_id_, gpu_queue_id_); + bool success = shared_merger_->MergeWithLease(this, lease_term); if (success && merge_unmerge_callback_ != nullptr) { merge_unmerge_callback_(); } - FML_CHECK(success) << "Unable to merge the raster and platform threads."; - lease_term_ = lease_term; merged_condition_.notify_one(); } -void RasterThreadMerger::UnMergeNow() { - std::scoped_lock lock(lease_term_mutex_); +void RasterThreadMerger::UnMergeNowIfLastOne() { + std::scoped_lock lock(mutex_); + if (TaskQueuesAreSame()) { return; } if (!IsEnabledUnSafe()) { return; } - lease_term_ = 0; - bool success = task_queues_->Unmerge(platform_queue_id_); + bool success = shared_merger_->UnMergeNowIfLastOne(this); if (success && merge_unmerge_callback_ != nullptr) { merge_unmerge_callback_(); } - FML_CHECK(success) << "Unable to un-merge the raster and platform threads."; } bool RasterThreadMerger::IsOnPlatformThread() const { @@ -80,34 +101,34 @@ bool RasterThreadMerger::IsOnRasterizingThread() const { } void RasterThreadMerger::ExtendLeaseTo(size_t lease_term) { + FML_DCHECK(lease_term > 0) << "lease_term should be positive."; if (TaskQueuesAreSame()) { return; } - std::scoped_lock lock(lease_term_mutex_); - FML_DCHECK(IsMergedUnSafe()) << "lease_term should be positive."; - if (lease_term_ != kLeaseNotSet && - static_cast(lease_term) > lease_term_) { - lease_term_ = lease_term; + std::scoped_lock lock(mutex_); + if (!IsEnabledUnSafe()) { + return; } + shared_merger_->ExtendLeaseTo(this, lease_term); } bool RasterThreadMerger::IsMerged() { - std::scoped_lock lock(lease_term_mutex_); + std::scoped_lock lock(mutex_); return IsMergedUnSafe(); } void RasterThreadMerger::Enable() { - std::scoped_lock lock(lease_term_mutex_); + std::scoped_lock lock(mutex_); enabled_ = true; } void RasterThreadMerger::Disable() { - std::scoped_lock lock(lease_term_mutex_); + std::scoped_lock lock(mutex_); enabled_ = false; } bool RasterThreadMerger::IsEnabled() { - std::scoped_lock lock(lease_term_mutex_); + std::scoped_lock lock(mutex_); return IsEnabledUnSafe(); } @@ -116,7 +137,7 @@ bool RasterThreadMerger::IsEnabledUnSafe() const { } bool RasterThreadMerger::IsMergedUnSafe() const { - return lease_term_ > 0 || TaskQueuesAreSame(); + return TaskQueuesAreSame() || shared_merger_->IsMergedUnSafe(); } bool RasterThreadMerger::TaskQueuesAreSame() const { @@ -128,7 +149,7 @@ void RasterThreadMerger::WaitUntilMerged() { return; } FML_CHECK(IsOnPlatformThread()); - std::unique_lock lock(lease_term_mutex_); + std::unique_lock lock(mutex_); merged_condition_.wait(lock, [&] { return IsMergedUnSafe(); }); } @@ -136,20 +157,18 @@ RasterThreadStatus RasterThreadMerger::DecrementLease() { if (TaskQueuesAreSame()) { return RasterThreadStatus::kRemainsMerged; } - std::unique_lock lock(lease_term_mutex_); + std::scoped_lock lock(mutex_); if (!IsMergedUnSafe()) { return RasterThreadStatus::kRemainsUnmerged; } if (!IsEnabledUnSafe()) { return RasterThreadStatus::kRemainsMerged; } - FML_DCHECK(lease_term_ > 0) - << "lease_term should always be positive when merged."; - lease_term_--; - if (lease_term_ == 0) { - // |UnMergeNow| is going to acquire the lock again. - lock.unlock(); - UnMergeNow(); + bool unmerged_after_decrement = shared_merger_->DecrementLease(this); + if (unmerged_after_decrement) { + if (merge_unmerge_callback_ != nullptr) { + merge_unmerge_callback_(); + } return RasterThreadStatus::kUnmergedNow; } diff --git a/fml/raster_thread_merger.h b/fml/raster_thread_merger.h index 9e38b23a555ee..bcad1904e1513 100644 --- a/fml/raster_thread_merger.h +++ b/fml/raster_thread_merger.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/message_loop_task_queues.h" +#include "flutter/fml/shared_thread_merger.h" namespace fml { @@ -22,6 +23,11 @@ enum class RasterThreadStatus { kUnmergedNow }; +/// This class is a client and proxy between the rasterizer and +/// |SharedThreadMerger|. The multiple |RasterThreadMerger| instances with same +/// owner_queue_id and same subsumed_queue_id share the same +/// |SharedThreadMerger| instance. Whether they share the same inner instance is +/// determined by |RasterThreadMerger::CreateOrShareThreadMerger| method. class RasterThreadMerger : public fml::RefCountedThreadSafe { public: @@ -36,14 +42,28 @@ class RasterThreadMerger // When task queues are statically merged this method becomes no-op. void MergeWithLease(size_t lease_term); - // Un-merges the threads now, and resets the lease term to 0. + // Gets the shared merger from current merger object + const fml::RefPtr& GetSharedRasterThreadMerger() const; + + /// Creates a new merger from parent, share the inside shared_merger member + /// when the platform_queue_id and raster_queue_id are same, otherwise create + /// a new shared_merger instance + static fml::RefPtr CreateOrShareThreadMerger( + const fml::RefPtr& parent_merger, + TaskQueueId platform_id, + TaskQueueId raster_id); + + // Un-merges the threads now if current caller is the last merge caller, + // and it resets the lease term to 0, otherwise it will remove the caller + // record and return. The multiple caller records were recorded after + // |MergeWithLease| or |ExtendLeaseTo| method. // // Must be executed on the raster task runner. // // If the task queues are the same, we consider them statically merged. // When task queues are statically merged, we never unmerge them and // this method becomes no-op. - void UnMergeNow(); + void UnMergeNowIfLastOne(); // If the task queues are the same, we consider them statically merged. // When task queues are statically merged this method becomes no-op. @@ -56,6 +76,9 @@ class RasterThreadMerger // When task queues are statically merged this method becomes no-op. RasterThreadStatus DecrementLease(); + // The method is locked by current instance, and asks the shared instance of + // SharedThreadMerger and the merging state is determined by the + // lease_term_ counter. bool IsMerged(); // Waits until the threads are merged. @@ -63,9 +86,6 @@ class RasterThreadMerger // Must run on the platform task runner. void WaitUntilMerged(); - RasterThreadMerger(fml::TaskQueueId platform_queue_id, - fml::TaskQueueId gpu_queue_id); - // Returns true if the current thread owns rasterizing. // When the threads are merged, platform thread owns rasterizing. // When un-merged, raster thread owns rasterizing. @@ -78,12 +98,12 @@ class RasterThreadMerger void Enable(); // Disables the thread merger. Once disabled, any call to - // |MergeWithLease| or |UnMergeNow| results in a noop. + // |MergeWithLease| or |UnMergeNowIfLastOne| results in a noop. void Disable(); // Whether the thread merger is enabled. By default, the thread merger is - // enabled. If false, calls to |MergeWithLease| or |UnMergeNow| results in a - // noop. + // enabled. If false, calls to |MergeWithLease| or |UnMergeNowIfLastOne| + // or |ExtendLeaseTo| or |DecrementLease| results in a noop. bool IsEnabled(); // Registers a callback that can be used to clean up global state right after @@ -94,13 +114,18 @@ class RasterThreadMerger void SetMergeUnmergeCallback(const fml::closure& callback); private: - static const int kLeaseNotSet; fml::TaskQueueId platform_queue_id_; fml::TaskQueueId gpu_queue_id_; - fml::RefPtr task_queues_; - std::atomic_int lease_term_; + + RasterThreadMerger(fml::TaskQueueId platform_queue_id, + fml::TaskQueueId gpu_queue_id); + RasterThreadMerger(fml::RefPtr shared_merger, + fml::TaskQueueId platform_queue_id, + fml::TaskQueueId gpu_queue_id); + + const fml::RefPtr shared_merger_; std::condition_variable merged_condition_; - std::mutex lease_term_mutex_; + std::mutex mutex_; fml::closure merge_unmerge_callback_; bool enabled_; diff --git a/fml/raster_thread_merger_unittests.cc b/fml/raster_thread_merger_unittests.cc index 030d82ed8a3dc..339cd174752dd 100644 --- a/fml/raster_thread_merger_unittests.cc +++ b/fml/raster_thread_merger_unittests.cc @@ -6,63 +6,74 @@ #include "flutter/fml/raster_thread_merger.h" -#include #include +#include "flutter/fml/memory/ref_ptr.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" +#include "flutter/fml/thread.h" #include "gtest/gtest.h" namespace fml { namespace testing { -TEST(RasterThreadMerger, RemainMergedTillLeaseExpires) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); +/// A mock task queue NOT calling MessageLoop->Run() in thread +struct TaskQueueWrapper { + fml::MessageLoop* loop = nullptr; + + /// The waiter for message loop initialized ok + fml::AutoResetWaitableEvent latch; + + /// The waiter for thread finished + fml::AutoResetWaitableEvent term; + + /// This field must below latch and term member, because + /// cpp standard reference: + /// non-static data members are initialized in the order they were declared in + /// the class definition + std::thread thread; + + TaskQueueWrapper() + : thread([this]() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + loop = &fml::MessageLoop::GetCurrent(); + latch.Signal(); + term.Wait(); + }) { + latch.Wait(); + } - fml::MessageLoop* loop2 = nullptr; - fml::AutoResetWaitableEvent latch2; - fml::AutoResetWaitableEvent term2; - std::thread thread2([&loop2, &latch2, &term2]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop2 = &fml::MessageLoop::GetCurrent(); - latch2.Signal(); - term2.Wait(); - }); + ~TaskQueueWrapper() { + term.Signal(); + thread.join(); + } - latch1.Wait(); - latch2.Wait(); + fml::TaskQueueId GetTaskQueueId() const { + return loop->GetTaskRunner()->GetTaskQueueId(); + } +}; - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); - fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto raster_thread_merger_ = +TEST(RasterThreadMerger, RemainMergedTillLeaseExpires) { + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); - const int kNumFramesMerged = 5; + const size_t kNumFramesMerged = 5; - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->MergeWithLease(kNumFramesMerged); + raster_thread_merger->MergeWithLease(kNumFramesMerged); - for (int i = 0; i < kNumFramesMerged; i++) { - ASSERT_TRUE(raster_thread_merger_->IsMerged()); - raster_thread_merger_->DecrementLease(); + for (size_t i = 0; i < kNumFramesMerged; i++) { + ASSERT_TRUE(raster_thread_merger->IsMerged()); + raster_thread_merger->DecrementLease(); } - ASSERT_FALSE(raster_thread_merger_->IsMerged()); - - term1.Signal(); - term2.Signal(); - thread1.join(); - thread2.join(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); } TEST(RasterThreadMerger, IsNotOnRasterizingThread) { @@ -89,32 +100,32 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) { fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto raster_thread_merger_ = + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2); loop1->GetTaskRunner()->PostTask([&]() { - ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_FALSE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); pre_merge.CountDown(); }); loop2->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2); pre_merge.CountDown(); }); pre_merge.Wait(); - raster_thread_merger_->MergeWithLease(1); + raster_thread_merger->MergeWithLease(1); loop1->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); post_merge.CountDown(); }); @@ -122,26 +133,26 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) { loop2->GetTaskRunner()->PostTask([&]() { // this will be false since this is going to be run // on loop1 really. - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); post_merge.CountDown(); }); post_merge.Wait(); - raster_thread_merger_->DecrementLease(); + raster_thread_merger->DecrementLease(); loop1->GetTaskRunner()->PostTask([&]() { - ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_FALSE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); post_unmerge.CountDown(); }); loop2->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2); post_unmerge.CountDown(); }); @@ -157,60 +168,35 @@ TEST(RasterThreadMerger, IsNotOnRasterizingThread) { } TEST(RasterThreadMerger, LeaseExtension) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; - fml::MessageLoop* loop2 = nullptr; - fml::AutoResetWaitableEvent latch2; - fml::AutoResetWaitableEvent term2; - std::thread thread2([&loop2, &latch2, &term2]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop2 = &fml::MessageLoop::GetCurrent(); - latch2.Signal(); - term2.Wait(); - }); - - latch1.Wait(); - latch2.Wait(); - - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); - fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto raster_thread_merger_ = + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); - const int kNumFramesMerged = 5; + const size_t kNumFramesMerged = 5; - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->MergeWithLease(kNumFramesMerged); + raster_thread_merger->MergeWithLease(kNumFramesMerged); // let there be one more turn till the leases expire. - for (int i = 0; i < kNumFramesMerged - 1; i++) { - ASSERT_TRUE(raster_thread_merger_->IsMerged()); - raster_thread_merger_->DecrementLease(); + for (size_t i = 0; i < kNumFramesMerged - 1; i++) { + ASSERT_TRUE(raster_thread_merger->IsMerged()); + raster_thread_merger->DecrementLease(); } // extend the lease once. - raster_thread_merger_->ExtendLeaseTo(kNumFramesMerged); + raster_thread_merger->ExtendLeaseTo(kNumFramesMerged); // we will NOT last for 1 extra turn, we just set it. - for (int i = 0; i < kNumFramesMerged; i++) { - ASSERT_TRUE(raster_thread_merger_->IsMerged()); - raster_thread_merger_->DecrementLease(); + for (size_t i = 0; i < kNumFramesMerged; i++) { + ASSERT_TRUE(raster_thread_merger->IsMerged()); + raster_thread_merger->DecrementLease(); } - ASSERT_FALSE(raster_thread_merger_->IsMerged()); - - term1.Signal(); - term2.Signal(); - thread1.join(); - thread2.join(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); } TEST(RasterThreadMerger, WaitUntilMerged) { @@ -231,7 +217,7 @@ TEST(RasterThreadMerger, WaitUntilMerged) { term_platform.Wait(); }); - const int kNumFramesMerged = 5; + const size_t kNumFramesMerged = 5; fml::MessageLoop* loop_raster = nullptr; fml::AutoResetWaitableEvent term_raster; std::thread thread_raster([&]() { @@ -253,7 +239,7 @@ TEST(RasterThreadMerger, WaitUntilMerged) { latch_merged.Wait(); ASSERT_TRUE(raster_thread_merger->IsMerged()); - for (int i = 0; i < kNumFramesMerged; i++) { + for (size_t i = 0; i < kNumFramesMerged; i++) { ASSERT_TRUE(raster_thread_merger->IsMerged()); raster_thread_merger->DecrementLease(); } @@ -267,204 +253,112 @@ TEST(RasterThreadMerger, WaitUntilMerged) { } TEST(RasterThreadMerger, HandleTaskQueuesAreTheSame) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); - - latch1.Wait(); - - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); + TaskQueueWrapper queue; + fml::TaskQueueId qid1 = queue.GetTaskQueueId(); fml::TaskQueueId qid2 = qid1; - const auto raster_thread_merger_ = + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); // Statically merged. - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + ASSERT_TRUE(raster_thread_merger->IsMerged()); // Test decrement lease and unmerge are both no-ops. // The task queues should be always merged. - const int kNumFramesMerged = 5; - raster_thread_merger_->MergeWithLease(kNumFramesMerged); + const size_t kNumFramesMerged = 5; + raster_thread_merger->MergeWithLease(kNumFramesMerged); - for (int i = 0; i < kNumFramesMerged; i++) { - ASSERT_TRUE(raster_thread_merger_->IsMerged()); - raster_thread_merger_->DecrementLease(); + for (size_t i = 0; i < kNumFramesMerged; i++) { + ASSERT_TRUE(raster_thread_merger->IsMerged()); + raster_thread_merger->DecrementLease(); } - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + ASSERT_TRUE(raster_thread_merger->IsMerged()); // Wait until merged should also return immediately. - raster_thread_merger_->WaitUntilMerged(); - ASSERT_TRUE(raster_thread_merger_->IsMerged()); - - term1.Signal(); - thread1.join(); + raster_thread_merger->WaitUntilMerged(); + ASSERT_TRUE(raster_thread_merger->IsMerged()); } TEST(RasterThreadMerger, Enable) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); - - fml::MessageLoop* loop2 = nullptr; - fml::AutoResetWaitableEvent latch2; - fml::AutoResetWaitableEvent term2; - std::thread thread2([&loop2, &latch2, &term2]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop2 = &fml::MessageLoop::GetCurrent(); - latch2.Signal(); - term2.Wait(); - }); - - latch1.Wait(); - latch2.Wait(); - - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); - fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto raster_thread_merger_ = + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); - raster_thread_merger_->Disable(); - raster_thread_merger_->MergeWithLease(1); - ASSERT_FALSE(raster_thread_merger_->IsMerged()); - - raster_thread_merger_->Enable(); - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + raster_thread_merger->Disable(); + raster_thread_merger->MergeWithLease(1); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->MergeWithLease(1); - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + raster_thread_merger->Enable(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->DecrementLease(); - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + raster_thread_merger->MergeWithLease(1); + ASSERT_TRUE(raster_thread_merger->IsMerged()); - term1.Signal(); - term2.Signal(); - thread1.join(); - thread2.join(); + raster_thread_merger->DecrementLease(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); } TEST(RasterThreadMerger, Disable) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); - - fml::MessageLoop* loop2 = nullptr; - fml::AutoResetWaitableEvent latch2; - fml::AutoResetWaitableEvent term2; - std::thread thread2([&loop2, &latch2, &term2]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop2 = &fml::MessageLoop::GetCurrent(); - latch2.Signal(); - term2.Wait(); - }); - - latch1.Wait(); - latch2.Wait(); - - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); - fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto raster_thread_merger_ = + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); - raster_thread_merger_->Disable(); - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + raster_thread_merger->Disable(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->MergeWithLease(1); - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + raster_thread_merger->MergeWithLease(1); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->Enable(); - raster_thread_merger_->MergeWithLease(1); - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + raster_thread_merger->Enable(); + raster_thread_merger->MergeWithLease(1); + ASSERT_TRUE(raster_thread_merger->IsMerged()); - raster_thread_merger_->Disable(); - raster_thread_merger_->UnMergeNow(); - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + raster_thread_merger->Disable(); + raster_thread_merger->UnMergeNowIfLastOne(); + ASSERT_TRUE(raster_thread_merger->IsMerged()); { - auto decrement_result = raster_thread_merger_->DecrementLease(); + auto decrement_result = raster_thread_merger->DecrementLease(); ASSERT_EQ(fml::RasterThreadStatus::kRemainsMerged, decrement_result); } - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + ASSERT_TRUE(raster_thread_merger->IsMerged()); - raster_thread_merger_->Enable(); - raster_thread_merger_->UnMergeNow(); - ASSERT_FALSE(raster_thread_merger_->IsMerged()); + raster_thread_merger->Enable(); + raster_thread_merger->UnMergeNowIfLastOne(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); - raster_thread_merger_->MergeWithLease(1); + raster_thread_merger->MergeWithLease(1); - ASSERT_TRUE(raster_thread_merger_->IsMerged()); + ASSERT_TRUE(raster_thread_merger->IsMerged()); { - auto decrement_result = raster_thread_merger_->DecrementLease(); + auto decrement_result = raster_thread_merger->DecrementLease(); ASSERT_EQ(fml::RasterThreadStatus::kUnmergedNow, decrement_result); } - ASSERT_FALSE(raster_thread_merger_->IsMerged()); - - term1.Signal(); - term2.Signal(); - thread1.join(); - thread2.join(); + ASSERT_FALSE(raster_thread_merger->IsMerged()); } TEST(RasterThreadMerger, IsEnabled) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); - - fml::MessageLoop* loop2 = nullptr; - fml::AutoResetWaitableEvent latch2; - fml::AutoResetWaitableEvent term2; - std::thread thread2([&loop2, &latch2, &term2]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop2 = &fml::MessageLoop::GetCurrent(); - latch2.Signal(); - term2.Wait(); - }); - - latch1.Wait(); - latch2.Wait(); - - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); - fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto raster_thread_merger_ = + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); - ASSERT_TRUE(raster_thread_merger_->IsEnabled()); - - raster_thread_merger_->Disable(); - ASSERT_FALSE(raster_thread_merger_->IsEnabled()); + ASSERT_TRUE(raster_thread_merger->IsEnabled()); - raster_thread_merger_->Enable(); - ASSERT_TRUE(raster_thread_merger_->IsEnabled()); + raster_thread_merger->Disable(); + ASSERT_FALSE(raster_thread_merger->IsEnabled()); - term1.Signal(); - term2.Signal(); - thread1.join(); - thread2.join(); + raster_thread_merger->Enable(); + ASSERT_TRUE(raster_thread_merger->IsEnabled()); } TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskMergesThreads) { @@ -489,21 +383,21 @@ TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskMergesThreads) { fml::TaskQueueId qid_raster = loop_raster->GetTaskRunner()->GetTaskQueueId(); fml::CountDownLatch post_merge(2); - const auto raster_thread_merger_ = + const auto raster_thread_merger = fml::MakeRefCounted(qid_platform, qid_raster); loop_raster->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_raster); - raster_thread_merger_->MergeWithLease(1); + raster_thread_merger->MergeWithLease(1); post_merge.CountDown(); }); loop_raster->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform); - raster_thread_merger_->DecrementLease(); + raster_thread_merger->DecrementLease(); post_merge.CountDown(); }); @@ -521,91 +415,63 @@ TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskMergesThreads) { } TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskUnMergesThreads) { - fml::MessageLoop* loop_platform = nullptr; - fml::AutoResetWaitableEvent latch1; - std::thread thread_platform([&loop_platform, &latch1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop_platform = &fml::MessageLoop::GetCurrent(); - loop_platform->GetTaskRunner()->PostTask([&]() { latch1.Signal(); }); - loop_platform->Run(); - }); - - fml::MessageLoop* loop_raster = nullptr; - fml::AutoResetWaitableEvent latch2; - std::thread thread_raster([&loop_raster, &loop_platform, &latch1, &latch2]() { - latch1.Wait(); + fml::Thread platform_thread("test_platform_thread"); + fml::AutoResetWaitableEvent raster_latch; + std::thread thread_raster([&]() { fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop_raster = &fml::MessageLoop::GetCurrent(); + fml::MessageLoop* loop_raster = &fml::MessageLoop::GetCurrent(); + fml::TaskQueueId qid_platform = - loop_platform->GetTaskRunner()->GetTaskQueueId(); + platform_thread.GetTaskRunner()->GetTaskQueueId(); fml::TaskQueueId qid_raster = loop_raster->GetTaskRunner()->GetTaskQueueId(); - fml::CountDownLatch post_merge(2); - const auto raster_thread_merger_ = + fml::AutoResetWaitableEvent merge_latch; + const auto raster_thread_merger = fml::MakeRefCounted(qid_platform, qid_raster); loop_raster->GetTaskRunner()->PostTask([&]() { - raster_thread_merger_->MergeWithLease(1); - post_merge.CountDown(); + raster_thread_merger->MergeWithLease(1); + merge_latch.Signal(); }); loop_raster->RunExpiredTasksNow(); + merge_latch.Wait(); + // threads should be merged at this point. + fml::AutoResetWaitableEvent unmerge_latch; loop_raster->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread()); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform); - raster_thread_merger_->DecrementLease(); - post_merge.CountDown(); + raster_thread_merger->DecrementLease(); + unmerge_latch.Signal(); }); + fml::AutoResetWaitableEvent post_unmerge_latch; loop_raster->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); - ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread()); - ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform); - post_merge.CountDown(); + ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); + ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); + ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_raster); + post_unmerge_latch.Signal(); }); + unmerge_latch.Wait(); loop_raster->RunExpiredTasksNow(); - post_merge.Wait(); - latch2.Signal(); - }); - latch2.Wait(); - loop_platform->GetTaskRunner()->PostTask( - [&]() { loop_platform->Terminate(); }); + post_unmerge_latch.Wait(); + raster_latch.Signal(); + }); - thread_platform.join(); + raster_latch.Wait(); thread_raster.join(); } TEST(RasterThreadMerger, SetMergeUnmergeCallback) { - fml::MessageLoop* loop1 = nullptr; - fml::AutoResetWaitableEvent latch1; - fml::AutoResetWaitableEvent term1; - std::thread thread1([&loop1, &latch1, &term1]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop1 = &fml::MessageLoop::GetCurrent(); - latch1.Signal(); - term1.Wait(); - }); - - fml::MessageLoop* loop2 = nullptr; - fml::AutoResetWaitableEvent latch2; - fml::AutoResetWaitableEvent term2; - std::thread thread2([&loop2, &latch2, &term2]() { - fml::MessageLoop::EnsureInitializedForCurrentThread(); - loop2 = &fml::MessageLoop::GetCurrent(); - latch2.Signal(); - term2.Wait(); - }); - - latch1.Wait(); - latch2.Wait(); - - fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); - fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); const auto raster_thread_merger = fml::MakeRefCounted(qid1, qid2); @@ -621,11 +487,153 @@ TEST(RasterThreadMerger, SetMergeUnmergeCallback) { raster_thread_merger->DecrementLease(); ASSERT_EQ(2, callbacks); +} - term1.Signal(); - term2.Signal(); - thread1.join(); - thread2.join(); +TEST(RasterThreadMerger, MultipleMergersCanMergeSameThreadPair) { + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + // Two mergers will share one same inner merger + const auto raster_thread_merger1 = + fml::RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); + const auto raster_thread_merger2 = + fml::RasterThreadMerger::CreateOrShareThreadMerger(raster_thread_merger1, + qid1, qid2); + const size_t kNumFramesMerged = 5; + ASSERT_FALSE(raster_thread_merger1->IsMerged()); + ASSERT_FALSE(raster_thread_merger2->IsMerged()); + + // Merge using the first merger + raster_thread_merger1->MergeWithLease(kNumFramesMerged); + + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + + // let there be one more turn till the leases expire. + for (size_t i = 0; i < kNumFramesMerged - 1; i++) { + // Check merge state using the two merger + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + raster_thread_merger1->DecrementLease(); + } + + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + + // extend the lease once with the first merger + raster_thread_merger1->ExtendLeaseTo(kNumFramesMerged); + + // we will NOT last for 1 extra turn, we just set it. + for (size_t i = 0; i < kNumFramesMerged; i++) { + // Check merge state using the two merger + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + raster_thread_merger1->DecrementLease(); + } + + ASSERT_FALSE(raster_thread_merger1->IsMerged()); + ASSERT_FALSE(raster_thread_merger2->IsMerged()); +} + +TEST(RasterThreadMerger, TheLastCallerOfMultipleMergersCanUnmergeNow) { + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + // Two mergers will share one same inner merger + const auto raster_thread_merger1 = + fml::RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); + const auto raster_thread_merger2 = + fml::RasterThreadMerger::CreateOrShareThreadMerger(raster_thread_merger1, + qid1, qid2); + const size_t kNumFramesMerged = 5; + ASSERT_FALSE(raster_thread_merger1->IsMerged()); + ASSERT_FALSE(raster_thread_merger2->IsMerged()); + + // Merge using the mergers + raster_thread_merger1->MergeWithLease(kNumFramesMerged); + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + // Extend the second merger's lease + raster_thread_merger2->ExtendLeaseTo(kNumFramesMerged); + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + + // Two callers state becomes one caller left. + raster_thread_merger1->UnMergeNowIfLastOne(); + // Check if still merged + ASSERT_TRUE(raster_thread_merger1->IsMerged()); + ASSERT_TRUE(raster_thread_merger2->IsMerged()); + + // One caller state becomes no callers left. + raster_thread_merger2->UnMergeNowIfLastOne(); + // Check if unmerged + ASSERT_FALSE(raster_thread_merger1->IsMerged()); + ASSERT_FALSE(raster_thread_merger2->IsMerged()); +} + +/// This case tests multiple standalone engines using independent merger to +/// merge two different raster threads into the same platform thread. +TEST(RasterThreadMerger, + TwoIndependentMergersCanMergeTwoDifferentThreadsIntoSamePlatformThread) { + TaskQueueWrapper queue1; + TaskQueueWrapper queue2; + TaskQueueWrapper queue3; + fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); + fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); + fml::TaskQueueId qid3 = queue3.GetTaskQueueId(); + + // Two mergers will NOT share same inner merger + const auto merger_from_2_to_1 = + fml::RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); + const auto merger_from_3_to_1 = + fml::RasterThreadMerger::CreateOrShareThreadMerger(merger_from_2_to_1, + qid1, qid3); + const size_t kNumFramesMerged = 5; + ASSERT_FALSE(merger_from_2_to_1->IsMerged()); + ASSERT_FALSE(merger_from_3_to_1->IsMerged()); + + // Merge thread2 into thread1 + merger_from_2_to_1->MergeWithLease(kNumFramesMerged); + // Merge thread3 into thread1 + merger_from_3_to_1->MergeWithLease(kNumFramesMerged); + + ASSERT_TRUE(merger_from_2_to_1->IsMerged()); + ASSERT_TRUE(merger_from_3_to_1->IsMerged()); + + for (size_t i = 0; i < kNumFramesMerged; i++) { + ASSERT_TRUE(merger_from_2_to_1->IsMerged()); + merger_from_2_to_1->DecrementLease(); + } + + ASSERT_FALSE(merger_from_2_to_1->IsMerged()); + ASSERT_TRUE(merger_from_3_to_1->IsMerged()); + + for (size_t i = 0; i < kNumFramesMerged; i++) { + ASSERT_TRUE(merger_from_3_to_1->IsMerged()); + merger_from_3_to_1->DecrementLease(); + } + + ASSERT_FALSE(merger_from_2_to_1->IsMerged()); + ASSERT_FALSE(merger_from_3_to_1->IsMerged()); + + merger_from_2_to_1->MergeWithLease(kNumFramesMerged); + ASSERT_TRUE(merger_from_2_to_1->IsMerged()); + ASSERT_FALSE(merger_from_3_to_1->IsMerged()); + merger_from_3_to_1->MergeWithLease(kNumFramesMerged); + ASSERT_TRUE(merger_from_2_to_1->IsMerged()); + ASSERT_TRUE(merger_from_3_to_1->IsMerged()); + + // Can unmerge independently + merger_from_2_to_1->UnMergeNowIfLastOne(); + ASSERT_FALSE(merger_from_2_to_1->IsMerged()); + ASSERT_TRUE(merger_from_3_to_1->IsMerged()); + + // Can unmerge independently + merger_from_3_to_1->UnMergeNowIfLastOne(); + ASSERT_FALSE(merger_from_2_to_1->IsMerged()); + ASSERT_FALSE(merger_from_3_to_1->IsMerged()); } } // namespace testing diff --git a/fml/shared_thread_merger.cc b/fml/shared_thread_merger.cc new file mode 100644 index 0000000000000..3ee0eb69eeca1 --- /dev/null +++ b/fml/shared_thread_merger.cc @@ -0,0 +1,93 @@ +// 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. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/fml/shared_thread_merger.h" + +#include + +namespace fml { + +SharedThreadMerger::SharedThreadMerger(fml::TaskQueueId owner, + fml::TaskQueueId subsumed) + : owner_(owner), + subsumed_(subsumed), + task_queues_(fml::MessageLoopTaskQueues::GetInstance()) {} + +bool SharedThreadMerger::MergeWithLease(RasterThreadMergerId caller, + size_t lease_term) { + FML_DCHECK(lease_term > 0) << "lease_term should be positive."; + std::scoped_lock lock(mutex_); + if (IsMergedUnSafe()) { + return true; + } + bool success = task_queues_->Merge(owner_, subsumed_); + FML_CHECK(success) << "Unable to merge the raster and platform threads."; + // Save the lease term + lease_term_by_caller_[caller] = lease_term; + return success; +} + +bool SharedThreadMerger::UnMergeNowUnSafe() { + FML_CHECK(IsAllLeaseTermsZeroUnSafe()) + << "all lease term records must be zero before calling " + "UnMergeNowUnSafe()"; + bool success = task_queues_->Unmerge(owner_, subsumed_); + FML_CHECK(success) << "Unable to un-merge the raster and platform threads."; + return success; +} + +bool SharedThreadMerger::UnMergeNowIfLastOne(RasterThreadMergerId caller) { + std::scoped_lock lock(mutex_); + lease_term_by_caller_.erase(caller); + if (!lease_term_by_caller_.empty()) { + return true; + } + return UnMergeNowUnSafe(); +} + +bool SharedThreadMerger::DecrementLease(RasterThreadMergerId caller) { + std::scoped_lock lock(mutex_); + auto entry = lease_term_by_caller_.find(caller); + bool exist = entry != lease_term_by_caller_.end(); + if (exist) { + std::atomic_size_t& lease_term_ref = entry->second; + FML_CHECK(lease_term_ref > 0) + << "lease_term should always be positive when merged, lease_term=" + << lease_term_ref; + lease_term_ref--; + } else { + FML_LOG(WARNING) << "The caller does not exist when calling " + "DecrementLease(), ignored. This may happens after " + "caller is erased in UnMergeNowIfLastOne(). caller=" + << caller; + } + if (IsAllLeaseTermsZeroUnSafe()) { + // Unmerge now because lease_term_ decreased to zero. + UnMergeNowUnSafe(); + return true; + } + return false; +} + +void SharedThreadMerger::ExtendLeaseTo(RasterThreadMergerId caller, + size_t lease_term) { + FML_DCHECK(lease_term > 0) << "lease_term should be positive."; + std::scoped_lock lock(mutex_); + FML_DCHECK(IsMergedUnSafe()) + << "should be merged state when calling this method"; + lease_term_by_caller_[caller] = lease_term; +} + +bool SharedThreadMerger::IsMergedUnSafe() const { + return !IsAllLeaseTermsZeroUnSafe(); +} + +bool SharedThreadMerger::IsAllLeaseTermsZeroUnSafe() const { + return std::all_of(lease_term_by_caller_.begin(), lease_term_by_caller_.end(), + [&](const auto& item) { return item.second == 0; }); +} + +} // namespace fml diff --git a/fml/shared_thread_merger.h b/fml/shared_thread_merger.h new file mode 100644 index 0000000000000..30b3d88e1be96 --- /dev/null +++ b/fml/shared_thread_merger.h @@ -0,0 +1,69 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_SHARED_THREAD_MERGER_H_ +#define FLUTTER_FML_SHARED_THREAD_MERGER_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/ref_counted.h" +#include "flutter/fml/message_loop_task_queues.h" + +namespace fml { + +class RasterThreadMerger; + +typedef void* RasterThreadMergerId; + +/// Instance of this class is shared between multiple |RasterThreadMerger| +/// instances, Most of the callings from |RasterThreadMerger| will be redirected +/// to this class with one more caller parameter. +class SharedThreadMerger + : public fml::RefCountedThreadSafe { + public: + SharedThreadMerger(TaskQueueId owner, TaskQueueId subsumed); + + // It's called by |RasterThreadMerger::MergeWithLease|. + // See the doc of |RasterThreadMerger::MergeWithLease|. + bool MergeWithLease(RasterThreadMergerId caller, size_t lease_term); + + // It's called by |RasterThreadMerger::UnMergeNowIfLastOne|. + // See the doc of |RasterThreadMerger::UnMergeNowIfLastOne|. + bool UnMergeNowIfLastOne(RasterThreadMergerId caller); + + // It's called by |RasterThreadMerger::ExtendLeaseTo|. + // See the doc of |RasterThreadMerger::ExtendLeaseTo|. + void ExtendLeaseTo(RasterThreadMergerId caller, size_t lease_term); + + // It's called by |RasterThreadMerger::IsMergedUnSafe|. + // See the doc of |RasterThreadMerger::IsMergedUnSafe|. + bool IsMergedUnSafe() const; + + // It's called by |RasterThreadMerger::DecrementLease|. + // See the doc of |RasterThreadMerger::DecrementLease|. + bool DecrementLease(RasterThreadMergerId caller); + + private: + fml::TaskQueueId owner_; + fml::TaskQueueId subsumed_; + fml::RefPtr task_queues_; + std::mutex mutex_; + + /// The |MergeWithLease| or |ExtendLeaseTo| method will record the caller + /// into this lease_term_by_caller_ map, |UnMergeNowIfLastOne| + /// method will remove the caller from this lease_term_by_caller_. + std::map lease_term_by_caller_; + + bool IsAllLeaseTermsZeroUnSafe() const; + + bool UnMergeNowUnSafe(); + + FML_DISALLOW_COPY_AND_ASSIGN(SharedThreadMerger); +}; + +} // namespace fml + +#endif // FLUTTER_FML_SHARED_THREAD_MERGER_H_ diff --git a/fml/synchronization/sync_switch.cc b/fml/synchronization/sync_switch.cc index 7ab90db9eb58d..5721703141670 100644 --- a/fml/synchronization/sync_switch.cc +++ b/fml/synchronization/sync_switch.cc @@ -18,11 +18,9 @@ SyncSwitch::Handlers& SyncSwitch::Handlers::SetIfFalse( return *this; } -SyncSwitch::SyncSwitch() : SyncSwitch(false) {} - SyncSwitch::SyncSwitch(bool value) : value_(value) {} -void SyncSwitch::Execute(const SyncSwitch::Handlers& handlers) { +void SyncSwitch::Execute(const SyncSwitch::Handlers& handlers) const { std::scoped_lock guard(mutex_); if (value_) { handlers.true_handler(); diff --git a/fml/synchronization/sync_switch.h b/fml/synchronization/sync_switch.h index 4fd7bfdd49c18..08451c7476f89 100644 --- a/fml/synchronization/sync_switch.h +++ b/fml/synchronization/sync_switch.h @@ -32,13 +32,10 @@ class SyncSwitch { std::function false_handler = [] {}; }; - /// Create a |SyncSwitch| with the false value. - SyncSwitch(); - /// Create a |SyncSwitch| with the specified value. /// /// @param[in] value Default value for the |SyncSwitch|. - SyncSwitch(bool value); + explicit SyncSwitch(bool value = false); /// Diverge execution between true and false values of the SyncSwitch. /// @@ -46,7 +43,7 @@ class SyncSwitch { /// |SetSwitch| inside of the handlers will result in a self deadlock. /// /// @param[in] handlers Called for the correct value of the |SyncSwitch|. - void Execute(const Handlers& handlers); + void Execute(const Handlers& handlers) const; /// Set the value of the SyncSwitch. /// @@ -56,7 +53,7 @@ class SyncSwitch { void SetSwitch(bool value); private: - std::mutex mutex_; + mutable std::mutex mutex_; bool value_; FML_DISALLOW_COPY_AND_ASSIGN(SyncSwitch); diff --git a/fml/task_queue_id.h b/fml/task_queue_id.h new file mode 100644 index 0000000000000..2395f9db92be6 --- /dev/null +++ b/fml/task_queue_id.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_TASK_QUEUE_ID_H_ +#define FLUTTER_FML_TASK_QUEUE_ID_H_ + +#include "flutter/fml/logging.h" + +namespace fml { + +/** + * `MessageLoopTaskQueues` task dispatcher's internal task queue identifier. + */ +class TaskQueueId { + public: + /// This constant indicates whether a task queue has been subsumed by a task + /// runner. + static const size_t kUnmerged; + + /// Intializes a task queue with the given value as it's ID. + explicit TaskQueueId(size_t value) : value_(value) {} + + operator size_t() const { return value_; } + + private: + size_t value_ = kUnmerged; +}; + +} // namespace fml + +#endif // FLUTTER_FML_TASK_QUEUE_ID_H_ diff --git a/fml/task_runner.h b/fml/task_runner.h index a66f67e13cc12..d65c3e919b8a6 100644 --- a/fml/task_runner.h +++ b/fml/task_runner.h @@ -16,21 +16,49 @@ namespace fml { class MessageLoopImpl; -class TaskRunner : public fml::RefCountedThreadSafe { +/// An interface over the ability to schedule tasks on a \p TaskRunner. +class BasicTaskRunner { + public: + /// Schedules \p task to be executed on the TaskRunner's associated event + /// loop. + virtual void PostTask(const fml::closure& task) = 0; +}; + +/// The object for scheduling tasks on a \p fml::MessageLoop. +/// +/// Typically there is one \p TaskRunner associated with each thread. When one +/// wants to execute an operation on that thread they post a task to the +/// TaskRunner. +/// +/// \see fml::MessageLoop +class TaskRunner : public fml::RefCountedThreadSafe, + public BasicTaskRunner { public: virtual ~TaskRunner(); - virtual void PostTask(const fml::closure& task); + virtual void PostTask(const fml::closure& task) override; virtual void PostTaskForTime(const fml::closure& task, fml::TimePoint target_time); + /// Schedules a task to be run on the MessageLoop after the time \p delay has + /// passed. + /// \note There is latency between when the task is schedule and actually + /// executed so that the actual execution time is: now + delay + + /// message_loop_latency, where message_loop_latency is undefined and could be + /// tens of milliseconds. virtual void PostDelayedTask(const fml::closure& task, fml::TimeDelta delay); + /// Returns \p true when the current executing thread's TaskRunner matches + /// this instance. virtual bool RunsTasksOnCurrentThread(); + /// Returns the unique identifier associated with the TaskRunner. + /// \see fml::MessageLoopTaskQueues virtual TaskQueueId GetTaskQueueId(); + /// Executes the \p task directly if the TaskRunner \p runner is the + /// TaskRunner associated with the current executing thread. static void RunNowOrPostTask(fml::RefPtr runner, const fml::closure& task); diff --git a/fml/task_source.cc b/fml/task_source.cc new file mode 100644 index 0000000000000..e62e1952e65a5 --- /dev/null +++ b/fml/task_source.cc @@ -0,0 +1,103 @@ +// 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. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/fml/task_source.h" + +namespace fml { + +TaskSource::TaskSource(TaskQueueId task_queue_id) + : task_queue_id_(task_queue_id) {} + +TaskSource::~TaskSource() { + ShutDown(); +} + +void TaskSource::ShutDown() { + primary_task_queue_ = {}; + secondary_task_queue_ = {}; +} + +void TaskSource::RegisterTask(const DelayedTask& task) { + switch (task.GetTaskSourceGrade()) { + case TaskSourceGrade::kUserInteraction: + primary_task_queue_.push(task); + break; + case TaskSourceGrade::kUnspecified: + primary_task_queue_.push(task); + break; + case TaskSourceGrade::kDartMicroTasks: + secondary_task_queue_.push(task); + break; + } +} + +void TaskSource::PopTask(TaskSourceGrade grade) { + switch (grade) { + case TaskSourceGrade::kUserInteraction: + primary_task_queue_.pop(); + break; + case TaskSourceGrade::kUnspecified: + primary_task_queue_.pop(); + break; + case TaskSourceGrade::kDartMicroTasks: + secondary_task_queue_.pop(); + break; + } +} + +size_t TaskSource::GetNumPendingTasks() const { + size_t size = primary_task_queue_.size(); + if (secondary_pause_requests_ == 0) { + size += secondary_task_queue_.size(); + } + return size; +} + +bool TaskSource::IsEmpty() const { + return GetNumPendingTasks() == 0; +} + +TaskSource::TopTask TaskSource::Top() const { + FML_CHECK(!IsEmpty()); + if (secondary_pause_requests_ > 0 || secondary_task_queue_.empty()) { + const auto& primary_top = primary_task_queue_.top(); + return { + .task_queue_id = task_queue_id_, + .task = primary_top, + }; + } else if (primary_task_queue_.empty()) { + const auto& secondary_top = secondary_task_queue_.top(); + return { + .task_queue_id = task_queue_id_, + .task = secondary_top, + }; + } else { + const auto& primary_top = primary_task_queue_.top(); + const auto& secondary_top = secondary_task_queue_.top(); + if (primary_top > secondary_top) { + return { + .task_queue_id = task_queue_id_, + .task = secondary_top, + }; + } else { + return { + .task_queue_id = task_queue_id_, + .task = primary_top, + }; + } + } +} + +void TaskSource::PauseSecondary() { + secondary_pause_requests_++; +} + +void TaskSource::ResumeSecondary() { + secondary_pause_requests_--; + FML_DCHECK(secondary_pause_requests_ >= 0); +} + +} // namespace fml diff --git a/fml/task_source.h b/fml/task_source.h new file mode 100644 index 0000000000000..64e151d9482ec --- /dev/null +++ b/fml/task_source.h @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_TASK_SOURCE_H_ +#define FLUTTER_FML_TASK_SOURCE_H_ + +#include "flutter/fml/delayed_task.h" +#include "flutter/fml/task_queue_id.h" +#include "flutter/fml/task_source_grade.h" + +namespace fml { + +class MessageLoopTaskQueues; + +/** + * A Source of tasks for the `MessageLoopTaskQueues` task dispatcher. This is a + * wrapper around a primary and secondary task heap with the difference between + * them being that the secondary task heap can be paused and resumed by the task + * dispatcher. `TaskSourceGrade` determines what task heap the task is assigned + * to. + * + * Registering Tasks + * ----------------- + * The task dispatcher associates a task source with each `TaskQueueID`. When + * the user of the task dispatcher registers a task, the task is in-turn + * registered with the `TaskSource` corresponding to the `TaskQueueID`. + * + * Processing Tasks + * ---------------- + * Task dispatcher provides the event loop a way to acquire tasks to run via + * `GetNextTaskToRun`. Task dispatcher asks the underlying `TaskSource` for the + * next task. + */ +class TaskSource { + public: + struct TopTask { + TaskQueueId task_queue_id; + const DelayedTask& task; + }; + + /// Construts a TaskSource with the given `task_queue_id`. + explicit TaskSource(TaskQueueId task_queue_id); + + ~TaskSource(); + + /// Drops the pending tasks from both primary and secondary task heaps. + void ShutDown(); + + /// Adds a task to the corresponding task heap as dictated by the + /// `TaskSourceGrade` of the `DelayedTask`. + void RegisterTask(const DelayedTask& task); + + /// Pops the task heap corresponding to the `TaskSourceGrade`. + void PopTask(TaskSourceGrade grade); + + /// Returns the number of pending tasks. Excludes the tasks from the secondary + /// heap if it's paused. + size_t GetNumPendingTasks() const; + + /// Returns true if `GetNumPendingTasks` is zero. + bool IsEmpty() const; + + /// Returns the top task based on scheduled time, taking into account whether + /// the secondary heap has been paused or not. + TopTask Top() const; + + /// Pause providing tasks from secondary task heap. + void PauseSecondary(); + + /// Resume providing tasks from secondary task heap. + void ResumeSecondary(); + + private: + const fml::TaskQueueId task_queue_id_; + fml::DelayedTaskQueue primary_task_queue_; + fml::DelayedTaskQueue secondary_task_queue_; + int secondary_pause_requests_ = 0; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TaskSource); +}; + +} // namespace fml + +#endif // FLUTTER_FML_TASK_SOURCE_H_ diff --git a/fml/task_source_grade.h b/fml/task_source_grade.h new file mode 100644 index 0000000000000..d7447f511f1e5 --- /dev/null +++ b/fml/task_source_grade.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_TASK_SOURCE_GRADE_H_ +#define FLUTTER_FML_TASK_SOURCE_GRADE_H_ + +namespace fml { + +/** + * Categories of work dispatched to `MessageLoopTaskQueues` dispatcher. By + * specifying the `TaskSourceGrade`, you indicate the task's importance to the + * dispatcher. + */ +enum class TaskSourceGrade { + /// This `TaskSourceGrade` indicates that a task is critical to user + /// interaction. + kUserInteraction, + /// This `TaskSourceGrade` indicates that a task corresponds to servicing a + /// dart micro task. These aren't critical to user interaction. + kDartMicroTasks, + /// The absence of a specialized `TaskSourceGrade`. + kUnspecified, +}; + +} // namespace fml + +#endif // FLUTTER_FML_TASK_SOURCE_GRADE_H_ diff --git a/fml/task_source_unittests.cc b/fml/task_source_unittests.cc new file mode 100644 index 0000000000000..09e37be2854e1 --- /dev/null +++ b/fml/task_source_unittests.cc @@ -0,0 +1,101 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/task_source.h" +#include "flutter/fml/time/chrono_timestamp_provider.h" +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" +#include "gtest/gtest.h" + +namespace fml { +namespace testing { + +TEST(TaskSourceTests, SimpleInitialization) { + TaskSource task_source = TaskSource(TaskQueueId(1)); + task_source.RegisterTask( + {1, [] {}, ChronoTicksSinceEpoch(), TaskSourceGrade::kUnspecified}); + ASSERT_EQ(task_source.GetNumPendingTasks(), 1u); +} + +TEST(TaskSourceTests, MultipleTaskGrades) { + TaskSource task_source = TaskSource(TaskQueueId(1)); + task_source.RegisterTask( + {1, [] {}, ChronoTicksSinceEpoch(), TaskSourceGrade::kUnspecified}); + task_source.RegisterTask( + {2, [] {}, ChronoTicksSinceEpoch(), TaskSourceGrade::kUserInteraction}); + task_source.RegisterTask( + {3, [] {}, ChronoTicksSinceEpoch(), TaskSourceGrade::kDartMicroTasks}); + ASSERT_EQ(task_source.GetNumPendingTasks(), 3u); +} + +TEST(TaskSourceTests, SimpleOrdering) { + TaskSource task_source = TaskSource(TaskQueueId(1)); + auto time_stamp = ChronoTicksSinceEpoch(); + int value = 0; + task_source.RegisterTask( + {1, [&] { value = 1; }, time_stamp, TaskSourceGrade::kUnspecified}); + task_source.RegisterTask({2, [&] { value = 7; }, + time_stamp + fml::TimeDelta::FromMilliseconds(1), + TaskSourceGrade::kUnspecified}); + task_source.Top().task.GetTask()(); + task_source.PopTask(TaskSourceGrade::kUnspecified); + ASSERT_EQ(value, 1); + task_source.Top().task.GetTask()(); + task_source.PopTask(TaskSourceGrade::kUnspecified); + ASSERT_EQ(value, 7); +} + +TEST(TaskSourceTests, SimpleOrderingMultiTaskHeaps) { + TaskSource task_source = TaskSource(TaskQueueId(1)); + auto time_stamp = ChronoTicksSinceEpoch(); + int value = 0; + task_source.RegisterTask( + {1, [&] { value = 1; }, time_stamp, TaskSourceGrade::kDartMicroTasks}); + task_source.RegisterTask({2, [&] { value = 7; }, + time_stamp + fml::TimeDelta::FromMilliseconds(1), + TaskSourceGrade::kUserInteraction}); + auto top_task = task_source.Top(); + top_task.task.GetTask()(); + task_source.PopTask(top_task.task.GetTaskSourceGrade()); + ASSERT_EQ(value, 1); + + auto second_task = task_source.Top(); + second_task.task.GetTask()(); + task_source.PopTask(second_task.task.GetTaskSourceGrade()); + ASSERT_EQ(value, 7); +} + +TEST(TaskSourceTests, OrderingMultiTaskHeapsSecondaryPaused) { + TaskSource task_source = TaskSource(TaskQueueId(1)); + auto time_stamp = ChronoTicksSinceEpoch(); + int value = 0; + task_source.RegisterTask( + {1, [&] { value = 1; }, time_stamp, TaskSourceGrade::kDartMicroTasks}); + task_source.RegisterTask({2, [&] { value = 7; }, + time_stamp + fml::TimeDelta::FromMilliseconds(1), + TaskSourceGrade::kUserInteraction}); + + task_source.PauseSecondary(); + + auto top_task = task_source.Top(); + top_task.task.GetTask()(); + task_source.PopTask(top_task.task.GetTaskSourceGrade()); + ASSERT_EQ(value, 7); + + ASSERT_TRUE(task_source.IsEmpty()); + + task_source.ResumeSecondary(); + + auto second_task = task_source.Top(); + second_task.task.GetTask()(); + task_source.PopTask(second_task.task.GetTaskSourceGrade()); + ASSERT_EQ(value, 1); +} + +} // namespace testing +} // namespace fml diff --git a/fml/thread_local.cc b/fml/thread_local.cc index f997ecc05f68b..d2ea549b8541b 100644 --- a/fml/thread_local.cc +++ b/fml/thread_local.cc @@ -25,7 +25,11 @@ void* ThreadLocalPointer::get() const { void* ThreadLocalPointer::swap(void* ptr) { void* old_ptr = get(); - FML_CHECK(pthread_setspecific(key_, ptr) == 0); + int err = pthread_setspecific(key_, ptr); + if (err) { + FML_CHECK(false) << "pthread_setspecific failed (" << err + << "): " << strerror(err); + } return old_ptr; } diff --git a/fml/time/chrono_timestamp_provider.cc b/fml/time/chrono_timestamp_provider.cc new file mode 100644 index 0000000000000..b6e8efd5e6e29 --- /dev/null +++ b/fml/time/chrono_timestamp_provider.cc @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/time/chrono_timestamp_provider.h" + +#include + +namespace fml { + +ChronoTimestampProvider::ChronoTimestampProvider() = default; + +ChronoTimestampProvider::~ChronoTimestampProvider() = default; + +fml::TimePoint ChronoTimestampProvider::Now() { + const auto chrono_time_point = std::chrono::steady_clock::now(); + const auto ticks_since_epoch = chrono_time_point.time_since_epoch().count(); + return fml::TimePoint::FromTicks(ticks_since_epoch); +} + +fml::TimePoint ChronoTicksSinceEpoch() { + return ChronoTimestampProvider::Instance().Now(); +} + +} // namespace fml diff --git a/fml/time/chrono_timestamp_provider.h b/fml/time/chrono_timestamp_provider.h new file mode 100644 index 0000000000000..bd0e6dd14c4a5 --- /dev/null +++ b/fml/time/chrono_timestamp_provider.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_TIME_CHRONO_TIMESTAMP_PROVIDER_H_ +#define FLUTTER_FML_TIME_CHRONO_TIMESTAMP_PROVIDER_H_ + +#include "flutter/fml/time/timestamp_provider.h" + +#include "flutter/fml/macros.h" +#include "flutter/fml/time/time_point.h" + +namespace fml { + +/// TimestampProvider implementation that is backed by std::chrono::steady_clock +/// meant to be used only in tests for `fml`. Other components needing the +/// current time ticks since epoch should instantiate their own time stamp +/// provider backed by Dart clock. +class ChronoTimestampProvider : TimestampProvider { + public: + static ChronoTimestampProvider& Instance() { + static ChronoTimestampProvider instance; + return instance; + } + + ~ChronoTimestampProvider() override; + + fml::TimePoint Now() override; + + private: + ChronoTimestampProvider(); + + FML_DISALLOW_COPY_AND_ASSIGN(ChronoTimestampProvider); +}; + +fml::TimePoint ChronoTicksSinceEpoch(); + +} // namespace fml + +#endif // FLUTTER_FML_TIME_CHRONO_TIMESTAMP_PROVIDER_H_ diff --git a/fml/time/dart_timestamp_provider.cc b/fml/time/dart_timestamp_provider.cc new file mode 100644 index 0000000000000..2c5de9d4d8f2a --- /dev/null +++ b/fml/time/dart_timestamp_provider.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/time/dart_timestamp_provider.h" + +#include "dart_tools_api.h" + +namespace fml { + +DartTimestampProvider::DartTimestampProvider() = default; + +DartTimestampProvider::~DartTimestampProvider() = default; + +int64_t DartTimestampProvider::ConvertToNanos(int64_t ticks, + int64_t frequency) { + int64_t nano_seconds = (ticks / frequency) * kNanosPerSecond; + int64_t leftover_ticks = ticks % frequency; + int64_t leftover_nanos = (leftover_ticks * kNanosPerSecond) / frequency; + return nano_seconds + leftover_nanos; +} + +fml::TimePoint DartTimestampProvider::Now() { + const int64_t ticks = Dart_TimelineGetTicks(); + const int64_t frequency = Dart_TimelineGetTicksFrequency(); + // optimization for the most common case. + if (frequency != kNanosPerSecond) { + return fml::TimePoint::FromTicks(ConvertToNanos(ticks, frequency)); + } else { + return fml::TimePoint::FromTicks(ticks); + } +} + +fml::TimePoint DartTimelineTicksSinceEpoch() { + return DartTimestampProvider::Instance().Now(); +} + +} // namespace fml diff --git a/fml/time/dart_timestamp_provider.h b/fml/time/dart_timestamp_provider.h new file mode 100644 index 0000000000000..97ab6b1a8ef47 --- /dev/null +++ b/fml/time/dart_timestamp_provider.h @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_TIME_DART_TIMESTAMP_PROVIDER_H_ +#define FLUTTER_FML_TIME_DART_TIMESTAMP_PROVIDER_H_ + +#include "flutter/fml/time/timestamp_provider.h" + +#include "flutter/fml/macros.h" +#include "flutter/fml/time/time_point.h" + +namespace fml { + +fml::TimePoint DartTimelineTicksSinceEpoch(); + +/// TimestampProvider implementation that is backed by Dart_TimelineGetTicks +class DartTimestampProvider : TimestampProvider { + public: + static DartTimestampProvider& Instance() { + static DartTimestampProvider instance; + return instance; + } + + ~DartTimestampProvider() override; + + fml::TimePoint Now() override; + + private: + static constexpr int64_t kNanosPerSecond = 1000000000; + + int64_t ConvertToNanos(int64_t ticks, int64_t frequency); + + DartTimestampProvider(); + + FML_DISALLOW_COPY_AND_ASSIGN(DartTimestampProvider); +}; + +} // namespace fml + +#endif // FLUTTER_FML_TIME_DART_TIMESTAMP_PROVIDER_H_ diff --git a/fml/time/time_point.cc b/fml/time/time_point.cc index ad1282879f91a..e3bd652ce013a 100644 --- a/fml/time/time_point.cc +++ b/fml/time/time_point.cc @@ -5,6 +5,8 @@ #include "flutter/fml/time/time_point.h" #include "flutter/fml/build_config.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/time/dart_timestamp_provider.h" #if defined(OS_FUCHSIA) #include @@ -21,14 +23,26 @@ TimePoint TimePoint::Now() { return TimePoint(zx_clock_get_monotonic()); } +TimePoint TimePoint::CurrentWallTime() { + return Now(); +} + #else +template +static int64_t NanosSinceEpoch( + std::chrono::time_point time_point) { + const auto elapsed = time_point.time_since_epoch(); + return std::chrono::duration_cast(elapsed).count(); +} + TimePoint TimePoint::Now() { - // The base time is arbitrary; use the clock epoch for convenience. - const auto elapsed_time = std::chrono::steady_clock::now().time_since_epoch(); - return TimePoint( - std::chrono::duration_cast(elapsed_time) - .count()); + return DartTimelineTicksSinceEpoch(); +} + +TimePoint TimePoint::CurrentWallTime() { + const int64_t nanos = NanosSinceEpoch(std::chrono::system_clock::now()); + return TimePoint(nanos); } #endif diff --git a/fml/time/time_point.h b/fml/time/time_point.h index a8c65121e9e4f..1960234f7c99d 100644 --- a/fml/time/time_point.h +++ b/fml/time/time_point.h @@ -25,6 +25,8 @@ class TimePoint { static TimePoint Now(); + static TimePoint CurrentWallTime(); + static constexpr TimePoint Min() { return TimePoint(std::numeric_limits::min()); } @@ -37,6 +39,11 @@ class TimePoint { return TimePoint(ticks.ToNanoseconds()); } + // Expects ticks in nanos. + static constexpr TimePoint FromTicks(int64_t ticks) { + return TimePoint(ticks); + } + TimeDelta ToEpochDelta() const { return TimeDelta::FromNanoseconds(ticks_); } // Compute the difference between two time points. diff --git a/fml/time/time_point_unittest.cc b/fml/time/time_point_unittest.cc index 965cf81d487f7..32b35ed287f19 100644 --- a/fml/time/time_point_unittest.cc +++ b/fml/time/time_point_unittest.cc @@ -2,7 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/fml/time/time_point.h" +#include "flutter/fml/time/chrono_timestamp_provider.h" + +#include "flutter/fml/time/dart_timestamp_provider.h" + +#include #include "gtest/gtest.h" @@ -10,8 +14,21 @@ namespace fml { namespace { TEST(TimePoint, Control) { - EXPECT_LT(TimePoint::Min(), TimePoint::Now()); - EXPECT_GT(TimePoint::Max(), TimePoint::Now()); + EXPECT_LT(TimePoint::Min(), ChronoTicksSinceEpoch()); + EXPECT_GT(TimePoint::Max(), ChronoTicksSinceEpoch()); +} + +TEST(TimePoint, DartClockIsMonotonic) { + using namespace std::chrono_literals; + const auto t1 = DartTimelineTicksSinceEpoch(); + std::this_thread::sleep_for(1us); + const auto t2 = DartTimelineTicksSinceEpoch(); + std::this_thread::sleep_for(1us); + const auto t3 = DartTimelineTicksSinceEpoch(); + EXPECT_LT(TimePoint::Min(), t1); + EXPECT_LE(t1, t2); + EXPECT_LE(t2, t3); + EXPECT_LT(t3, TimePoint::Max()); } } // namespace diff --git a/fml/time/time_unittest.cc b/fml/time/time_unittest.cc index 535d79ec5a824..929b42aaf834c 100644 --- a/fml/time/time_unittest.cc +++ b/fml/time/time_unittest.cc @@ -4,17 +4,17 @@ #include +#include "flutter/fml/time/chrono_timestamp_provider.h" #include "flutter/fml/time/time_delta.h" -#include "flutter/fml/time/time_point.h" #include "gtest/gtest.h" namespace fml { namespace { TEST(Time, Now) { - auto start = TimePoint::Now(); + auto start = ChronoTicksSinceEpoch(); for (int i = 0; i < 3; ++i) { - auto now = TimePoint::Now(); + auto now = ChronoTicksSinceEpoch(); EXPECT_GE(now, start); std::this_thread::yield(); } diff --git a/fml/time/timestamp_provider.h b/fml/time/timestamp_provider.h new file mode 100644 index 0000000000000..8b10036f0b1b8 --- /dev/null +++ b/fml/time/timestamp_provider.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_TIME_TIMESTAMP_PROVIDER_H_ +#define FLUTTER_FML_TIME_TIMESTAMP_PROVIDER_H_ + +#include + +#include "flutter/fml/time/time_point.h" + +namespace fml { + +/// Pluggable provider of monotonic timestamps. Invocations of `Now` must return +/// unique values. Any two consecutive invocations must be ordered. +class TimestampProvider { + public: + virtual ~TimestampProvider(){}; + + // Returns the number of ticks elapsed by a monotonic clock since epoch. + virtual fml::TimePoint Now() = 0; +}; + +} // namespace fml + +#endif // FLUTTER_FML_TIME_TIMESTAMP_PROVIDER_H_ diff --git a/fml/trace_event.cc b/fml/trace_event.cc index bb2becbd03651..8ee1bed590504 100644 --- a/fml/trace_event.cc +++ b/fml/trace_event.cc @@ -19,6 +19,7 @@ namespace tracing { namespace { AsciiTrie gAllowlist; +TimelineEventHandler gTimelineEventHandler; inline void FlutterTimelineEvent(const char* label, int64_t timestamp0, @@ -27,9 +28,9 @@ inline void FlutterTimelineEvent(const char* label, intptr_t argument_count, const char** argument_names, const char** argument_values) { - if (gAllowlist.Query(label)) { - Dart_TimelineEvent(label, timestamp0, timestamp1_or_async_id, type, - argument_count, argument_names, argument_values); + if (gTimelineEventHandler && gAllowlist.Query(label)) { + gTimelineEventHandler(label, timestamp0, timestamp1_or_async_id, type, + argument_count, argument_names, argument_values); } } } // namespace @@ -38,6 +39,10 @@ void TraceSetAllowlist(const std::vector& allowlist) { gAllowlist.Fill(allowlist); } +void TraceSetTimelineEventHandler(TimelineEventHandler handler) { + gTimelineEventHandler = handler; +} + size_t TraceNonce() { static std::atomic_size_t gLastItem; return ++gLastItem; @@ -288,6 +293,8 @@ void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { void TraceSetAllowlist(const std::vector& allowlist) {} +void TraceSetTimelineEventHandler(TimelineEventHandler handler) {} + size_t TraceNonce() { return 0; } diff --git a/fml/trace_event.h b/fml/trace_event.h index ae80298cfd699..a030cce117e89 100644 --- a/fml/trace_event.h +++ b/fml/trace_event.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_FML_TRACE_EVENT_H_ #define FLUTTER_FML_TRACE_EVENT_H_ +#include + #include "flutter/fml/build_config.h" #if defined(OS_FUCHSIA) @@ -68,6 +70,19 @@ ::fml::tracing::TraceCounter((category_group), (name), (counter_id), (arg1), \ __VA_ARGS__); +// Avoid using the same `name` and `argX_name` for nested traces, which can +// lead to double free errors. E.g. the following code should be avoided: +// +// ```cpp +// { +// TRACE_EVENT1("flutter", "Foo::Bar", "count", "initial_count_value"); +// ... +// TRACE_EVENT_INSTANT1("flutter", "Foo::Bar", +// "count", "updated_count_value"); +// } +// ``` +// +// Instead, either use different `name` or `arg1` parameter names. #define FML_TRACE_EVENT(category_group, name, ...) \ ::fml::tracing::TraceEvent((category_group), (name), __VA_ARGS__); \ __FML__AUTO_TRACE_END(name) @@ -132,6 +147,16 @@ using TraceIDArg = int64_t; void TraceSetAllowlist(const std::vector& allowlist); +using TimelineEventHandler = std::function; + +void TraceSetTimelineEventHandler(TimelineEventHandler handler); + void TraceTimelineEvent(TraceArg category_group, TraceArg name, int64_t timestamp_micros, diff --git a/fml/unique_fd.cc b/fml/unique_fd.cc index 2da6d938f9875..5721b0f82958d 100644 --- a/fml/unique_fd.cc +++ b/fml/unique_fd.cc @@ -13,7 +13,10 @@ namespace internal { namespace os_win { -void UniqueFDTraits::Free(HANDLE fd) { +std::mutex UniqueFDTraits::file_map_mutex; +std::map UniqueFDTraits::file_map; + +void UniqueFDTraits::Free_Handle(HANDLE fd) { CloseHandle(fd); } diff --git a/fml/unique_fd.h b/fml/unique_fd.h index 5b74073f426a4..c4b79dc3496e4 100644 --- a/fml/unique_fd.h +++ b/fml/unique_fd.h @@ -10,6 +10,9 @@ #if OS_WIN #include +#include +#include +#include #else // OS_WIN #include #include @@ -22,10 +25,46 @@ namespace internal { namespace os_win { +struct DirCacheEntry { + std::wstring filename; + FILE_ID_128 id; +}; + +// The order of these is important. Must come before UniqueFDTraits struct +// else linker error. Embedding in struct also causes linker error. + struct UniqueFDTraits { + static std::mutex file_map_mutex; + static std::map file_map; + static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; } static bool IsValid(HANDLE value) { return value != InvalidValue(); } - static void Free(HANDLE fd); + static void Free_Handle(HANDLE fd); + + static void Free(HANDLE fd) { + RemoveCacheEntry(fd); + + UniqueFDTraits::Free_Handle(fd); + } + + static void RemoveCacheEntry(HANDLE fd) { + const std::lock_guard lock(file_map_mutex); + + file_map.erase(fd); + } + + static void StoreCacheEntry(HANDLE fd, DirCacheEntry state) { + const std::lock_guard lock(file_map_mutex); + file_map[fd] = state; + } + + static std::optional GetCacheEntry(HANDLE fd) { + const std::lock_guard lock(file_map_mutex); + auto found = file_map.find(fd); + return found == file_map.end() + ? std::nullopt + : std::optional{found->second}; + } }; } // namespace os_win diff --git a/fml/wakeable.h b/fml/wakeable.h index 2b36139817644..36408c0528d1d 100644 --- a/fml/wakeable.h +++ b/fml/wakeable.h @@ -9,6 +9,8 @@ namespace fml { +/// Interface over the ability to \p WakeUp a \p fml::MessageLoopImpl. +/// \see fml::MessageLoopTaskQueues class Wakeable { public: virtual ~Wakeable() {} diff --git a/lib/io/dart_io.cc b/lib/io/dart_io.cc index 21b5952b15233..017054a5388fb 100644 --- a/lib/io/dart_io.cc +++ b/lib/io/dart_io.cc @@ -23,7 +23,7 @@ void DartIO::InitForIsolate(bool may_insecurely_connect_to_all_domains, FML_CHECK(!LogIfError(result)); Dart_Handle embedder_config_type = - Dart_GetType(io_lib, ToDart("_EmbedderConfig"), 0, nullptr); + Dart_GetNonNullableType(io_lib, ToDart("_EmbedderConfig"), 0, nullptr); FML_CHECK(!LogIfError(embedder_config_type)); Dart_Handle allow_insecure_connections_result = Dart_SetField( @@ -36,6 +36,19 @@ void DartIO::InitForIsolate(bool may_insecurely_connect_to_all_domains, Dart_Handle set_domain_network_policy_result = Dart_Invoke( embedder_config_type, ToDart("_setDomainPolicies"), 1, dart_args); FML_CHECK(!LogIfError(set_domain_network_policy_result)); + + Dart_Handle ui_lib = Dart_LookupLibrary(ToDart("dart:ui")); + Dart_Handle dart_validate_args[1]; + dart_validate_args[0] = ToDart(may_insecurely_connect_to_all_domains); + Dart_Handle http_connection_hook_closure = + Dart_Invoke(ui_lib, ToDart("_getHttpConnectionHookClosure"), + /*number_of_arguments=*/1, dart_validate_args); + FML_CHECK(!LogIfError(http_connection_hook_closure)); + Dart_Handle http_lib = Dart_LookupLibrary(ToDart("dart:_http")); + FML_CHECK(!LogIfError(http_lib)); + Dart_Handle set_http_connection_hook_result = Dart_SetField( + http_lib, ToDart("_httpConnectionHook"), http_connection_hook_closure); + FML_CHECK(!LogIfError(set_http_connection_hook_result)); } } // namespace flutter diff --git a/lib/snapshot/BUILD.gn b/lib/snapshot/BUILD.gn index eb71524900fac..59d02e2631f64 100644 --- a/lib/snapshot/BUILD.gn +++ b/lib/snapshot/BUILD.gn @@ -8,15 +8,6 @@ import("//flutter/common/config.gni") import("//flutter/lib/ui/dart_ui.gni") import("//third_party/dart/utils/compile_platform.gni") -bindings_output_dir = "$root_gen_dir/sky/bindings" - -copy("generate_dart_ui") { - visibility = [ ":*" ] - sources = dart_ui_files - - outputs = [ "$bindings_output_dir/dart_ui/{{source_file_part}}" ] -} - compiled_action("generate_snapshot_bin") { if (target_cpu == "x86" && host_os == "linux") { # By default Dart will create a 32-bit gen_snapshot host binary if the target @@ -46,7 +37,6 @@ compiled_action("generate_snapshot_bin") { ] args = [ - "--enable-experiment=non-nullable", "--snapshot_kind=core", "--enable_mirrors=false", "--vm_snapshot_data=" + rebase_path(vm_snapshot_data), @@ -197,6 +187,32 @@ bin_to_linkable("platform_strong_dill_linkable") { executable = false } +if (host_os == "mac" && (target_cpu == "arm" || target_cpu == "arm64")) { + action("create_arm_gen_snapshot") { + output_dir = "$root_out_dir/clang_x64" + script = "//flutter/sky/tools/create_macos_gen_snapshots.py" + visibility = [ ":*" ] + deps = [ "//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)" ] + args = [ + "--dst", + rebase_path(output_dir), + ] + if (target_cpu == "arm") { + args += [ + "--armv7-out-dir", + rebase_path("$root_out_dir"), + ] + outputs = [ "$output_dir/gen_snapshot_armv7" ] + } else { + args += [ + "--arm64-out-dir", + rebase_path("$root_out_dir"), + ] + outputs = [ "$output_dir/gen_snapshot_arm64" ] + } + } +} + source_set("snapshot") { deps = [ ":isolate_snapshot_data_linkable", @@ -205,6 +221,10 @@ source_set("snapshot") { ":vm_snapshot_data_linkable", ":vm_snapshot_instructions_linkable", ] + if (host_os == "mac" && (target_cpu == "arm" || target_cpu == "arm64")) { + deps += [ ":create_arm_gen_snapshot" ] + } + sources = get_target_outputs(":isolate_snapshot_data_linkable") + get_target_outputs(":isolate_snapshot_instructions_linkable") + get_target_outputs(":vm_snapshot_data_linkable") + @@ -223,15 +243,14 @@ compile_platform("strong_platform") { "$root_out_dir/flutter_patched_sdk/vm_outline_strong.dill", ] + pool = "//flutter/build/dart:dart_pool" + is_runtime_mode_release = flutter_runtime_mode == "release" || flutter_runtime_mode == "jit_release" - allow_causal_async_stacks = !is_runtime_mode_release args = [ - "--enable-experiment=non-nullable", "--nnbd-agnostic", "--target=flutter", "-Ddart.vm.product=$is_runtime_mode_release", - "-Ddart.developer.causal_async_stacks=$allow_causal_async_stacks", "-Ddart.isVM=true", "dart:core", ] diff --git a/lib/snapshot/fuchsia_compilation_trace.txt b/lib/snapshot/fuchsia_compilation_trace.txt deleted file mode 100644 index 22337aa202081..0000000000000 --- a/lib/snapshot/fuchsia_compilation_trace.txt +++ /dev/null @@ -1,2043 +0,0 @@ -dart:core,_AbstractType,toString -dart:core,RangeError,checkValidRange -dart:core,RangeError,checkNotNegative -dart:core,RegExp,RegExp. -dart:core,RegExp,get:_cache -dart:core,RegExp,get:_recentlyUsed -dart:core,bool,bool.fromEnvironment -dart:core,_ImmutableMapKeyIterable,get:_map -dart:core,_ImmutableMapKeyIterable,_ImmutableMapKeyIterable. -dart:core,_ImmutableMapKeyIterable,get:iterator -dart:core,Duration,get:MICROSECONDS_PER_SECOND -dart:core,Duration,get:MICROSECONDS_PER_MINUTE -dart:core,Duration,get:MICROSECONDS_PER_HOUR -dart:core,Duration,get:MICROSECONDS_PER_DAY -dart:core,Duration,get:ZERO -dart:core,Duration,get:_duration -dart:core,Duration,Duration. -dart:core,Duration,Duration._microseconds -dart:core,Duration,- -dart:core,Duration,get:inMilliseconds -dart:core,Duration,get:inMicroseconds -dart:core,_SimpleUri,get:_uri -dart:core,_SimpleUri,get:_schemeEnd -dart:core,_SimpleUri,get:_hostStart -dart:core,_SimpleUri,get:_portStart -dart:core,_SimpleUri,get:_pathStart -dart:core,_SimpleUri,get:_queryStart -dart:core,_SimpleUri,get:_fragmentStart -dart:core,_SimpleUri,get:_schemeCache -dart:core,_SimpleUri,_SimpleUri. -dart:core,_SimpleUri,get:hasPort -dart:core,_SimpleUri,get:hasQuery -dart:core,_SimpleUri,get:hasFragment -dart:core,_SimpleUri,get:_isHttp -dart:core,_SimpleUri,get:scheme -dart:core,_SimpleUri,get:userInfo -dart:core,_SimpleUri,get:host -dart:core,_SimpleUri,get:port -dart:core,_SimpleUri,get:path -dart:core,_SimpleUri,removeFragment -dart:core,_GrowableList,removeAt -dart:core,_GrowableList,remove -dart:core,_GrowableList,insertAll -dart:core,_GrowableList,setAll -dart:core,_GrowableList,_GrowableList. -dart:core,_GrowableList,_GrowableList.withCapacity -dart:core,_GrowableList,_GrowableList.withData -dart:core,_GrowableList,get:_capacity -dart:core,_GrowableList,get:length -dart:core,_GrowableList,set:length -dart:core,_GrowableList,_setLength -dart:core,_GrowableList,_setData -dart:core,_GrowableList,[] -dart:core,_GrowableList,[]= -dart:core,_GrowableList,add -dart:core,_GrowableList,addAll -dart:core,_GrowableList,removeLast -dart:core,_GrowableList,_grow -dart:core,_GrowableList,_shrink -dart:core,_GrowableList,forEach -dart:core,_GrowableList,join -dart:core,_GrowableList,elementAt -dart:core,_GrowableList,get:isEmpty -dart:core,_GrowableList,get:isNotEmpty -dart:core,_GrowableList,clear -dart:core,_GrowableList,get:iterator -dart:core,_Closure,== -dart:core,_Closure,get:hashCode -dart:core,_Closure,_clone -dart:core,_Closure,call -dart:core,_Closure,call -dart:core,_Closure,call -dart:core,_Closure,call -dart:core,Uri,parse -dart:core,_RegExpHashValue,get:regexp -dart:core,_RegExpHashValue,get:key -dart:core,_RegExpHashValue,_RegExpHashValue. -dart:core,_RegExp,_RegExp. -dart:core,_RegExp,firstMatch -dart:core,_RegExp,_ExecuteMatch -dart:core,_IntegerImplementation,+ -dart:core,_IntegerImplementation,- -dart:core,_IntegerImplementation,* -dart:core,_IntegerImplementation,~/ -dart:core,_IntegerImplementation,/ -dart:core,_IntegerImplementation,% -dart:core,_IntegerImplementation,unary- -dart:core,_IntegerImplementation,| -dart:core,_IntegerImplementation,^ -dart:core,_IntegerImplementation,remainder -dart:core,_IntegerImplementation,_moduloFromInteger -dart:core,_IntegerImplementation,_remainderFromInteger -dart:core,_IntegerImplementation,>> -dart:core,_IntegerImplementation,<< -dart:core,_IntegerImplementation,< -dart:core,_IntegerImplementation,> -dart:core,_IntegerImplementation,>= -dart:core,_IntegerImplementation,<= -dart:core,_IntegerImplementation,abs -dart:core,_IntegerImplementation,toDouble -dart:core,Iterable,Iterable. -dart:core,Iterable,toList -dart:core,Iterable,get:isNotEmpty -dart:core,_TwoByteString,== -dart:core,_Double,_Double.fromInteger -dart:core,_Double,get:hashCode -dart:core,_Double,+ -dart:core,_Double,_add -dart:core,_Double,- -dart:core,_Double,_sub -dart:core,_Double,* -dart:core,_Double,/ -dart:core,_Double,% -dart:core,_Double,_modulo -dart:core,_Double,unary- -dart:core,_Double,== -dart:core,_Double,< -dart:core,_Double,> -dart:core,_Double,>= -dart:core,_Double,<= -dart:core,_Double,_addFromInteger -dart:core,_Double,_subFromInteger -dart:core,_Double,_mulFromInteger -dart:core,_Double,get:isNegative -dart:core,_Double,get:isInfinite -dart:core,_Double,get:isNaN -dart:core,_Double,get:isFinite -dart:core,_Double,abs -dart:core,_Double,round -dart:core,_Double,floor -dart:core,_Double,roundToDouble -dart:core,_Double,floorToDouble -dart:core,_Double,ceilToDouble -dart:core,_Double,truncateToDouble -dart:core,_Double,clamp -dart:core,_Double,toInt -dart:core,_Double,toDouble -dart:core,_Double,compareTo -dart:core,List,List.generate -dart:core,List,List.from -dart:core,List,List._internal -dart:core,List,List._fromLiteral -dart:core,Object,Object. -dart:core,Object,== -dart:core,Object,get:_hashCodeRnd -dart:core,Object,_getHash -dart:core,Object,_setHash -dart:core,Object,_objectHashCode -dart:core,Object,get:hashCode -dart:core,Object,get:_identityHashCode -dart:core,Object,get:runtimeType -dart:core,Object,_instanceOf -dart:core,Object,_simpleInstanceOfTrue -dart:core,Object,_simpleInstanceOfFalse -dart:core,_SyncIterator,get:isYieldEach -dart:core,_SyncIterator,set:isYieldEach -dart:core,_SyncIterator,get:yieldEachIterator -dart:core,_SyncIterator,get:_current -dart:core,_SyncIterator,set:_current -dart:core,_SyncIterator,get:_moveNextFn -dart:core,_SyncIterator,set:_moveNextFn -dart:core,_SyncIterator,get:current -dart:core,_SyncIterator,_SyncIterator. -dart:core,_SyncIterator,moveNext -dart:core,double,get:NAN -dart:core,double,get:INFINITY -dart:core,double,_tryParseDouble -dart:core,double,_parse -dart:core,double,parse -dart:core,_List,_List. -dart:core,_List,[] -dart:core,_List,[]= -dart:core,_List,get:length -dart:core,_List,get:iterator -dart:core,_ImmutableMap,get:_kvPairs -dart:core,_ImmutableMap,_ImmutableMap._create -dart:core,_ImmutableMap,[] -dart:core,_ImmutableMap,get:length -dart:core,_ImmutableMap,get:keys -dart:core,_OneByteString,get:hashCode -dart:core,_OneByteString,codeUnitAt -dart:core,_OneByteString,_isWhitespace -dart:core,_OneByteString,== -dart:core,_OneByteString,_substringUncheckedNative -dart:core,_OneByteString,_splitWithCharCode -dart:core,_OneByteString,split -dart:core,_OneByteString,_concatAll -dart:core,_OneByteString,indexOf -dart:core,_OneByteString,contains -dart:core,_OneByteString,get:_LC_TABLE -dart:core,_OneByteString,get:_UC_TABLE -dart:core,_OneByteString,toLowerCase -dart:core,_OneByteString,toUpperCase -dart:core,_OneByteString,_allocate -dart:core,_OneByteString,_setAt -dart:core,_GrowableArrayMarker,_GrowableArrayMarker. -dart:core,int,_tryParseSmi -dart:core,int,parse -dart:core,StringBuffer,get:_parts -dart:core,StringBuffer,set:_parts -dart:core,StringBuffer,get:_partsCodeUnits -dart:core,StringBuffer,set:_partsCodeUnits -dart:core,StringBuffer,get:_partsCompactionIndex -dart:core,StringBuffer,get:_partsCodeUnitsSinceCompaction -dart:core,StringBuffer,set:_partsCodeUnitsSinceCompaction -dart:core,StringBuffer,get:_bufferPosition -dart:core,StringBuffer,StringBuffer. -dart:core,StringBuffer,write -dart:core,StringBuffer,writeAll -dart:core,StringBuffer,toString -dart:core,StringBuffer,_consumeBuffer -dart:core,StringBuffer,_addPart -dart:core,Map,Map._fromLiteral -dart:core,Stopwatch,get:_start -dart:core,Stopwatch,set:_start -dart:core,Stopwatch,get:_stop -dart:core,Stopwatch,set:_stop -dart:core,Stopwatch,Stopwatch. -dart:core,Stopwatch,get:frequency -dart:core,Stopwatch,start -dart:core,Stopwatch,get:elapsedTicks -dart:core,Stopwatch,get:elapsedMicroseconds -dart:core,Stopwatch,_initTicker -dart:core,Stopwatch,_now -dart:core,Stopwatch,_computeFrequency -dart:core,IntegerDivisionByZeroException,IntegerDivisionByZeroException. -dart:core,DateTime,get:_value -dart:core,DateTime,get:isUtc -dart:core,DateTime,DateTime.now -dart:core,DateTime,DateTime._withValue -dart:core,DateTime,_getCurrentMicros -dart:core,DateTime,DateTime.fromMillisecondsSinceEpoch -dart:core,DateTime,DateTime._now -dart:core,DateTime,get:millisecondsSinceEpoch -dart:core,String,String.fromCharCodes -dart:core,String,String.fromCharCode -dart:core,_StringBase,createFromCharCodes -dart:core,_StringBase,_scanCodeUnits -dart:core,_StringBase,_createOneByteString -dart:core,_StringBase,_createFromCodePoints -dart:core,_StringBase,[] -dart:core,_StringBase,get:length -dart:core,_StringBase,get:isEmpty -dart:core,_StringBase,toString -dart:core,_StringBase,_substringMatches -dart:core,_StringBase,startsWith -dart:core,_StringBase,substring -dart:core,_StringBase,_substringUnchecked -dart:core,_StringBase,_isOneByteWhitespace -dart:core,_StringBase,_firstNonWhitespace -dart:core,_StringBase,_lastNonWhitespace -dart:core,_StringBase,trim -dart:core,_StringBase,_interpolateSingle -dart:core,_StringBase,_interpolate -dart:core,_StringBase,get:codeUnits -dart:core,_StringBase,_concatRange -dart:core,_StringBase,_concatRangeNative -dart:core,_Smi,get:hashCode -dart:core,_Smi,~ -dart:core,_Smi,get:bitLength -dart:core,_Smi,& -dart:core,_Smi,_bitAndFromSmi -dart:core,_Smi,get:_digitTable -dart:core,_Smi,get:_smallLookupTable -dart:core,_Smi,_positiveBase10Length -dart:core,_Smi,toString -dart:core,_FixedSizeArrayIterator,get:_array -dart:core,_FixedSizeArrayIterator,get:_length -dart:core,_FixedSizeArrayIterator,get:_index -dart:core,_FixedSizeArrayIterator,set:_index -dart:core,_FixedSizeArrayIterator,get:_current -dart:core,_FixedSizeArrayIterator,set:_current -dart:core,_FixedSizeArrayIterator,_FixedSizeArrayIterator. -dart:core,_FixedSizeArrayIterator,get:current -dart:core,_FixedSizeArrayIterator,moveNext -dart:core,_ImmutableMapKeyIterator,get:_map -dart:core,_ImmutableMapKeyIterator,get:_index -dart:core,_ImmutableMapKeyIterator,set:_index -dart:core,_ImmutableMapKeyIterator,set:_current -dart:core,_ImmutableMapKeyIterator,_ImmutableMapKeyIterator. -dart:core,_ImmutableMapKeyIterator,moveNext -dart:core,_RegExpHashKey,get:pattern -dart:core,_RegExpHashKey,_RegExpHashKey. -dart:core,_RegExpHashKey,get:hashCode -dart:core,_SyncIterable,get:_moveNextFn -dart:core,_SyncIterable,_SyncIterable. -dart:core,_SyncIterable,get:iterator -dart:core,_ImmutableList,[] -dart:core,_ImmutableList,get:length -dart:core,_ImmutableList,get:iterator -dart:core,_ImmutableList,get:last -dart:core,::,print -dart:core,::,get:_scannerTables -dart:core,::,_createTables -dart:core,::,_scan -dart:core,::,_startsWithData -dart:core,::,get:_GROWABLE_ARRAY_MARKER -dart:core,::,identityHashCode -dart:isolate,_Timer,get:_heap -dart:isolate,_Timer,get:_callback -dart:isolate,_Timer,set:_callback -dart:isolate,_Timer,get:_wakeupTime -dart:isolate,_Timer,get:_milliSeconds -dart:isolate,_Timer,get:_repeating -dart:isolate,_Timer,get:_indexOrNext -dart:isolate,_Timer,set:_indexOrNext -dart:isolate,_Timer,_nextId -dart:isolate,_Timer,_Timer._internal -dart:isolate,_Timer,_createTimer -dart:isolate,_Timer,_Timer. -dart:isolate,_Timer,get:_isInHeap -dart:isolate,_Timer,_compareTo -dart:isolate,_Timer,cancel -dart:isolate,_Timer,_enqueue -dart:isolate,_Timer,_notifyZeroHandler -dart:isolate,_Timer,_queueFromZeroEvent -dart:isolate,_Timer,_notifyEventHandler -dart:isolate,_Timer,_queueFromTimeoutEvent -dart:isolate,_Timer,_runTimers -dart:isolate,_Timer,_handleMessage -dart:isolate,_Timer,_scheduleWakeup -dart:isolate,_Timer,_createTimerHandler -dart:isolate,_Timer,_factory -dart:isolate,_TimerHeap,get:_list -dart:isolate,_TimerHeap,get:_used -dart:isolate,_TimerHeap,set:_used -dart:isolate,_TimerHeap,_TimerHeap. -dart:isolate,_TimerHeap,get:isEmpty -dart:isolate,_TimerHeap,get:first -dart:isolate,_TimerHeap,isFirst -dart:isolate,_TimerHeap,add -dart:isolate,_TimerHeap,removeFirst -dart:isolate,_TimerHeap,remove -dart:isolate,_TimerHeap,_bubbleUp -dart:isolate,_TimerHeap,_bubbleDown -dart:isolate,_TimerHeap,_swap -dart:isolate,_TimerHeap,_parent -dart:isolate,_TimerHeap,_parentIndex -dart:isolate,_TimerHeap,_leftChildIndex -dart:isolate,_TimerHeap,_rightChildIndex -dart:isolate,_SendPortImpl,send -dart:isolate,_SendPortImpl,_sendInternal -dart:isolate,_RawReceivePortImpl,_RawReceivePortImpl. -dart:isolate,_RawReceivePortImpl,close -dart:isolate,_RawReceivePortImpl,get:sendPort -dart:isolate,_RawReceivePortImpl,_get_id -dart:isolate,_RawReceivePortImpl,_get_sendport -dart:isolate,_RawReceivePortImpl,_lookupHandler -dart:isolate,_RawReceivePortImpl,_handleMessage -dart:isolate,_RawReceivePortImpl,_closeInternal -dart:isolate,_RawReceivePortImpl,set:handler -dart:isolate,_RawReceivePortImpl,_initHandlerMap -dart:isolate,_RawReceivePortImpl,get:_handlerMap -dart:isolate,RawReceivePort,RawReceivePort. -dart:isolate,::,_runPendingImmediateCallback -dart:isolate,::,_removePendingImmediateCallback -dart:isolate,::,_startMainIsolate -dart:isolate,::,_startIsolate -dart:isolate,::,_setupHooks -dart:collection,_HashFieldBase&_HashBase,_HashFieldBase&_HashBase. -dart:collection,_HashFieldBase&_HashBase,get:_checkSum -dart:collection,_HashFieldBase&_HashBase,_isModifiedSince -dart:collection,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode&SetMixin,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode&SetMixin. -dart:collection,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode&SetMixin,get:isNotEmpty -dart:collection,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode&SetMixin,toList -dart:collection,SetBase,SetBase. -dart:collection,_HashMapValueIterable,_HashMapValueIterable. -dart:collection,_HashMapValueIterable,get:iterator -dart:collection,_HashMapValueIterable,contains -dart:collection,MapView&&_UnmodifiableMapMixin,MapView&&_UnmodifiableMapMixin. -dart:collection,_InternalLinkedHashMap,_InternalLinkedHashMap. -dart:collection,_TypeTest,_TypeTest. -dart:collection,_TypeTest,get:test -dart:collection,LinkedHashSet,LinkedHashSet. -dart:collection,_HashMapIterable,get:_map -dart:collection,_HashMapIterable,_HashMapIterable. -dart:collection,_HashMapIterable,get:length -dart:collection,_HashSetEntry,get:key -dart:collection,_HashSetEntry,get:hashCode -dart:collection,_HashSetEntry,get:next -dart:collection,_HashSetEntry,set:next -dart:collection,_HashSetEntry,_HashSetEntry. -dart:collection,_HashSetEntry,remove -dart:collection,_CompactLinkedHashSet,_CompactLinkedHashSet. -dart:collection,_CompactLinkedHashSet,get:length -dart:collection,_CompactLinkedHashSet,add -dart:collection,_CompactLinkedHashSet,remove -dart:collection,_CompactLinkedHashSet,get:iterator -dart:collection,_HashSetBase,_HashSetBase. -dart:collection,_HashFieldBase,get:_index -dart:collection,_HashFieldBase,get:_hashMask -dart:collection,_HashFieldBase,get:_data -dart:collection,_HashFieldBase,get:_usedData -dart:collection,_HashFieldBase,set:_usedData -dart:collection,_HashFieldBase,get:_deletedKeys -dart:collection,_HashFieldBase,set:_deletedKeys -dart:collection,_HashFieldBase,_HashFieldBase. -dart:collection,_HashVMBase&MapMixin,_HashVMBase&MapMixin. -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_HashVMBase&MapMixin&&_LinkedHashMapMixin. -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,get:length -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,get:isEmpty -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_rehash -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_init -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_getIndexLength -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_insert -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_findValueOrInsertPoint -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,[]= -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,putIfAbsent -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,remove -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,_getValueOrData -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,containsKey -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,[] -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,forEach -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,get:keys -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin,get:values -dart:collection,_HashMapIterator,get:_map -dart:collection,_HashMapIterator,get:_stamp -dart:collection,_HashMapIterator,get:_index -dart:collection,_HashMapIterator,set:_index -dart:collection,_HashMapIterator,get:_entry -dart:collection,_HashMapIterator,set:_entry -dart:collection,_HashMapIterator,_HashMapIterator. -dart:collection,_HashMapIterator,moveNext -dart:collection,_HashVMBase,get:_index -dart:collection,_HashVMBase,set:_index -dart:collection,_HashVMBase,get:_hashMask -dart:collection,_HashVMBase,set:_hashMask -dart:collection,_HashVMBase,get:_data -dart:collection,_HashVMBase,set:_data -dart:collection,_HashVMBase,get:_usedData -dart:collection,_HashVMBase,set:_usedData -dart:collection,_HashVMBase,get:_deletedKeys -dart:collection,_HashVMBase,set:_deletedKeys -dart:collection,_HashVMBase,_HashVMBase. -dart:collection,ListQueue,set:_table -dart:collection,ListQueue,get:_head -dart:collection,ListQueue,get:_tail -dart:collection,ListQueue,ListQueue. -dart:collection,ListQueue,get:isEmpty -dart:collection,HashMap,HashMap. -dart:collection,_CompactLinkedCustomHashMap,_CompactLinkedCustomHashMap. -dart:collection,SetMixin,toList -dart:collection,SetMixin,SetMixin. -dart:collection,LinkedList,get:_modificationCount -dart:collection,LinkedList,set:_modificationCount -dart:collection,LinkedList,get:_length -dart:collection,LinkedList,set:_length -dart:collection,LinkedList,get:_first -dart:collection,LinkedList,set:_first -dart:collection,LinkedList,LinkedList. -dart:collection,LinkedList,addFirst -dart:collection,LinkedList,get:isEmpty -dart:collection,LinkedList,_insertBefore -dart:collection,_HashMapEntry,get:key -dart:collection,_HashMapEntry,get:value -dart:collection,_HashMapEntry,set:value -dart:collection,_HashMapEntry,get:hashCode -dart:collection,_HashMapEntry,get:next -dart:collection,_HashMapEntry,set:next -dart:collection,_HashMapEntry,_HashMapEntry. -dart:collection,_HashSetIterator,get:_set -dart:collection,_HashSetIterator,get:_modificationCount -dart:collection,_HashSetIterator,get:_index -dart:collection,_HashSetIterator,set:_index -dart:collection,_HashSetIterator,get:_next -dart:collection,_HashSetIterator,set:_next -dart:collection,_HashSetIterator,get:_current -dart:collection,_HashSetIterator,set:_current -dart:collection,_HashSetIterator,_HashSetIterator. -dart:collection,_HashSetIterator,moveNext -dart:collection,_HashSetIterator,get:current -dart:collection,IterableBase,IterableBase. -dart:collection,IterableBase,iterableToFullString -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase&_OperatorEqualsAndHashCode,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase&_OperatorEqualsAndHashCode. -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase&_OperatorEqualsAndHashCode,_hashCode -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase&_OperatorEqualsAndHashCode,_equals -dart:collection,MapView,get:_map -dart:collection,MapView,MapView. -dart:collection,MapView,[] -dart:collection,_HashMap,get:_elementCount -dart:collection,_HashMap,set:_elementCount -dart:collection,_HashMap,get:_buckets -dart:collection,_HashMap,set:_buckets -dart:collection,_HashMap,get:_modificationCount -dart:collection,_HashMap,set:_modificationCount -dart:collection,_HashMap,get:length -dart:collection,_HashMap,get:keys -dart:collection,_HashMap,get:values -dart:collection,_HashMap,containsKey -dart:collection,_HashMap,containsValue -dart:collection,_HashMap,[] -dart:collection,_HashMap,[]= -dart:collection,_HashMap,putIfAbsent -dart:collection,_HashMap,forEach -dart:collection,_HashMap,remove -dart:collection,_HashMap,clear -dart:collection,_HashMap,_removeEntry -dart:collection,_HashMap,_addEntry -dart:collection,_HashMap,_resize -dart:collection,_HashMap,_HashMap. -dart:collection,_CompactIterator,get:_table -dart:collection,_CompactIterator,get:_data -dart:collection,_CompactIterator,get:_len -dart:collection,_CompactIterator,get:_offset -dart:collection,_CompactIterator,set:_offset -dart:collection,_CompactIterator,get:_step -dart:collection,_CompactIterator,get:_checkSum -dart:collection,_CompactIterator,get:current -dart:collection,_CompactIterator,set:current -dart:collection,_CompactIterator,_CompactIterator. -dart:collection,_CompactIterator,moveNext -dart:collection,Object&ListMixin,Object&ListMixin. -dart:collection,Object&ListMixin,elementAt -dart:collection,Object&ListMixin,get:isEmpty -dart:collection,Object&ListMixin,contains -dart:collection,Object&ListMixin,map -dart:collection,Object&ListMixin,fold -dart:collection,Object&ListMixin,skip -dart:collection,Object&ListMixin,sort -dart:collection,Object&ListMixin,setRange -dart:collection,Object&ListMixin,get:reversed -dart:collection,Object&ListMixin,toString -dart:collection,_HashMapValueIterator,_HashMapValueIterator. -dart:collection,_HashMapValueIterator,get:current -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin,_HashFieldBase&MapMixin&&_LinkedHashMapMixin. -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin,forEach -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin,get:keys -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin,get:values -dart:collection,LinkedListEntry,get:_list -dart:collection,LinkedListEntry,set:_list -dart:collection,LinkedListEntry,set:_next -dart:collection,LinkedListEntry,set:_previous -dart:collection,LinkedListEntry,get:list -dart:collection,LinkedListEntry,LinkedListEntry. -dart:collection,UnmodifiableMapView,UnmodifiableMapView. -dart:collection,_HashBase,get:_INITIAL_INDEX_SIZE -dart:collection,_HashBase,_indexSizeToHashMask -dart:collection,_HashBase,_hashPattern -dart:collection,_HashBase,_firstProbe -dart:collection,_HashBase,_nextProbe -dart:collection,_HashBase,_isDeleted -dart:collection,_HashBase,_setDeletedAt -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase. -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase,get:_checkSum -dart:collection,_HashVMBase&MapMixin&&_LinkedHashMapMixin&_HashBase,_isModifiedSince -dart:collection,HashSet,HashSet. -dart:collection,ListBase,ListBase. -dart:collection,_HashSet,get:_buckets -dart:collection,_HashSet,set:_buckets -dart:collection,_HashSet,get:_elementCount -dart:collection,_HashSet,set:_elementCount -dart:collection,_HashSet,get:_modificationCount -dart:collection,_HashSet,set:_modificationCount -dart:collection,_HashSet,_equals -dart:collection,_HashSet,_hashCode -dart:collection,_HashSet,get:iterator -dart:collection,_HashSet,get:length -dart:collection,_HashSet,get:isEmpty -dart:collection,_HashSet,get:isNotEmpty -dart:collection,_HashSet,contains -dart:collection,_HashSet,add -dart:collection,_HashSet,_remove -dart:collection,_HashSet,remove -dart:collection,_HashSet,clear -dart:collection,_HashSet,_addEntry -dart:collection,_HashSet,_resize -dart:collection,_HashSet,_HashSet. -dart:collection,_HashMapKeyIterator,_HashMapKeyIterator. -dart:collection,_HashMapKeyIterator,get:current -dart:collection,LinkedHashMap,LinkedHashMap.from -dart:collection,LinkedHashMap,LinkedHashMap. -dart:collection,_CompactIterable,get:_table -dart:collection,_CompactIterable,get:_data -dart:collection,_CompactIterable,get:_len -dart:collection,_CompactIterable,get:_offset -dart:collection,_CompactIterable,get:_step -dart:collection,_CompactIterable,_CompactIterable. -dart:collection,_CompactIterable,get:iterator -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin&_HashBase,_HashFieldBase&MapMixin&&_LinkedHashMapMixin&_HashBase. -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin&_HashBase,get:_checkSum -dart:collection,_HashFieldBase&MapMixin&&_LinkedHashMapMixin&_HashBase,_isModifiedSince -dart:collection,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode. -dart:collection,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode,_hashCode -dart:collection,_HashFieldBase&_HashBase&_OperatorEqualsAndHashCode,_equals -dart:collection,_HashMapKeyIterable,_HashMapKeyIterable. -dart:collection,_HashMapKeyIterable,get:iterator -dart:collection,_HashFieldBase&MapMixin,_HashFieldBase&MapMixin. -dart:collection,::,get:_toStringVisiting -dart:collection,::,_isToStringVisiting -dart:developer,_SyncBlock,get:category -dart:developer,_SyncBlock,get:name -dart:developer,_SyncBlock,get:_arguments -dart:developer,_SyncBlock,get:_start -dart:developer,_SyncBlock,get:_startCpu -dart:developer,_SyncBlock,_SyncBlock._ -dart:developer,_SyncBlock,finish -dart:developer,ServiceExtensionResponse,get:extensionError -dart:developer,Timeline,startSync -dart:developer,Timeline,finishSync -dart:developer,Timeline,instantSync -dart:developer,Timeline,get:_stack -dart:developer,Timeline,get:_isolateId -dart:developer,::,registerExtension -dart:developer,::,postEvent -dart:developer,::,get:_isProduct -dart:developer,::,_argumentsAsJson -dart:developer,::,_postEvent -dart:developer,::,_lookupExtension -dart:developer,::,_registerExtension -dart:developer,::,_isDartStreamEnabled -dart:developer,::,_getTraceClock -dart:developer,::,_getThreadCpuClock -dart:developer,::,_getIsolateNum -dart:developer,::,_reportCompleteEvent -dart:developer,::,_reportInstantEvent -dart:typed_data,_Float32List,[] -dart:typed_data,_Float32List,[]= -dart:typed_data,Float32List,Float32List. -dart:typed_data,Float64List,Float64List. -dart:typed_data,Uint16List,Uint16List. -dart:typed_data,Uint16List,Uint16List.fromList -dart:typed_data,_TypedListView,_TypedListView. -dart:typed_data,_TypedListView,get:buffer -dart:typed_data,_TypedListView,get:_typedData -dart:typed_data,_TypedListView,get:offsetInBytes -dart:typed_data,_TypedListView,get:length -dart:typed_data,_Int32List,[] -dart:typed_data,_Int32List,[]= -dart:typed_data,_Uint32List,[] -dart:typed_data,_Uint32List,[]= -dart:typed_data,_Uint32List,get:elementSizeInBytes -dart:typed_data,_TypedListView&_IntListMixin,_TypedListView&_IntListMixin. -dart:typed_data,_ByteDataView,_ByteDataView. -dart:typed_data,_ByteDataView,get:buffer -dart:typed_data,_ByteDataView,get:lengthInBytes -dart:typed_data,_ByteDataView,getInt8 -dart:typed_data,_ByteDataView,getUint8 -dart:typed_data,_ByteDataView,setUint8 -dart:typed_data,_ByteDataView,getInt32 -dart:typed_data,_ByteDataView,setInt32 -dart:typed_data,_ByteDataView,getUint32 -dart:typed_data,_ByteDataView,setUint32 -dart:typed_data,_ByteDataView,getUint64 -dart:typed_data,_ByteDataView,setUint64 -dart:typed_data,_ByteDataView,setFloat32 -dart:typed_data,_ByteDataView,get:_typedData -dart:typed_data,_ByteDataView,get:_offset -dart:typed_data,_ByteDataView,get:length -dart:typed_data,_Uint8ArrayView,_Uint8ArrayView. -dart:typed_data,_Uint8ArrayView,[] -dart:typed_data,_ExternalUint32Array,[] -dart:typed_data,_ExternalUint32Array,_getIndexedUint32 -dart:typed_data,Endianness,Endianness._ -dart:typed_data,Endianness,get:BIG_ENDIAN -dart:typed_data,Endianness,get:LITTLE_ENDIAN -dart:typed_data,Endianness,get:HOST_ENDIAN -dart:typed_data,_Uint8List,[] -dart:typed_data,_Uint8List,[]= -dart:typed_data,_Uint8List,get:elementSizeInBytes -dart:typed_data,_Uint8List,_createList -dart:typed_data,Uint8List,Uint8List.view -dart:typed_data,Uint8List,Uint8List. -dart:typed_data,Uint8List,Uint8List.fromList -dart:typed_data,_TypedListBase,sublist -dart:typed_data,_TypedListBase,setRange -dart:typed_data,_TypedListBase,fillRange -dart:typed_data,_TypedListBase,_setRange -dart:typed_data,_TypedListBase,_TypedListBase. -dart:typed_data,_Float64List,[] -dart:typed_data,_Float64List,[]= -dart:typed_data,Int32List,Int32List. -dart:typed_data,_ByteBuffer,get:_data -dart:typed_data,_ByteBuffer,_ByteBuffer. -dart:typed_data,_ByteBuffer,get:lengthInBytes -dart:typed_data,_ByteBuffer,asByteData -dart:typed_data,_ByteBuffer,asUint8List -dart:typed_data,ByteData,ByteData.view -dart:typed_data,ByteData,ByteData. -dart:typed_data,ByteData,ByteData._view -dart:typed_data,_Uint16List,[]= -dart:typed_data,_Uint16List,setRange -dart:typed_data,_Uint16List,get:elementSizeInBytes -dart:typed_data,Uint32List,Uint32List. -dart:typed_data,_TypedList,get:offsetInBytes -dart:typed_data,_TypedList,get:lengthInBytes -dart:typed_data,_TypedList,get:buffer -dart:typed_data,_TypedList,get:length -dart:typed_data,_TypedList,_getInt8 -dart:typed_data,_TypedList,_getUint8 -dart:typed_data,_TypedList,_setUint8 -dart:typed_data,_TypedList,_getInt32 -dart:typed_data,_TypedList,_setInt32 -dart:typed_data,_TypedList,_getUint32 -dart:typed_data,_TypedList,_setUint32 -dart:typed_data,_TypedList,_getUint64 -dart:typed_data,_TypedList,_setUint64 -dart:typed_data,_TypedList,_setFloat32 -dart:typed_data,_ExternalUint8Array,[] -dart:typed_data,_ExternalUint8Array,get:elementSizeInBytes -dart:typed_data,::,_rangeCheck -dart:typed_data,::,_defaultIfNull -dart:async,_ControllerStream,get:_controller -dart:async,_ControllerStream,_ControllerStream. -dart:async,_ControllerStream,_createSubscription -dart:async,_BufferingStreamSubscription,get:_onData -dart:async,_BufferingStreamSubscription,set:_onData -dart:async,_BufferingStreamSubscription,get:_onError -dart:async,_BufferingStreamSubscription,set:_onError -dart:async,_BufferingStreamSubscription,get:_onDone -dart:async,_BufferingStreamSubscription,set:_onDone -dart:async,_BufferingStreamSubscription,get:_zone -dart:async,_BufferingStreamSubscription,get:_state -dart:async,_BufferingStreamSubscription,set:_state -dart:async,_BufferingStreamSubscription,get:_cancelFuture -dart:async,_BufferingStreamSubscription,set:_cancelFuture -dart:async,_BufferingStreamSubscription,get:_pending -dart:async,_BufferingStreamSubscription,set:_pending -dart:async,_BufferingStreamSubscription,_BufferingStreamSubscription. -dart:async,_BufferingStreamSubscription,_setPendingEvents -dart:async,_BufferingStreamSubscription,onData -dart:async,_BufferingStreamSubscription,onError -dart:async,_BufferingStreamSubscription,onDone -dart:async,_BufferingStreamSubscription,pause -dart:async,_BufferingStreamSubscription,resume -dart:async,_BufferingStreamSubscription,cancel -dart:async,_BufferingStreamSubscription,get:_isInputPaused -dart:async,_BufferingStreamSubscription,get:_isClosed -dart:async,_BufferingStreamSubscription,get:_isCanceled -dart:async,_BufferingStreamSubscription,get:_waitsForCancel -dart:async,_BufferingStreamSubscription,get:_inCallback -dart:async,_BufferingStreamSubscription,get:_hasPending -dart:async,_BufferingStreamSubscription,get:_isPaused -dart:async,_BufferingStreamSubscription,get:_canFire -dart:async,_BufferingStreamSubscription,get:_cancelOnError -dart:async,_BufferingStreamSubscription,_cancel -dart:async,_BufferingStreamSubscription,_decrementPauseCount -dart:async,_BufferingStreamSubscription,_add -dart:async,_BufferingStreamSubscription,_addError -dart:async,_BufferingStreamSubscription,_close -dart:async,_BufferingStreamSubscription,_onCancel -dart:async,_BufferingStreamSubscription,_addPending -dart:async,_BufferingStreamSubscription,_sendData -dart:async,_BufferingStreamSubscription,_sendError -dart:async,_BufferingStreamSubscription,_sendDone -dart:async,_BufferingStreamSubscription,_guardCallback -dart:async,_BufferingStreamSubscription,_checkState -dart:async,StreamConsumer,StreamConsumer. -dart:async,_EventSinkWrapper,get:_sink -dart:async,_EventSinkWrapper,_EventSinkWrapper. -dart:async,_EventSinkWrapper,add -dart:async,_EventSinkWrapper,close -dart:async,_Zone,_Zone. -dart:async,_DelayedData,get:value -dart:async,_DelayedData,_DelayedData. -dart:async,_DelayedData,perform -dart:async,_SinkTransformerStreamSubscription,get:_transformerSink -dart:async,_SinkTransformerStreamSubscription,set:_transformerSink -dart:async,_SinkTransformerStreamSubscription,get:_subscription -dart:async,_SinkTransformerStreamSubscription,set:_subscription -dart:async,_SinkTransformerStreamSubscription,_SinkTransformerStreamSubscription. -dart:async,_SinkTransformerStreamSubscription,get:_isSubscribed -dart:async,_SinkTransformerStreamSubscription,_add -dart:async,_SinkTransformerStreamSubscription,_close -dart:async,_SinkTransformerStreamSubscription,_onCancel -dart:async,_SinkTransformerStreamSubscription,_handleData -dart:async,_SinkTransformerStreamSubscription,_handleDone -dart:async,_SinkTransformerStreamSubscription,get:_handleData -dart:async,_SinkTransformerStreamSubscription,get:_handleError -dart:async,_SinkTransformerStreamSubscription,get:_handleDone -dart:async,_RootZone,_RootZone. -dart:async,_RootZone,runGuarded -dart:async,_RootZone,runUnaryGuarded -dart:async,_RootZone,runBinaryGuarded -dart:async,_RootZone,bindUnaryCallback -dart:async,_RootZone,run -dart:async,_RootZone,runUnary -dart:async,_RootZone,registerCallback -dart:async,_RootZone,registerUnaryCallback -dart:async,_RootZone,registerBinaryCallback -dart:async,_RootZone,errorCallback -dart:async,_RootZone,scheduleMicrotask -dart:async,_RootZone,createTimer -dart:async,_DelayedDone,_DelayedDone. -dart:async,_DelayedDone,perform -dart:async,_DelayedDone,get:next -dart:async,_StreamImplEvents,get:firstPendingEvent -dart:async,_StreamImplEvents,set:firstPendingEvent -dart:async,_StreamImplEvents,get:lastPendingEvent -dart:async,_StreamImplEvents,set:lastPendingEvent -dart:async,_StreamImplEvents,get:isEmpty -dart:async,_StreamImplEvents,add -dart:async,_StreamImplEvents,handleNext -dart:async,_StreamImplEvents,_StreamImplEvents. -dart:async,_GeneratedStreamImpl,get:_pending -dart:async,_GeneratedStreamImpl,get:_isUsed -dart:async,_GeneratedStreamImpl,set:_isUsed -dart:async,_GeneratedStreamImpl,_GeneratedStreamImpl. -dart:async,_GeneratedStreamImpl,_createSubscription -dart:async,_StreamImpl,listen -dart:async,_StreamImpl,_onListen -dart:async,_StreamImpl,_StreamImpl. -dart:async,_StreamController&&_SyncStreamControllerDispatch,_StreamController&&_SyncStreamControllerDispatch. -dart:async,_StreamController&&_SyncStreamControllerDispatch,_sendData -dart:async,_StreamController&&_SyncStreamControllerDispatch,_sendError -dart:async,_StreamController&&_SyncStreamControllerDispatch,_sendDone -dart:async,_ForwardingStreamSubscription,get:_stream -dart:async,_ForwardingStreamSubscription,get:_subscription -dart:async,_ForwardingStreamSubscription,set:_subscription -dart:async,_ForwardingStreamSubscription,_ForwardingStreamSubscription. -dart:async,_ForwardingStreamSubscription,_add -dart:async,_ForwardingStreamSubscription,_onCancel -dart:async,_ForwardingStreamSubscription,_handleData -dart:async,_ForwardingStreamSubscription,_handleDone -dart:async,_ForwardingStreamSubscription,get:_handleData -dart:async,_ForwardingStreamSubscription,get:_handleError -dart:async,_ForwardingStreamSubscription,get:_handleDone -dart:async,_ControllerSubscription,get:_controller -dart:async,_ControllerSubscription,_ControllerSubscription. -dart:async,_ControllerSubscription,_onCancel -dart:async,_ControllerSubscription,_onPause -dart:async,_ControllerSubscription,_onResume -dart:async,_ControllerSubscription,get:_onPause -dart:async,_ControllerSubscription,get:_onResume -dart:async,_Completer,get:future -dart:async,_Completer,get:isCompleted -dart:async,_Completer,_Completer. -dart:async,_Completer,get:completeError -dart:async,Stream,Stream. -dart:async,Stream,Stream._internal -dart:async,Stream,Stream.fromIterable -dart:async,Stream,Stream.eventTransformed -dart:async,Stream,handleError -dart:async,Stream,pipe -dart:async,Stream,transform -dart:async,_PendingEvents,get:_state -dart:async,_PendingEvents,set:_state -dart:async,_PendingEvents,get:isScheduled -dart:async,_PendingEvents,get:_eventScheduled -dart:async,_PendingEvents,schedule -dart:async,_PendingEvents,cancelSchedule -dart:async,_PendingEvents,_PendingEvents. -dart:async,_StreamController,get:_varData -dart:async,_StreamController,set:_varData -dart:async,_StreamController,get:_state -dart:async,_StreamController,set:_state -dart:async,_StreamController,get:_doneFuture -dart:async,_StreamController,set:_doneFuture -dart:async,_StreamController,get:onListen -dart:async,_StreamController,get:onPause -dart:async,_StreamController,get:onResume -dart:async,_StreamController,get:onCancel -dart:async,_StreamController,_StreamController. -dart:async,_StreamController,get:stream -dart:async,_StreamController,get:_isCanceled -dart:async,_StreamController,get:hasListener -dart:async,_StreamController,get:_isInitialState -dart:async,_StreamController,get:isClosed -dart:async,_StreamController,get:isPaused -dart:async,_StreamController,get:_isAddingStream -dart:async,_StreamController,get:_mayAddEvent -dart:async,_StreamController,get:_pendingEvents -dart:async,_StreamController,get:_subscription -dart:async,_StreamController,_ensureDoneFuture -dart:async,_StreamController,add -dart:async,_StreamController,addError -dart:async,_StreamController,close -dart:async,_StreamController,_closeUnchecked -dart:async,_StreamController,_add -dart:async,_StreamController,_addError -dart:async,_StreamController,_subscribe -dart:async,_StreamController,_recordCancel -dart:async,_StreamController,_recordPause -dart:async,_StreamController,_recordResume -dart:async,_StreamController,get:addError -dart:async,_StreamController,get:close -dart:async,_SyncCompleter,complete -dart:async,_SyncCompleter,_SyncCompleter. -dart:async,_AsyncRun,_scheduleImmediate -dart:async,StreamController,StreamController. -dart:async,_AsyncCallbackEntry,get:callback -dart:async,_AsyncCallbackEntry,get:next -dart:async,_AsyncCallbackEntry,set:next -dart:async,_AsyncCallbackEntry,_AsyncCallbackEntry. -dart:async,Future,get:_nullFuture -dart:async,Future,Future. -dart:async,Future,Future.microtask -dart:async,Future,Future.value -dart:async,Future,wait -dart:async,Timer,Timer. -dart:async,Timer,run -dart:async,Timer,_createTimer -dart:async,_HandleErrorStream,_HandleErrorStream. -dart:async,_AsyncCompleter,complete -dart:async,_AsyncCompleter,_AsyncCompleter. -dart:async,_AsyncCompleter,get:complete -dart:async,Zone,get:ROOT -dart:async,Zone,get:_current -dart:async,Zone,get:current -dart:async,Completer,Completer. -dart:async,Completer,Completer.sync -dart:async,_ForwardingStream,get:_source -dart:async,_ForwardingStream,_ForwardingStream. -dart:async,_ForwardingStream,listen -dart:async,_ForwardingStream,_createSubscription -dart:async,_ForwardingStream,_handleData -dart:async,_ForwardingStream,_handleDone -dart:async,_SyncStreamController,_SyncStreamController. -dart:async,StreamView,get:_stream -dart:async,StreamView,StreamView. -dart:async,StreamView,listen -dart:async,_DelayedEvent,get:next -dart:async,_DelayedEvent,set:next -dart:async,_DelayedEvent,_DelayedEvent. -dart:async,_Future,get:_state -dart:async,_Future,set:_state -dart:async,_Future,get:_zone -dart:async,_Future,get:_resultOrListeners -dart:async,_Future,set:_resultOrListeners -dart:async,_Future,_Future. -dart:async,_Future,_Future.immediate -dart:async,_Future,_Future.value -dart:async,_Future,get:_mayComplete -dart:async,_Future,get:_mayAddListener -dart:async,_Future,get:_isChained -dart:async,_Future,get:_isComplete -dart:async,_Future,get:_hasError -dart:async,_Future,_setChained -dart:async,_Future,then -dart:async,_Future,_thenNoZoneRegistration -dart:async,_Future,catchError -dart:async,_Future,whenComplete -dart:async,_Future,_setPendingComplete -dart:async,_Future,_clearPendingComplete -dart:async,_Future,_setValue -dart:async,_Future,_addListener -dart:async,_Future,_prependListeners -dart:async,_Future,_removeListeners -dart:async,_Future,_reverseListeners -dart:async,_Future,_chainForeignFuture -dart:async,_Future,_chainCoreFuture -dart:async,_Future,_complete -dart:async,_Future,_completeWithValue -dart:async,_Future,_asyncComplete -dart:async,_Future,_propagateToListeners -dart:async,_Future,set:_awaiter -dart:async,_FutureListener,get:STATE_THEN -dart:async,_FutureListener,get:STATE_THEN_ONERROR -dart:async,_FutureListener,get:STATE_CATCHERROR -dart:async,_FutureListener,get:STATE_CATCHERROR_TEST -dart:async,_FutureListener,get:STATE_WHENCOMPLETE -dart:async,_FutureListener,get:_nextListener -dart:async,_FutureListener,set:_nextListener -dart:async,_FutureListener,get:result -dart:async,_FutureListener,get:state -dart:async,_FutureListener,get:callback -dart:async,_FutureListener,_FutureListener.then -dart:async,_FutureListener,_FutureListener.catchError -dart:async,_FutureListener,_FutureListener.whenComplete -dart:async,_FutureListener,get:_zone -dart:async,_FutureListener,get:handlesValue -dart:async,_FutureListener,get:handlesComplete -dart:async,_FutureListener,get:_onValue -dart:async,_FutureListener,get:_whenCompleteAction -dart:async,_FutureListener,handleValue -dart:async,_FutureListener,handleWhenComplete -dart:async,_IterablePendingEvents,get:_iterator -dart:async,_IterablePendingEvents,set:_iterator -dart:async,_IterablePendingEvents,_IterablePendingEvents. -dart:async,_IterablePendingEvents,get:isEmpty -dart:async,_IterablePendingEvents,handleNext -dart:async,_BoundSinkStream,get:_sinkMapper -dart:async,_BoundSinkStream,get:_stream -dart:async,_BoundSinkStream,_BoundSinkStream. -dart:async,_BoundSinkStream,listen -dart:async,::,_registerErrorHandler -dart:async,::,_nonNullError -dart:async,::,_microtaskLoop -dart:async,::,_startMicrotaskLoop -dart:async,::,_scheduleAsyncCallback -dart:async,::,scheduleMicrotask -dart:async,::,_runGuarded -dart:async,::,_rootScheduleMicrotask -dart:async,::,get:_ROOT_ZONE -dart:async,::,_asyncThenWrapperHelper -dart:async,::,_asyncErrorWrapperHelper -dart:async,::,_awaitHelper -dart:async,::,_completeOnAsyncReturn -dart:async,::,_asyncStackTraceHelper -dart:async,::,_clearAsyncThreadStackTrace -dart:async,::,_setAsyncThreadStackTrace -dart:async,::,_setScheduleImmediateClosure -dart:convert,_ByteAdapterSink,get:_sink -dart:convert,_ByteAdapterSink,_ByteAdapterSink. -dart:convert,_ByteAdapterSink,add -dart:convert,_ByteAdapterSink,close -dart:convert,JsonDecoder,get:_reviver -dart:convert,JsonDecoder,JsonDecoder. -dart:convert,JsonDecoder,convert -dart:convert,_ChunkedJsonParser,get:STATE_INITIAL -dart:convert,_ChunkedJsonParser,get:STATE_END -dart:convert,_ChunkedJsonParser,get:STATE_ARRAY_EMPTY -dart:convert,_ChunkedJsonParser,get:STATE_ARRAY_VALUE -dart:convert,_ChunkedJsonParser,get:STATE_ARRAY_COMMA -dart:convert,_ChunkedJsonParser,get:STATE_OBJECT_EMPTY -dart:convert,_ChunkedJsonParser,get:STATE_OBJECT_KEY -dart:convert,_ChunkedJsonParser,get:STATE_OBJECT_COLON -dart:convert,_ChunkedJsonParser,get:STATE_OBJECT_VALUE -dart:convert,_ChunkedJsonParser,get:STATE_OBJECT_COMMA -dart:convert,_ChunkedJsonParser,get:VALUE_READ_BITS -dart:convert,_ChunkedJsonParser,get:listener -dart:convert,_ChunkedJsonParser,get:state -dart:convert,_ChunkedJsonParser,set:state -dart:convert,_ChunkedJsonParser,get:states -dart:convert,_ChunkedJsonParser,get:partialState -dart:convert,_ChunkedJsonParser,_ChunkedJsonParser. -dart:convert,_ChunkedJsonParser,saveState -dart:convert,_ChunkedJsonParser,restoreState -dart:convert,_ChunkedJsonParser,close -dart:convert,_ChunkedJsonParser,parse -dart:convert,_ChunkedJsonParser,parseString -dart:convert,Codec,Codec. -dart:convert,Encoding,Encoding. -dart:convert,ChunkedConversionSink,ChunkedConversionSink. -dart:convert,Utf8Decoder,get:_allowMalformed -dart:convert,Utf8Decoder,Utf8Decoder. -dart:convert,Utf8Decoder,convert -dart:convert,Utf8Decoder,_convertIntercepted -dart:convert,_Utf8Encoder,get:_bufferIndex -dart:convert,_Utf8Encoder,set:_bufferIndex -dart:convert,_Utf8Encoder,get:_buffer -dart:convert,_Utf8Encoder,_Utf8Encoder.withBufferSize -dart:convert,_Utf8Encoder,_createBuffer -dart:convert,_Utf8Encoder,_fillBuffer -dart:convert,_ByteCallbackSink,get:_callback -dart:convert,_ByteCallbackSink,get:_buffer -dart:convert,_ByteCallbackSink,set:_buffer -dart:convert,_ByteCallbackSink,get:_bufferIndex -dart:convert,_ByteCallbackSink,set:_bufferIndex -dart:convert,_ByteCallbackSink,_ByteCallbackSink. -dart:convert,_ByteCallbackSink,add -dart:convert,_ByteCallbackSink,_roundToPowerOf2 -dart:convert,_ByteCallbackSink,close -dart:convert,_ByteCallbackSink,get:add -dart:convert,_ByteCallbackSink,get:close -dart:convert,ByteConversionSinkBase,ByteConversionSinkBase. -dart:convert,_JsonStringifier,get:_seen -dart:convert,_JsonStringifier,_JsonStringifier. -dart:convert,_JsonStringifier,_checkCycle -dart:convert,_JsonStringifier,_removeSeen -dart:convert,_JsonStringifier,writeObject -dart:convert,_JsonStringifier,writeJsonValue -dart:convert,_JsonStringifier,writeMap -dart:convert,ByteConversionSink,ByteConversionSink. -dart:convert,Utf8Encoder,Utf8Encoder. -dart:convert,Utf8Encoder,convert -dart:convert,_JsonListener,_JsonListener. -dart:convert,_ConverterStreamEventSink,get:_chunkedSink -dart:convert,_ConverterStreamEventSink,_ConverterStreamEventSink. -dart:convert,_ConverterStreamEventSink,add -dart:convert,_ConverterStreamEventSink,close -dart:convert,Converter,Converter. -dart:convert,Converter,bind -dart:convert,JsonEncoder,get:indent -dart:convert,JsonEncoder,get:_toEncodable -dart:convert,JsonEncoder,JsonEncoder. -dart:convert,JsonEncoder,convert -dart:convert,_JsonStringStringifier,get:_sink -dart:convert,_JsonStringStringifier,_JsonStringStringifier. -dart:convert,_JsonStringStringifier,stringify -dart:convert,_JsonStringStringifier,printOn -dart:convert,_JsonStringStringifier,writeString -dart:convert,JsonCodec,get:_reviver -dart:convert,JsonCodec,get:_toEncodable -dart:convert,JsonCodec,JsonCodec. -dart:convert,JsonCodec,decode -dart:convert,JsonCodec,encode -dart:convert,JsonCodec,get:encoder -dart:convert,JsonCodec,get:decoder -dart:convert,Utf8Codec,get:_allowMalformed -dart:convert,Utf8Codec,Utf8Codec. -dart:convert,Utf8Codec,decode -dart:convert,Utf8Codec,get:encoder -dart:convert,_Utf8Decoder,get:_stringSink -dart:convert,_Utf8Decoder,set:_isFirstCharacter -dart:convert,_Utf8Decoder,get:_value -dart:convert,_Utf8Decoder,set:_value -dart:convert,_Utf8Decoder,get:_expectedUnits -dart:convert,_Utf8Decoder,set:_expectedUnits -dart:convert,_Utf8Decoder,get:_extraUnits -dart:convert,_Utf8Decoder,set:_extraUnits -dart:convert,_Utf8Decoder,_Utf8Decoder. -dart:convert,_Utf8Decoder,get:hasPartialInput -dart:convert,_Utf8Decoder,get:_LIMITS -dart:convert,_Utf8Decoder,flush -dart:convert,_Utf8Decoder,convert -dart:convert,_BuildJsonListener,get:stack -dart:convert,_BuildJsonListener,get:currentContainer -dart:convert,_BuildJsonListener,set:currentContainer -dart:convert,_BuildJsonListener,get:key -dart:convert,_BuildJsonListener,set:key -dart:convert,_BuildJsonListener,get:value -dart:convert,_BuildJsonListener,set:value -dart:convert,_BuildJsonListener,pushContainer -dart:convert,_BuildJsonListener,popContainer -dart:convert,_BuildJsonListener,handleString -dart:convert,_BuildJsonListener,beginObject -dart:convert,_BuildJsonListener,propertyName -dart:convert,_BuildJsonListener,propertyValue -dart:convert,_BuildJsonListener,endObject -dart:convert,_BuildJsonListener,beginArray -dart:convert,_BuildJsonListener,arrayElement -dart:convert,_BuildJsonListener,endArray -dart:convert,_BuildJsonListener,get:result -dart:convert,_BuildJsonListener,_BuildJsonListener. -dart:convert,_JsonStringParser,get:chunk -dart:convert,_JsonStringParser,set:chunk -dart:convert,_JsonStringParser,get:chunkEnd -dart:convert,_JsonStringParser,set:chunkEnd -dart:convert,_JsonStringParser,_JsonStringParser. -dart:convert,_JsonStringParser,getChar -dart:convert,_JsonStringParser,getString -dart:convert,::,get:JSON -dart:convert,::,get:UTF8 -dart:convert,::,_isLeadSurrogate -dart:convert,::,_parseJson -dart:_internal,ListBase&&UnmodifiableListMixin,ListBase&&UnmodifiableListMixin. -dart:_internal,ReversedListIterable,get:_source -dart:_internal,ReversedListIterable,ReversedListIterable. -dart:_internal,ReversedListIterable,get:length -dart:_internal,ReversedListIterable,elementAt -dart:_internal,ListIterator,get:_iterable -dart:_internal,ListIterator,get:_length -dart:_internal,ListIterator,get:_index -dart:_internal,ListIterator,set:_index -dart:_internal,ListIterator,get:_current -dart:_internal,ListIterator,set:_current -dart:_internal,ListIterator,ListIterator. -dart:_internal,ListIterator,get:current -dart:_internal,ListIterator,moveNext -dart:_internal,Lists,copy -dart:_internal,UnmodifiableListBase,UnmodifiableListBase. -dart:_internal,ListIterable,ListIterable. -dart:_internal,ListIterable,get:iterator -dart:_internal,ListIterable,map -dart:_internal,ListIterable,toList -dart:_internal,SubListIterable,get:_iterable -dart:_internal,SubListIterable,get:_start -dart:_internal,SubListIterable,get:_endOrLength -dart:_internal,SubListIterable,SubListIterable. -dart:_internal,SubListIterable,get:_endIndex -dart:_internal,SubListIterable,get:_startIndex -dart:_internal,SubListIterable,get:length -dart:_internal,SubListIterable,elementAt -dart:_internal,CodeUnits,get:_string -dart:_internal,CodeUnits,CodeUnits. -dart:_internal,CodeUnits,get:length -dart:_internal,CodeUnits,[] -dart:_internal,ClassID,getID -dart:_internal,MappedListIterable,get:_source -dart:_internal,MappedListIterable,get:_f -dart:_internal,MappedListIterable,MappedListIterable. -dart:_internal,MappedListIterable,get:length -dart:_internal,MappedListIterable,elementAt -dart:_internal,Sort,sort -dart:_internal,Sort,_doSort -dart:_internal,Sort,_insertionSort -dart:_internal,EfficientLengthIterable,EfficientLengthIterable. -dart:_internal,::,get:POWERS_OF_TEN -dart:_internal,::,get:is64Bit -dart:_internal,::,_inquireIs64Bit -dart:_internal,::,printToConsole -dart:_internal,::,get:_printClosure -dart:math,_Random,get:_state -dart:math,_Random,_Random._withState -dart:math,_Random,_nextState -dart:math,_Random,nextInt -dart:math,_Random,get:_POW2_32 -dart:math,_Random,get:_prng -dart:math,_Random,_setupSeed -dart:math,_Random,_initialSeed -dart:math,_Random,_nextSeed -dart:math,Random,Random. -dart:math,::,min -dart:math,::,max -dart:math,::,pow -dart:math,::,_doublePow -dart:math,::,_pow -dart:math,::,sin -dart:math,::,cos -dart:zircon,HandleWaiter,HandleWaiter. -dart:zircon,HandleWaiter,_constructor -dart:zircon,HandleWaiter,asyncWait -dart:zircon,HandleWaiter,cancelWait -dart:zircon,HandleWaiter,_asyncWait -dart:zircon,HandleWaiter,_cancelWait -dart:zircon,HandleWaiter,get:_callback -dart:zircon,HandleWaiter,set:_callback -dart:fuchsia,MxStartupInfo,takeEnvironment -dart:fuchsia,MxStartupInfo,takeOutgoingServices -dart:zircon,MxHandle,_registerFinalizer -dart:zircon,MxHandle,_unregisterFinalizer -dart:zircon,MxHandle,close -dart:zircon,MxChannel,create -dart:zircon,MxChannel,write -dart:zircon,MxChannel,queryAndRead -dart:zircon,Handle,Handle. -dart:zircon,Handle,get:h -dart:zircon,Handle,get:_h -dart:zircon,Handle,set:_h -dart:zircon,Handle,get:isValid -dart:zircon,Handle,close -dart:zircon,Handle,release -dart:io,_Filter,_newZLibInflateFilter -dart:io,_HttpOutboundMessage,get:_outgoing -dart:io,_HttpOutboundMessage,get:headers -dart:io,_HttpOutboundMessage,_HttpOutboundMessage. -dart:io,_HttpOutboundMessage,set:contentLength -dart:io,_HttpOutboundMessage,get:persistentConnection -dart:io,_HttpOutboundMessage,set:persistentConnection -dart:io,_HttpOutboundMessage,get:_isConnectionClosed -dart:io,SocketDirection,get:RECEIVE -dart:io,SocketDirection,SocketDirection._ -dart:io,_HttpInboundMessage,get:_incoming -dart:io,_HttpInboundMessage,_HttpInboundMessage. -dart:io,_HttpInboundMessage,get:headers -dart:io,_HttpInboundMessage,get:contentLength -dart:io,_HttpInboundMessage,get:persistentConnection -dart:io,_IOService,get:_servicePorts -dart:io,_IOService,get:_messageMap -dart:io,_IOService,_dispatch -dart:io,_IOService,_ensureInitialize -dart:io,_IOService,_finalize -dart:io,_IOService,_getNextId -dart:io,_InternetAddress,get:_in_addr -dart:io,_InternetAddress,get:type -dart:io,_InternetAddress,get:isLoopback -dart:io,_InternetAddress,_InternetAddress. -dart:io,_NativeSocket,get:FIRST_EVENT -dart:io,_NativeSocket,get:LAST_EVENT -dart:io,_NativeSocket,get:EVENT_COUNT -dart:io,_NativeSocket,get:TYPE_LISTENING_SOCKET -dart:io,_NativeSocket,get:TYPE_TYPE_MASK -dart:io,_NativeSocket,get:TYPE_TCP_SOCKET -dart:io,_NativeSocket,get:_RETRY_DURATION -dart:io,_NativeSocket,get:_RETRY_DURATION_LOOPBACK -dart:io,_NativeSocket,get:isClosed -dart:io,_NativeSocket,set:isClosed -dart:io,_NativeSocket,get:isClosing -dart:io,_NativeSocket,set:isClosing -dart:io,_NativeSocket,get:isClosedRead -dart:io,_NativeSocket,get:closedReadEventSent -dart:io,_NativeSocket,get:isClosedWrite -dart:io,_NativeSocket,get:closeCompleter -dart:io,_NativeSocket,get:eventHandlers -dart:io,_NativeSocket,get:eventPort -dart:io,_NativeSocket,set:eventPort -dart:io,_NativeSocket,get:flagsSent -dart:io,_NativeSocket,set:flagsSent -dart:io,_NativeSocket,get:typeFlags -dart:io,_NativeSocket,get:localPort -dart:io,_NativeSocket,set:localPort -dart:io,_NativeSocket,get:localAddress -dart:io,_NativeSocket,set:localAddress -dart:io,_NativeSocket,get:available -dart:io,_NativeSocket,set:available -dart:io,_NativeSocket,get:tokens -dart:io,_NativeSocket,set:tokens -dart:io,_NativeSocket,get:sendReadEvents -dart:io,_NativeSocket,set:sendReadEvents -dart:io,_NativeSocket,get:readEventIssued -dart:io,_NativeSocket,set:readEventIssued -dart:io,_NativeSocket,get:sendWriteEvents -dart:io,_NativeSocket,set:sendWriteEvents -dart:io,_NativeSocket,get:writeEventIssued -dart:io,_NativeSocket,set:writeEventIssued -dart:io,_NativeSocket,get:writeAvailable -dart:io,_NativeSocket,set:writeAvailable -dart:io,_NativeSocket,get:resourceInfo -dart:io,_NativeSocket,set:resourceInfo -dart:io,_NativeSocket,set:owner -dart:io,_NativeSocket,lookup -dart:io,_NativeSocket,connect -dart:io,_NativeSocket,setupResourceInfo -dart:io,_NativeSocket,_NativeSocket.normal -dart:io,_NativeSocket,get:isListening -dart:io,_NativeSocket,read -dart:io,_NativeSocket,write -dart:io,_NativeSocket,get:port -dart:io,_NativeSocket,get:address -dart:io,_NativeSocket,issueReadEvent -dart:io,_NativeSocket,issueWriteEvent -dart:io,_NativeSocket,multiplex -dart:io,_NativeSocket,returnTokens -dart:io,_NativeSocket,setHandlers -dart:io,_NativeSocket,setListening -dart:io,_NativeSocket,close -dart:io,_NativeSocket,sendToEventHandler -dart:io,_NativeSocket,connectToEventHandler -dart:io,_NativeSocket,disconnectFromEventHandler -dart:io,_NativeSocket,isErrorResponse -dart:io,_NativeSocket,setOption -dart:io,_NativeSocket,nativeAvailable -dart:io,_NativeSocket,nativeRead -dart:io,_NativeSocket,nativeWrite -dart:io,_NativeSocket,nativeCreateConnect -dart:io,_NativeSocket,nativeGetPort -dart:io,_NativeSocket,nativeSetOption -dart:io,_NativeSocket,get:multiplex -dart:io,_HttpClientResponse,get:_httpClient -dart:io,_HttpClientResponse,get:_httpRequest -dart:io,_HttpClientResponse,_HttpClientResponse. -dart:io,_HttpClientResponse,get:statusCode -dart:io,_HttpClientResponse,get:reasonPhrase -dart:io,_HttpClientResponse,get:isRedirect -dart:io,_HttpClientResponse,listen -dart:io,_HttpClientResponse,get:_shouldAuthenticateProxy -dart:io,_HttpClientResponse,get:_shouldAuthenticate -dart:io,_ConnectionTarget,get:key -dart:io,_ConnectionTarget,get:host -dart:io,_ConnectionTarget,get:port -dart:io,_ConnectionTarget,get:isSecure -dart:io,_ConnectionTarget,get:context -dart:io,_ConnectionTarget,get:_idle -dart:io,_ConnectionTarget,get:_active -dart:io,_ConnectionTarget,get:_pending -dart:io,_ConnectionTarget,get:_connecting -dart:io,_ConnectionTarget,set:_connecting -dart:io,_ConnectionTarget,_ConnectionTarget. -dart:io,_ConnectionTarget,get:isEmpty -dart:io,_ConnectionTarget,get:hasIdle -dart:io,_ConnectionTarget,_checkPending -dart:io,_ConnectionTarget,addNewActive -dart:io,_ConnectionTarget,returnConnection -dart:io,_ConnectionTarget,connectionClosed -dart:io,_ConnectionTarget,close -dart:io,_ConnectionTarget,connect -dart:io,_HttpParser,get:_parserCalled -dart:io,_HttpParser,set:_parserCalled -dart:io,_HttpParser,get:_buffer -dart:io,_HttpParser,set:_buffer -dart:io,_HttpParser,get:_index -dart:io,_HttpParser,set:_index -dart:io,_HttpParser,get:_requestParser -dart:io,_HttpParser,get:_state -dart:io,_HttpParser,set:_state -dart:io,_HttpParser,get:_httpVersionIndex -dart:io,_HttpParser,set:_httpVersionIndex -dart:io,_HttpParser,get:_messageType -dart:io,_HttpParser,set:_messageType -dart:io,_HttpParser,get:_statusCode -dart:io,_HttpParser,set:_statusCode -dart:io,_HttpParser,get:_statusCodeLength -dart:io,_HttpParser,set:_statusCodeLength -dart:io,_HttpParser,get:_method -dart:io,_HttpParser,get:_uri_or_reason_phrase -dart:io,_HttpParser,get:_headerField -dart:io,_HttpParser,get:_headerValue -dart:io,_HttpParser,get:_httpVersion -dart:io,_HttpParser,set:_httpVersion -dart:io,_HttpParser,get:_transferLength -dart:io,_HttpParser,set:_transferLength -dart:io,_HttpParser,set:_persistentConnection -dart:io,_HttpParser,get:_connectionUpgrade -dart:io,_HttpParser,set:_connectionUpgrade -dart:io,_HttpParser,get:_chunked -dart:io,_HttpParser,set:_chunked -dart:io,_HttpParser,get:_noMessageBody -dart:io,_HttpParser,set:_noMessageBody -dart:io,_HttpParser,get:_remainingContent -dart:io,_HttpParser,set:_remainingContent -dart:io,_HttpParser,get:_headers -dart:io,_HttpParser,set:_headers -dart:io,_HttpParser,get:_incoming -dart:io,_HttpParser,set:_incoming -dart:io,_HttpParser,get:_socketSubscription -dart:io,_HttpParser,set:_socketSubscription -dart:io,_HttpParser,get:_paused -dart:io,_HttpParser,set:_paused -dart:io,_HttpParser,get:_bodyPaused -dart:io,_HttpParser,set:_bodyPaused -dart:io,_HttpParser,get:_controller -dart:io,_HttpParser,set:_controller -dart:io,_HttpParser,get:_bodyController -dart:io,_HttpParser,set:_bodyController -dart:io,_HttpParser,_HttpParser.responseParser -dart:io,_HttpParser,_HttpParser._ -dart:io,_HttpParser,listen -dart:io,_HttpParser,listenToStream -dart:io,_HttpParser,_parse -dart:io,_HttpParser,_headersEnd -dart:io,_HttpParser,_doParse -dart:io,_HttpParser,_onData -dart:io,_HttpParser,_onDone -dart:io,_HttpParser,get:version -dart:io,_HttpParser,set:isHead -dart:io,_HttpParser,_reset -dart:io,_HttpParser,_releaseBuffer -dart:io,_HttpParser,_isTokenChar -dart:io,_HttpParser,_toLowerCaseByte -dart:io,_HttpParser,_expect -dart:io,_HttpParser,_createIncoming -dart:io,_HttpParser,_closeIncoming -dart:io,_HttpParser,_pauseStateChanged -dart:io,_HttpParser,_reportError -dart:io,_HttpParser,get:_onData -dart:io,_HttpParser,get:_onDone -dart:io,Socket,connect -dart:io,_Const,get:HTTP -dart:io,_Const,get:HTTP1DOT -dart:io,_Const,get:HTTP11 -dart:io,_Const,get:SEPARATOR_MAP -dart:io,FileStat,get:_notFound -dart:io,FileStat,get:size -dart:io,FileStat,FileStat._internal -dart:io,FileStat,FileStat._internalNotFound -dart:io,FileStat,stat -dart:io,File,File. -dart:io,RawSocketEvent,get:READ -dart:io,RawSocketEvent,get:WRITE -dart:io,RawSocketEvent,get:READ_CLOSED -dart:io,RawSocketEvent,get:CLOSED -dart:io,RawSocketEvent,RawSocketEvent._ -dart:io,_Platform,get:operatingSystem -dart:io,_Platform,get:localHostname -dart:io,_Platform,get:environment -dart:io,_Platform,get:version -dart:io,_Platform,_operatingSystem -dart:io,_Platform,_localHostname -dart:io,_Platform,_environment -dart:io,_Platform,_version -dart:io,_Platform,set:_nativeScript -dart:io,ZLibDecoder,get:windowBits -dart:io,ZLibDecoder,get:dictionary -dart:io,ZLibDecoder,get:raw -dart:io,ZLibDecoder,ZLibDecoder. -dart:io,ZLibDecoder,startChunkedConversion -dart:io,_ProxyConfiguration,_ProxyConfiguration. -dart:io,_ProxyConfiguration,_ProxyConfiguration.direct -dart:io,_ProxyConfiguration,get:proxies -dart:io,_NativeSocketNativeWrapper,_NativeSocketNativeWrapper. -dart:io,Directory,Directory. -dart:io,_FilterImpl,process -dart:io,_FilterImpl,processed -dart:io,_FilterImpl,_FilterImpl. -dart:io,FileSystemEntityType,get:FILE -dart:io,FileSystemEntityType,get:DIRECTORY -dart:io,FileSystemEntityType,get:LINK -dart:io,FileSystemEntityType,get:NOT_FOUND -dart:io,FileSystemEntityType,get:_typeList -dart:io,FileSystemEntityType,FileSystemEntityType._internal -dart:io,FileSystemEntityType,_lookup -dart:io,_HttpOutgoing,get:_doneCompleter -dart:io,_HttpOutgoing,get:socket -dart:io,_HttpOutgoing,get:ignoreBody -dart:io,_HttpOutgoing,get:headersWritten -dart:io,_HttpOutgoing,set:headersWritten -dart:io,_HttpOutgoing,get:_buffer -dart:io,_HttpOutgoing,set:_buffer -dart:io,_HttpOutgoing,get:_length -dart:io,_HttpOutgoing,set:_length -dart:io,_HttpOutgoing,get:_closeFuture -dart:io,_HttpOutgoing,set:_closeFuture -dart:io,_HttpOutgoing,get:chunked -dart:io,_HttpOutgoing,get:contentLength -dart:io,_HttpOutgoing,set:contentLength -dart:io,_HttpOutgoing,get:_bytesWritten -dart:io,_HttpOutgoing,get:_socketError -dart:io,_HttpOutgoing,get:outbound -dart:io,_HttpOutgoing,set:outbound -dart:io,_HttpOutgoing,_HttpOutgoing. -dart:io,_HttpOutgoing,writeHeaders -dart:io,_HttpOutgoing,addStream -dart:io,_HttpOutgoing,close -dart:io,_HttpOutgoing,get:done -dart:io,_HttpOutgoing,setHeader -dart:io,SocketOption,get:TCP_NODELAY -dart:io,SocketOption,get:_value -dart:io,SocketOption,SocketOption._ -dart:io,_IOSinkImpl,set:_encodingMutable -dart:io,_IOSinkImpl,_IOSinkImpl. -dart:io,_SocketResourceInfo,get:openSockets -dart:io,_SocketResourceInfo,_SocketResourceInfo. -dart:io,_SocketResourceInfo,SocketOpened -dart:io,_SocketResourceInfo,SocketClosed -dart:io,_ConnectionInfo,get:connection -dart:io,_ConnectionInfo,get:proxy -dart:io,_ConnectionInfo,_ConnectionInfo. -dart:io,_AuthenticationScheme,get:DIGEST -dart:io,_AuthenticationScheme,_AuthenticationScheme. -dart:io,_RawSocket,get:_socket -dart:io,_RawSocket,get:_controller -dart:io,_RawSocket,set:_controller -dart:io,_RawSocket,get:_readEventsEnabled -dart:io,_RawSocket,set:_readEventsEnabled -dart:io,_RawSocket,get:_writeEventsEnabled -dart:io,_RawSocket,set:_writeEventsEnabled -dart:io,_RawSocket,get:_isMacOSTerminalInput -dart:io,_RawSocket,connect -dart:io,_RawSocket,_RawSocket. -dart:io,_RawSocket,listen -dart:io,_RawSocket,read -dart:io,_RawSocket,write -dart:io,_RawSocket,close -dart:io,_RawSocket,set:readEventsEnabled -dart:io,_RawSocket,set:writeEventsEnabled -dart:io,_RawSocket,setOption -dart:io,_RawSocket,_resume -dart:io,_RawSocket,_onSubscriptionStateChange -dart:io,_RawSocket,get:_onSubscriptionStateChange -dart:io,_RawSocket,get:_onPauseStateChange -dart:io,_CopyingBytesBuilder,get:_emptyList -dart:io,_CopyingBytesBuilder,get:_length -dart:io,_CopyingBytesBuilder,set:_length -dart:io,_CopyingBytesBuilder,get:_buffer -dart:io,_CopyingBytesBuilder,set:_buffer -dart:io,_CopyingBytesBuilder,_CopyingBytesBuilder. -dart:io,_CopyingBytesBuilder,add -dart:io,_CopyingBytesBuilder,addByte -dart:io,_CopyingBytesBuilder,takeBytes -dart:io,_CopyingBytesBuilder,clear -dart:io,_CopyingBytesBuilder,_pow2roundup -dart:io,_File,get:path -dart:io,_File,_File. -dart:io,_File,exists -dart:io,_Proxy,get:username -dart:io,_Proxy,get:isDirect -dart:io,_Proxy,_Proxy.direct -dart:io,_Proxy,get:isAuthenticated -dart:io,_State,get:FIRST_BODY_STATE -dart:io,_HttpClientRequest,get:method -dart:io,_HttpClientRequest,get:uri -dart:io,_HttpClientRequest,get:cookies -dart:io,_HttpClientRequest,get:_httpClient -dart:io,_HttpClientRequest,get:_responseCompleter -dart:io,_HttpClientRequest,get:_proxy -dart:io,_HttpClientRequest,get:_response -dart:io,_HttpClientRequest,set:_response -dart:io,_HttpClientRequest,get:_followRedirects -dart:io,_HttpClientRequest,set:_followRedirects -dart:io,_HttpClientRequest,set:_maxRedirects -dart:io,_HttpClientRequest,_HttpClientRequest. -dart:io,_HttpClientRequest,get:done -dart:io,_HttpClientRequest,close -dart:io,_HttpClientRequest,set:maxRedirects -dart:io,_HttpClientRequest,get:followRedirects -dart:io,_HttpClientRequest,set:followRedirects -dart:io,_HttpClientRequest,_onIncoming -dart:io,_HttpClientRequest,_requestUri -dart:io,_HttpClientRequest,_writeHeader -dart:io,_IOServicePorts,get:_ports -dart:io,_IOServicePorts,get:_freePorts -dart:io,_IOServicePorts,get:_usedPorts -dart:io,_IOServicePorts,_IOServicePorts. -dart:io,_IOServicePorts,_getPort -dart:io,_IOServicePorts,_returnPort -dart:io,_IOServicePorts,_newServicePort -dart:io,_HttpClient,get:_closing -dart:io,_HttpClient,set:_closing -dart:io,_HttpClient,get:_closingForcefully -dart:io,_HttpClient,set:_closingForcefully -dart:io,_HttpClient,get:_connectionTargets -dart:io,_HttpClient,get:_credentials -dart:io,_HttpClient,get:_context -dart:io,_HttpClient,get:_findProxy -dart:io,_HttpClient,get:_idleTimeout -dart:io,_HttpClient,get:_badCertificateCallback -dart:io,_HttpClient,get:idleTimeout -dart:io,_HttpClient,get:maxConnectionsPerHost -dart:io,_HttpClient,get:autoUncompress -dart:io,_HttpClient,get:userAgent -dart:io,_HttpClient,_HttpClient. -dart:io,_HttpClient,openUrl -dart:io,_HttpClient,close -dart:io,_HttpClient,_openUrl -dart:io,_HttpClient,_returnConnection -dart:io,_HttpClient,_connectionClosed -dart:io,_HttpClient,_connectionsChanged -dart:io,_HttpClient,_closeConnections -dart:io,_HttpClient,_getConnectionTarget -dart:io,_HttpClient,_getConnection -dart:io,_HttpClient,_findCredentials -dart:io,_HttpClient,_findProxyFromEnvironment -dart:io,_HttpClient,get:_platformEnvironmentCache -dart:io,_HttpClientConnection,get:key -dart:io,_HttpClientConnection,get:_socket -dart:io,_HttpClientConnection,get:_httpParser -dart:io,_HttpClientConnection,get:_subscription -dart:io,_HttpClientConnection,set:_subscription -dart:io,_HttpClientConnection,get:_httpClient -dart:io,_HttpClientConnection,get:_dispose -dart:io,_HttpClientConnection,get:_idleTimer -dart:io,_HttpClientConnection,set:_idleTimer -dart:io,_HttpClientConnection,get:closed -dart:io,_HttpClientConnection,set:closed -dart:io,_HttpClientConnection,set:_currentUri -dart:io,_HttpClientConnection,get:_nextResponseCompleter -dart:io,_HttpClientConnection,set:_nextResponseCompleter -dart:io,_HttpClientConnection,get:_streamFuture -dart:io,_HttpClientConnection,set:_streamFuture -dart:io,_HttpClientConnection,_HttpClientConnection. -dart:io,_HttpClientConnection,send -dart:io,_HttpClientConnection,close -dart:io,_HttpClientConnection,makeKey -dart:io,_HttpClientConnection,stopTimer -dart:io,_HttpClientConnection,startTimer -dart:io,_ReadWriteResourceInfo,get:totalRead -dart:io,_ReadWriteResourceInfo,set:totalRead -dart:io,_ReadWriteResourceInfo,get:totalWritten -dart:io,_ReadWriteResourceInfo,set:totalWritten -dart:io,_ReadWriteResourceInfo,get:readCount -dart:io,_ReadWriteResourceInfo,set:readCount -dart:io,_ReadWriteResourceInfo,get:writeCount -dart:io,_ReadWriteResourceInfo,set:writeCount -dart:io,_ReadWriteResourceInfo,set:lastRead -dart:io,_ReadWriteResourceInfo,set:lastWrite -dart:io,_ReadWriteResourceInfo,addRead -dart:io,_ReadWriteResourceInfo,didRead -dart:io,_ReadWriteResourceInfo,addWrite -dart:io,_ReadWriteResourceInfo,_ReadWriteResourceInfo. -dart:io,HttpClient,HttpClient. -dart:io,HttpClient,findProxyFromEnvironment -dart:io,_StreamSinkImpl,get:_target -dart:io,_StreamSinkImpl,get:_doneCompleter -dart:io,_StreamSinkImpl,get:_controllerInstance -dart:io,_StreamSinkImpl,set:_controllerInstance -dart:io,_StreamSinkImpl,get:_controllerCompleter -dart:io,_StreamSinkImpl,set:_controllerCompleter -dart:io,_StreamSinkImpl,get:_isClosed -dart:io,_StreamSinkImpl,set:_isClosed -dart:io,_StreamSinkImpl,get:_isBound -dart:io,_StreamSinkImpl,set:_isBound -dart:io,_StreamSinkImpl,get:_hasError -dart:io,_StreamSinkImpl,_StreamSinkImpl. -dart:io,_StreamSinkImpl,add -dart:io,_StreamSinkImpl,addStream -dart:io,_StreamSinkImpl,flush -dart:io,_StreamSinkImpl,close -dart:io,_StreamSinkImpl,_closeTarget -dart:io,_StreamSinkImpl,get:done -dart:io,_StreamSinkImpl,_completeDoneValue -dart:io,_StreamSinkImpl,get:_controller -dart:io,_StreamSinkImpl,get:_completeDoneValue -dart:io,_StreamSinkImpl,get:_completeDoneError -dart:io,_Directory,get:path -dart:io,_Directory,_Directory. -dart:io,_IOResourceInfo,get:id -dart:io,_IOResourceInfo,get:_sw -dart:io,_IOResourceInfo,get:_startTime -dart:io,_IOResourceInfo,get:timestamp -dart:io,_IOResourceInfo,_IOResourceInfo. -dart:io,_IOResourceInfo,getNextID -dart:io,_ZLibInflateFilter,_ZLibInflateFilter. -dart:io,_ZLibInflateFilter,_init -dart:io,_FilterSink,get:_filter -dart:io,_FilterSink,get:_sink -dart:io,_FilterSink,get:_closed -dart:io,_FilterSink,set:_closed -dart:io,_FilterSink,get:_empty -dart:io,_FilterSink,set:_empty -dart:io,_FilterSink,_FilterSink. -dart:io,_FilterSink,add -dart:io,_FilterSink,addSlice -dart:io,_FilterSink,close -dart:io,_EventHandler,_sendData -dart:io,_EventHandler,_timerMillisecondClock -dart:io,_Socket,get:_raw -dart:io,_Socket,set:_raw -dart:io,_Socket,set:_closed -dart:io,_Socket,get:_controller -dart:io,_Socket,set:_controller -dart:io,_Socket,get:_controllerClosed -dart:io,_Socket,set:_controllerClosed -dart:io,_Socket,get:_consumer -dart:io,_Socket,set:_consumer -dart:io,_Socket,get:_sink -dart:io,_Socket,set:_sink -dart:io,_Socket,get:_subscription -dart:io,_Socket,set:_subscription -dart:io,_Socket,_Socket. -dart:io,_Socket,listen -dart:io,_Socket,add -dart:io,_Socket,addStream -dart:io,_Socket,flush -dart:io,_Socket,destroy -dart:io,_Socket,setOption -dart:io,_Socket,_ensureRawSocketSubscription -dart:io,_Socket,_closeRawSocket -dart:io,_Socket,_onSubscriptionStateChange -dart:io,_Socket,_onPauseStateChange -dart:io,_Socket,_onData -dart:io,_Socket,_onDone -dart:io,_Socket,_write -dart:io,_Socket,_disableWriteEvent -dart:io,_Socket,get:_onSubscriptionStateChange -dart:io,_Socket,get:_onPauseStateChange -dart:io,_Socket,get:_onData -dart:io,_Socket,get:_onError -dart:io,_Socket,get:_onDone -dart:io,IOSink,IOSink. -dart:io,_BufferAndStart,get:buffer -dart:io,_BufferAndStart,get:start -dart:io,_BufferAndStart,_BufferAndStart. -dart:io,InternetAddressType,get:IP_V4 -dart:io,InternetAddressType,get:IP_V6 -dart:io,InternetAddressType,get:ANY -dart:io,InternetAddressType,get:_value -dart:io,InternetAddressType,InternetAddressType._ -dart:io,InternetAddressType,InternetAddressType._from -dart:io,GZipCodec,get:windowBits -dart:io,GZipCodec,get:dictionary -dart:io,GZipCodec,get:raw -dart:io,GZipCodec,GZipCodec._default -dart:io,GZipCodec,get:decoder -dart:io,_HttpIncoming,get:_dataCompleter -dart:io,_HttpIncoming,get:_stream -dart:io,_HttpIncoming,set:fullBodyRead -dart:io,_HttpIncoming,get:headers -dart:io,_HttpIncoming,get:upgraded -dart:io,_HttpIncoming,get:statusCode -dart:io,_HttpIncoming,set:statusCode -dart:io,_HttpIncoming,get:reasonPhrase -dart:io,_HttpIncoming,set:reasonPhrase -dart:io,_HttpIncoming,set:uri -dart:io,_HttpIncoming,set:hasSubscriber -dart:io,_HttpIncoming,_HttpIncoming. -dart:io,_HttpIncoming,listen -dart:io,_HttpIncoming,get:dataDone -dart:io,_HttpIncoming,close -dart:io,HttpException,HttpException. -dart:io,_NativeSocketNativeWrapper&_ServiceObject,_NativeSocketNativeWrapper&_ServiceObject. -dart:io,_SocketStreamConsumer,get:subscription -dart:io,_SocketStreamConsumer,set:subscription -dart:io,_SocketStreamConsumer,get:socket -dart:io,_SocketStreamConsumer,get:offset -dart:io,_SocketStreamConsumer,set:offset -dart:io,_SocketStreamConsumer,get:buffer -dart:io,_SocketStreamConsumer,set:buffer -dart:io,_SocketStreamConsumer,get:paused -dart:io,_SocketStreamConsumer,set:paused -dart:io,_SocketStreamConsumer,get:streamCompleter -dart:io,_SocketStreamConsumer,set:streamCompleter -dart:io,_SocketStreamConsumer,_SocketStreamConsumer. -dart:io,_SocketStreamConsumer,addStream -dart:io,_SocketStreamConsumer,write -dart:io,_SocketStreamConsumer,done -dart:io,_SocketStreamConsumer,stop -dart:io,_ZLibDecoderSink,_ZLibDecoderSink. -dart:io,RawSocket,connect -dart:io,Platform,get:_operatingSystem -dart:io,Platform,get:_localHostname -dart:io,Platform,get:_version -dart:io,Platform,get:operatingSystem -dart:io,Platform,get:localHostname -dart:io,Platform,get:isLinux -dart:io,Platform,get:isMacOS -dart:io,Platform,get:isWindows -dart:io,Platform,get:isAndroid -dart:io,Platform,get:isIOS -dart:io,Platform,get:environment -dart:io,Platform,get:version -dart:io,FileSystemEntity,stat -dart:io,FileSystemEntity,FileSystemEntity. -dart:io,_HttpHeaders,get:_headers -dart:io,_HttpHeaders,get:protocolVersion -dart:io,_HttpHeaders,get:_mutable -dart:io,_HttpHeaders,set:_mutable -dart:io,_HttpHeaders,get:_noFoldingHeaders -dart:io,_HttpHeaders,get:_contentLength -dart:io,_HttpHeaders,set:_contentLength -dart:io,_HttpHeaders,get:_persistentConnection -dart:io,_HttpHeaders,get:_chunkedTransferEncoding -dart:io,_HttpHeaders,get:_host -dart:io,_HttpHeaders,set:_host -dart:io,_HttpHeaders,get:_port -dart:io,_HttpHeaders,set:_port -dart:io,_HttpHeaders,get:_defaultPortForScheme -dart:io,_HttpHeaders,_HttpHeaders. -dart:io,_HttpHeaders,[] -dart:io,_HttpHeaders,value -dart:io,_HttpHeaders,forEach -dart:io,_HttpHeaders,get:persistentConnection -dart:io,_HttpHeaders,set:persistentConnection -dart:io,_HttpHeaders,get:contentLength -dart:io,_HttpHeaders,set:contentLength -dart:io,_HttpHeaders,get:chunkedTransferEncoding -dart:io,_HttpHeaders,get:host -dart:io,_HttpHeaders,set:host -dart:io,_HttpHeaders,set:port -dart:io,_HttpHeaders,_add -dart:io,_HttpHeaders,_addContentLength -dart:io,_HttpHeaders,_addDate -dart:io,_HttpHeaders,_addExpires -dart:io,_HttpHeaders,_addContentType -dart:io,_HttpHeaders,_addValue -dart:io,_HttpHeaders,_set -dart:io,_HttpHeaders,_checkMutable -dart:io,_HttpHeaders,_updateHostHeader -dart:io,_HttpHeaders,_foldHeader -dart:io,_HttpHeaders,_finalize -dart:io,_HttpHeaders,_build -dart:io,SocketException,SocketException.closed -dart:io,::,_isErrorResponse -dart:io,::,_ensureFastAndSerializableByteData -dart:io,::,get:GZIP -dart:io,::,_validateZLibWindowBits -dart:io,::,get:_OUTGOING_BUFFER_SIZE -dart:io,::,_getHttpVersion -dart:io,::,_setupHooks -dart:io,::,_throwOnBadPort -dart:ui,TextBaseline,get:index -dart:ui,StrokeCap,get:index -dart:ui,BlurStyle,get:index -dart:ui,Rect,Rect.fromLTRB -dart:ui,Rect,Rect.fromLTWH -dart:ui,Rect,get:_value -dart:ui,Rect,get:left -dart:ui,Rect,get:top -dart:ui,Rect,get:right -dart:ui,Rect,get:bottom -dart:ui,Rect,get:width -dart:ui,Rect,get:height -dart:ui,Rect,get:size -dart:ui,Rect,get:isFinite -dart:ui,Rect,shift -dart:ui,Rect,inflate -dart:ui,Rect,get:shortestSide -dart:ui,Rect,get:topLeft -dart:ui,Rect,get:center -dart:ui,Rect,== -dart:ui,Scene,dispose -dart:ui,Paragraph,get:width -dart:ui,Paragraph,get:height -dart:ui,Paragraph,get:maxIntrinsicWidth -dart:ui,Paragraph,get:didExceedMaxLines -dart:ui,Paragraph,layout -dart:ui,Paragraph,_layout -dart:ui,Paragraph,_paint -dart:ui,TextStyle,TextStyle. -dart:ui,TextStyle,get:_encoded -dart:ui,TextStyle,get:_fontFamily -dart:ui,TextStyle,get:_fontSize -dart:ui,TextStyle,get:_letterSpacing -dart:ui,TextStyle,get:_wordSpacing -dart:ui,TextStyle,get:_height -dart:ui,TextStyle,get:_locale -dart:ui,FilterQuality,get:index -dart:ui,ParagraphStyle,ParagraphStyle. -dart:ui,ParagraphStyle,get:_encoded -dart:ui,ParagraphStyle,get:_fontFamily -dart:ui,ParagraphStyle,get:_fontSize -dart:ui,ParagraphStyle,get:_lineHeight -dart:ui,ParagraphStyle,get:_ellipsis -dart:ui,ParagraphStyle,get:_locale -dart:ui,_Jenkins,combine -dart:ui,_Jenkins,finish -dart:ui,_HashEnd,_HashEnd. -dart:ui,ParagraphBuilder,ParagraphBuilder. -dart:ui,ParagraphBuilder,_constructor -dart:ui,ParagraphBuilder,pushStyle -dart:ui,ParagraphBuilder,_pushStyle -dart:ui,ParagraphBuilder,pop -dart:ui,ParagraphBuilder,addText -dart:ui,ParagraphBuilder,build -dart:ui,FontWeight,FontWeight._ -dart:ui,FontWeight,get:index -dart:ui,FontWeight,get:w100 -dart:ui,FontWeight,get:w400 -dart:ui,FontWeight,get:w500 -dart:ui,WindowPadding,WindowPadding._ -dart:ui,WindowPadding,get:left -dart:ui,WindowPadding,get:top -dart:ui,WindowPadding,get:right -dart:ui,WindowPadding,get:bottom -dart:ui,WindowPadding,get:zero -dart:ui,OffsetBase,OffsetBase. -dart:ui,OffsetBase,get:_dx -dart:ui,OffsetBase,get:_dy -dart:ui,Color,Color. -dart:ui,Color,Color.fromARGB -dart:ui,Color,get:value -dart:ui,Color,get:alpha -dart:ui,Color,get:red -dart:ui,Color,get:green -dart:ui,Color,get:blue -dart:ui,Color,withAlpha -dart:ui,Color,== -dart:ui,Offset,Offset. -dart:ui,Offset,get:dx -dart:ui,Offset,get:dy -dart:ui,Offset,get:zero -dart:ui,Offset,translate -dart:ui,Offset,+ -dart:ui,Offset,& -dart:ui,Offset,== -dart:ui,MaskFilter,MaskFilter.blur -dart:ui,MaskFilter,_constructor -dart:ui,Size,Size. -dart:ui,Size,get:width -dart:ui,Size,get:height -dart:ui,Size,get:zero -dart:ui,Size,- -dart:ui,Size,/ -dart:ui,Size,== -dart:ui,Window,Window._ -dart:ui,Window,get:devicePixelRatio -dart:ui,Window,get:_devicePixelRatio -dart:ui,Window,set:_devicePixelRatio -dart:ui,Window,get:physicalSize -dart:ui,Window,get:_physicalSize -dart:ui,Window,set:_physicalSize -dart:ui,Window,get:padding -dart:ui,Window,get:_padding -dart:ui,Window,set:_padding -dart:ui,Window,get:onMetricsChanged -dart:ui,Window,set:onMetricsChanged -dart:ui,Window,set:_locale -dart:ui,Window,get:onLocaleChanged -dart:ui,Window,set:onLocaleChanged -dart:ui,Window,get:onBeginFrame -dart:ui,Window,set:onBeginFrame -dart:ui,Window,get:onDrawFrame -dart:ui,Window,set:onDrawFrame -dart:ui,Window,set:onPointerDataPacket -dart:ui,Window,scheduleFrame -dart:ui,Window,render -dart:ui,Window,get:semanticsEnabled -dart:ui,Window,get:_semanticsEnabled -dart:ui,Window,set:onSemanticsEnabledChanged -dart:ui,Window,set:onSemanticsAction -dart:ui,Window,sendPlatformMessage -dart:ui,Window,_sendPlatformMessage -dart:ui,Window,set:onPlatformMessage -dart:ui,Window,onBeginFrame -dart:ui,Window,onDrawFrame -dart:ui,RRect,RRect.fromLTRBXY -dart:ui,RRect,RRect.fromRectXY -dart:ui,RRect,RRect.fromLTRBAndCorners -dart:ui,RRect,RRect.fromRectAndCorners -dart:ui,RRect,get:_value -dart:ui,RRect,get:left -dart:ui,RRect,get:top -dart:ui,RRect,get:right -dart:ui,RRect,get:bottom -dart:ui,RRect,shift -dart:ui,RRect,get:outerRect -dart:ui,Radius,Radius.circular -dart:ui,Radius,Radius.elliptical -dart:ui,Radius,get:x -dart:ui,Radius,get:y -dart:ui,Radius,get:zero -dart:ui,Radius,== -dart:ui,Canvas,Canvas. -dart:ui,Canvas,_constructor -dart:ui,Canvas,save -dart:ui,Canvas,saveLayer -dart:ui,Canvas,_saveLayer -dart:ui,Canvas,restore -dart:ui,Canvas,translate -dart:ui,Canvas,transform -dart:ui,Canvas,_transform -dart:ui,Canvas,clipRRect -dart:ui,Canvas,_clipRRect -dart:ui,Canvas,clipPath -dart:ui,Canvas,drawRect -dart:ui,Canvas,_drawRect -dart:ui,Canvas,drawRRect -dart:ui,Canvas,_drawRRect -dart:ui,Canvas,drawDRRect -dart:ui,Canvas,_drawDRRect -dart:ui,Canvas,drawCircle -dart:ui,Canvas,_drawCircle -dart:ui,Canvas,drawArc -dart:ui,Canvas,_drawArc -dart:ui,Canvas,drawImageRect -dart:ui,Canvas,_drawImageRect -dart:ui,Canvas,drawParagraph -dart:ui,Canvas,drawShadow -dart:ui,Canvas,_drawShadow -dart:ui,Image,get:width -dart:ui,Image,get:height -dart:ui,Paint,get:_data -dart:ui,Paint,get:_kIsAntiAliasOffset -dart:ui,Paint,get:_kColorOffset -dart:ui,Paint,get:_kStyleOffset -dart:ui,Paint,get:_kStrokeWidthOffset -dart:ui,Paint,get:_kStrokeCapOffset -dart:ui,Paint,get:_kFilterQualityOffset -dart:ui,Paint,get:_objects -dart:ui,Paint,set:_objects -dart:ui,Paint,set:isAntiAlias -dart:ui,Paint,set:color -dart:ui,Paint,set:style -dart:ui,Paint,set:strokeWidth -dart:ui,Paint,set:strokeCap -dart:ui,Paint,set:maskFilter -dart:ui,Paint,set:filterQuality -dart:ui,Paint,Paint. -dart:ui,PaintingStyle,get:index -dart:ui,PictureRecorder,PictureRecorder. -dart:ui,PictureRecorder,_constructor -dart:ui,PictureRecorder,get:isRecording -dart:ui,PictureRecorder,endRecording -dart:ui,Locale,Locale. -dart:ui,Path,Path. -dart:ui,Path,_constructor -dart:ui,Path,addOval -dart:ui,Path,_addOval -dart:ui,Path,addRRect -dart:ui,Path,_addRRect -dart:ui,Path,shift -dart:ui,Path,_shift -dart:ui,SceneBuilder,SceneBuilder. -dart:ui,SceneBuilder,_constructor -dart:ui,SceneBuilder,pushTransform -dart:ui,SceneBuilder,_pushTransform -dart:ui,SceneBuilder,pushClipRect -dart:ui,SceneBuilder,_pushClipRect -dart:ui,SceneBuilder,pop -dart:ui,SceneBuilder,addPicture -dart:ui,SceneBuilder,_addPicture -dart:ui,SceneBuilder,build -dart:ui,ParagraphConstraints,ParagraphConstraints. -dart:ui,ParagraphConstraints,get:width -dart:ui,_Logger,_printString -dart:ui,::,get:_hashEnd -dart:ui,::,hashValues -dart:ui,::,_updateWindowMetrics -dart:ui,::,_updateLocale -dart:ui,::,_beginFrame -dart:ui,::,_drawFrame -dart:ui,::,lerpDouble -dart:ui,::,_print -dart:ui,::,_setupHooks -dart:ui,::,_scheduleMicrotask -dart:ui,::,_getPrintClosure -dart:ui,::,_getScheduleMicrotaskClosure -dart:ui,::,_getGetBaseURLClosure -dart:ui,::,_getMainClosure -dart:ui,::,get:_kFakeHostEndian -dart:ui,::,decodeImageFromList -dart:ui,::,_encodeTextStyle -dart:ui,::,_encodeParagraphStyle -dart:ui,::,get:window diff --git a/lib/snapshot/libraries.json b/lib/snapshot/libraries.json index 540785de60f7f..6267363cdeb65 100644 --- a/lib/snapshot/libraries.json +++ b/lib/snapshot/libraries.json @@ -82,9 +82,11 @@ "ffi": { "uri": "../../../third_party/dart/sdk/lib/ffi/ffi.dart", "patches": [ + "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart", + "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart", "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart", "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart", - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart" + "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart" ] }, "wasm": { diff --git a/lib/snapshot/libraries.yaml b/lib/snapshot/libraries.yaml index 49838b0d52788..a0dfd15a382d0 100644 --- a/lib/snapshot/libraries.yaml +++ b/lib/snapshot/libraries.yaml @@ -87,9 +87,11 @@ flutter: ffi: uri: "../../../third_party/dart/sdk/lib/ffi/ffi.dart" patches: + - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart" + - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_allocation_patch.dart" - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart" - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart" - - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_patch.dart" + - "../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart" wasm: uri: "../../../third_party/dart/sdk/lib/wasm/wasm.dart" diff --git a/lib/snapshot/pubspec.yaml b/lib/snapshot/pubspec.yaml index 54585c8caccc8..5e4d2c6a083be 100644 --- a/lib/snapshot/pubspec.yaml +++ b/lib/snapshot/pubspec.yaml @@ -3,3 +3,6 @@ # found in the LICENSE file. # This file is needed by Fuchsia's dart_library template. + +environment: + sdk: '>=2.11.0 <3.0.0' diff --git a/lib/spirv/README.md b/lib/spirv/README.md new file mode 100644 index 0000000000000..5f7cf05f2d557 --- /dev/null +++ b/lib/spirv/README.md @@ -0,0 +1,45 @@ +# SPIR-V Transpiler + +Note: This library is currently considered experimental until shader compilation is verified by engine unit tests, see the Testing section below for more details. + +A dart library for transpiling a subset of SPIR-V to the shader languages used by Flutter internally. + +- [SkSL](https://skia.org/docs/user/sksl/) +- [GLSL ES 100](https://www.khronos.org/files/opengles_shading_language.pdf) +- [GLSL ES 300](https://www.khronos.org/registry/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf) + +All exported symbols are documented in `lib/spirv.dart`. + +The supported subset of SPIR-V is specified in `lib/src/constants.dart`. + +If you're using GLSL to generate SPIR-V with `glslangValidator` or `shaderc`, +the code will need to adhere to the following rules. + +- There must be a single vec4 output at location 0. +- The output can only be written to from the main function. +- `gl_FragCoord` can only be read from the main function, and its z and w components + have no meaning. +- Control flow is prohibited aside from function calls and `return`. + `if`, `while`, `for`, `switch`, etc. +- No inputs from other shader stages. +- Only float, float-vector types, and square float-matrix types. +- Only square matrices are supported. +- Only built-in functions present in GLSL ES 100 are used. +- Debug symbols must be stripped, you can use the `spirv-opt` `--strip-debug` flag. + +These rules may become less strict in future versions. Confirmant SPIR-V should succesfully transpile from the current version onwards. In other words, a spir-v shader you use now that meets these rules should keep working, but the output of the transpiler may change for that shader. + +Support for textures, control flow, and structured types is planned, but not currently included. + +## Testing + +## Exception Tests + +These tests rely on the `.spvasm` (SPIR-V Assembly) and `.glsl` files contained under `test/exception_shaders` in this directory. They are compiled to binary SPIR-V using `spirv-asm`, from the SwiftShader dependency. They are tested by testing/dart/spirv_exception_test.dart as part of the normal suite of dart tests. The purpose of these tests is to exercise every explicit failure path for shader transpilation. Each `glsl` or `spvasm` file should include a comment describing the failure that it is testing. The given files should be valid apart from the single failure case they are testing. + +## Pixel Tests + +Pixel test are not yet checked in, and should run as part of unit-testing for each implementation of `dart:ui`. These tests aim to validate the correctness of transpilation to each target language. Each shader should render the color green #00FF00 for a correct transpilation, and any other color for failure. They will be a combination of `.spvasm` files and more-readable GLSL files that are compiled to SPIR-V via `glslang`, provided by the SwiftShader dependency. Information for pixel tests will be expanded in a follow-up PR. + +These tests will be able to be run alone by executing `./ui_unittests` in the build-output directory. + diff --git a/lib/spirv/lib/spirv.dart b/lib/spirv/lib/spirv.dart new file mode 100644 index 0000000000000..7034c284b862d --- /dev/null +++ b/lib/spirv/lib/spirv.dart @@ -0,0 +1,69 @@ +// 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. + +/// This library defines a transpiler for converting SPIR-V into SkSL or GLSL. +library spirv; + +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +// These parts only contain private members, all public +// members are in this file (spirv.dart) +part 'src/constants.dart'; +part 'src/transpiler.dart'; +part 'src/types.dart'; + +/// The language to transpile to. +enum TargetLanguage { + /// SkSL, for Skia. + sksl, + + /// GLSL ES 1.00, for WebGL 1. + glslES, + + /// GLSL ES 3.00, for WebGL 2. + glslES300, +} + +/// The result of a transpilation. +class TranspileResult { + /// Source code string in [language]. + final String src; + + /// The shader language in [src]. + final TargetLanguage language; + + /// The number of float uniforms used in this shader. + final int uniformFloatCount; + + TranspileResult._(this.src, this.uniformFloatCount, this.language); +} + +/// Thrown during transpilation due to malformed or unsupported SPIR-V. +class TranspileException implements Exception { + /// The SPIR-V operator last read, or zero if there was none. + final int op; + + /// Human readable message explaining the exception. + final String message; + + @override + String toString() => '$op: $message'; + + TranspileException._(this.op, this.message); +} + +/// Transpile the provided SPIR-V buffer into a string of the [target] lang. +/// Throws an instance of [TranspileException] for malformed or unsupported +/// SPIR-V. +TranspileResult transpile(ByteBuffer spirv, TargetLanguage target) { + final _Transpiler t = _Transpiler(spirv.asUint32List(), target); + t.transpile(); + return TranspileResult._( + t.src.toString(), + t.uniformFloatCount, + target, + ); +} diff --git a/lib/spirv/lib/src/constants.dart b/lib/spirv/lib/src/constants.dart new file mode 100644 index 0000000000000..0cae6c58c1a1a --- /dev/null +++ b/lib/spirv/lib/src/constants.dart @@ -0,0 +1,195 @@ +// 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. + +part of spirv; + +// This file contains a subset of SPIR-V constants defined at +// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html + +// Header constants +const int _magicNumber = 0x07230203; + +// Supported ExecutionModes +const int _originLowerLeft = 8; + +// Supported memory models +const int _addressingModelLogical = 0; +const int _memoryModelGLSL450 = 1; + +// Supported capabilities +const int _capabilityMatrix = 0; +const int _capabilityShader = 1; + +// Supported storage classes +const int _storageClassUniformConstant = 0; +const int _storageClassInput = 1; +const int _storageClassOutput = 3; +const int _storageClassFunction = 7; + +// Explicity supported decorations, others are ignored +const int _decorationBuiltIn = 11; +const int _decorationLocation = 30; + +// Explicitly supported builtin types +const int _builtinFragCoord = 15; + +// Supported instructions +const int _opExtInstImport = 11; +const int _opExtInst = 12; +const int _opMemoryModel = 14; +const int _opEntryPoint = 15; +const int _opExecutionMode = 16; +const int _opCapability = 17; +const int _opTypeVoid = 19; +const int _opTypeBool = 20; +const int _opTypeFloat = 22; +const int _opTypeVector = 23; +const int _opTypeMatrix = 24; +const int _opTypePointer = 32; +const int _opTypeFunction = 33; +const int _opConstant = 43; +const int _opConstantComposite = 44; +const int _opFunction = 54; +const int _opFunctionParameter = 55; +const int _opFunctionEnd = 56; +const int _opFunctionCall = 57; +const int _opVariable = 59; +const int _opLoad = 61; +const int _opStore = 62; +const int _opAccessChain = 65; +const int _opDecorate = 71; +const int _opVectorShuffle = 79; +const int _opCompositeConstruct = 80; +const int _opCompositeExtract = 81; +const int _opFNegate = 127; +const int _opFAdd = 129; +const int _opFSub = 131; +const int _opFMul = 133; +const int _opFDiv = 136; +const int _opFMod = 141; +const int _opVectorTimesScalar = 142; +const int _opMatrixTimesScalar = 143; +const int _opVectorTimesMatrix = 144; +const int _opMatrixTimesVector = 145; +const int _opMatrixTimesMatrix = 146; +const int _opDot = 148; +const int _opLabel = 248; +const int _opReturn = 253; +const int _opReturnValue = 254; + +// GLSL extension constants defined at +// https://www.khronos.org/registry/spir-v/specs/unified1/GLSL.std.450.html + +// Supported GLSL extension name +const String _glslStd450 = 'GLSL.std.450'; + +// Supported GLSL ops +const int _glslStd450Trunc = 3; +const int _glslStd450FAbs = 4; +const int _glslStd450FSign = 6; +const int _glslStd450Floor = 8; +const int _glslStd450Ceil = 9; +const int _glslStd450Fract = 10; +const int _glslStd450Radians = 11; +const int _glslStd450Degrees = 12; +const int _glslStd450Sin = 13; +const int _glslStd450Cos = 14; +const int _glslStd450Tan = 15; +const int _glslStd450Asin = 16; +const int _glslStd450Acos = 17; +const int _glslStd450Atan = 18; +const int _glslStd450Atan2 = 25; +const int _glslStd450Pow = 26; +const int _glslStd450Exp = 27; +const int _glslStd450Log = 28; +const int _glslStd450Exp2 = 29; +const int _glslStd450Log2 = 30; +const int _glslStd450Sqrt = 31; +const int _glslStd450InverseSqrt = 32; +const int _glslStd450FMin = 37; +const int _glslStd450FMax = 40; +const int _glslStd450FClamp = 43; +const int _glslStd450FMix = 46; +const int _glslStd450Step = 48; +const int _glslStd450SmoothStep = 49; +const int _glslStd450Length = 66; +const int _glslStd450Distance = 67; +const int _glslStd450Cross = 68; +const int _glslStd450Normalize = 69; +const int _glslStd450FaceForward = 70; +const int _glslStd450Reflect = 71; + +const Map _glslStd450OpNames = { + _glslStd450Trunc: 'trunc', + _glslStd450FAbs: 'abs', + _glslStd450FSign: 'sign', + _glslStd450Floor: 'floor', + _glslStd450Ceil: 'ceil', + _glslStd450Fract: 'fract', + _glslStd450Radians: 'radians', + _glslStd450Degrees: 'degrees', + _glslStd450Sin: 'sin', + _glslStd450Cos: 'cos', + _glslStd450Tan: 'tan', + _glslStd450Asin: 'asin', + _glslStd450Acos: 'acos', + _glslStd450Atan: 'atan', + _glslStd450Atan2: 'atan2', + _glslStd450Pow: 'pow', + _glslStd450Exp: 'exp', + _glslStd450Log: 'log', + _glslStd450Exp2: 'exp2', + _glslStd450Log2: 'log2', + _glslStd450Sqrt: 'sqrt', + _glslStd450InverseSqrt: 'inversesqrt', + _glslStd450FMin: 'min', + _glslStd450FMax: 'max', + _glslStd450FClamp: 'clamp', + _glslStd450FMix: 'mix', + _glslStd450Step: 'step', + _glslStd450SmoothStep: 'smoothstep', + _glslStd450Length: 'length', + _glslStd450Distance: 'distance', + _glslStd450Cross: 'cross', + _glslStd450Normalize: 'normalize', + _glslStd450FaceForward: 'faceforward', + _glslStd450Reflect: 'reflect', +}; + +const Map _glslStd450OpArgc = { + _glslStd450Trunc: 1, + _glslStd450FAbs: 1, + _glslStd450FSign: 1, + _glslStd450Floor: 1, + _glslStd450Ceil: 1, + _glslStd450Fract: 1, + _glslStd450Radians: 1, + _glslStd450Degrees: 1, + _glslStd450Sin: 1, + _glslStd450Cos: 1, + _glslStd450Tan: 1, + _glslStd450Asin: 1, + _glslStd450Acos: 1, + _glslStd450Atan: 1, + _glslStd450Atan2: 2, + _glslStd450Pow: 2, + _glslStd450Exp: 2, + _glslStd450Log: 1, + _glslStd450Exp2: 1, + _glslStd450Log2: 1, + _glslStd450Sqrt: 1, + _glslStd450InverseSqrt: 1, + _glslStd450FMin: 2, + _glslStd450FMax: 2, + _glslStd450FClamp: 3, + _glslStd450FMix: 3, + _glslStd450Step: 2, + _glslStd450SmoothStep: 3, + _glslStd450Length: 1, + _glslStd450Distance: 2, + _glslStd450Cross: 2, + _glslStd450Normalize: 1, + _glslStd450FaceForward: 3, + _glslStd450Reflect: 2, +}; diff --git a/lib/spirv/lib/src/transpiler.dart b/lib/spirv/lib/src/transpiler.dart new file mode 100644 index 0000000000000..2ad5d9a888098 --- /dev/null +++ b/lib/spirv/lib/src/transpiler.dart @@ -0,0 +1,839 @@ +// 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. + +part of spirv; + +/// The name of the fragment-coordinate parameter when generating SkSL. +const String _fragParamName = 'iFragCoord'; + +/// The name of a local variable in the main function of an SkSL shader. +/// It will be assigned in place of an output variable at location 0. +/// This will be returned at the end of the main function. +const String _colorVariableName = 'oColor'; + +/// The name of the color-output variable in GLSL ES 100. +const String _glslESColorName = 'gl_FragColor'; + +/// The name of the fragment-coordinate value in GLSL. +const String _glslFragCoord = 'gl_FragCoord'; + +const String _mainFunctionName = 'main'; + +/// State machine for transpiling SPIR-V to the target language. +/// +/// SPIR-V is specified as a sequence of 32-bit values called words. +/// The first words are the header, and the rest are a sequence of +/// instructions. Instructions begin with one word that includes +/// an opcode and the number of words contained by the instruction. +/// +/// This transpiler works by maintaining a read position, [position], +/// which is advanced by methods with names beginning in "read", "parse", +/// or "op". State is written to member variables as the read position +/// advances, this will become more complex with a larger supported +/// subset of SPIR-V, and with more optimized output. It is currently +/// designed only for simplicity and speed, as the resuling code +/// will be compiled and optimized before making it to the GPU. +/// +/// The main method for the class is [transpile]. +/// +/// The list of supported SPIR-V operands is specified by the switch +/// statement in [parseInstruction] and the accompanying constants +/// in `src/constants.dart`. +/// +/// The methods beginning with `op` correspond to a specific opcode in SPIR-V. +/// The accompanying documentation is at +/// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html +/// +/// For the spec of a specific instruction, navigate to the above url with +/// the capitalized name of the operator appended. For example, for +/// [opConstant] append `#OpConstant`, like the following: +/// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConstant +class _Transpiler { + _Transpiler(this.spirv, this.target) { + out = src; + } + + final Uint32List spirv; + final TargetLanguage target; + + /// The resulting source code of the target language is written to src. + final StringBuffer src = StringBuffer(); + + /// ID mapped to numerical types. + final Map types = {}; + + /// ID mapped to function types. + final Map functionTypes = {}; + + /// Function ID mapped to source-code definition. + final Map functionDefs = {}; + + /// ID mapped to location decorator. + /// See [opDecorate] for more information. + final Map locations = {}; + + /// ID mapped to ID. Used by [OpLoad]. + final Map alias = {}; + + /// The current word-index in the SPIR-V buffer. + int position = 0; + + /// The word-index of the next unread instruction. + int nextPosition = 0; + + /// The current op code being handled, or 0 if none. + int currentOp = 0; + + /// The ID of the GLSL.std.450 instruction set. + /// See [opExtInstImport] for more information. + int glslExtImport = 0; + + /// The ID of the shader's entry point. + /// See [opEntryPoint]. + int entryPoint = 0; + + /// The ID of a 32-bit float type. + /// See [opTypeFloat]. + int floatType = 0; + + /// The ID of the function that is currently being defined. + /// Set by [opFunction] and unset by [opFunctionEnd]. + int currentFunction = 0; + + /// The type of [currentFunction], or null. + _FunctionType? currentFunctionType; + + /// Count of parameters declared so far for the [currentFunction]. + int declaredParams = 0; + + /// The ID for the color output variable. + /// Set by [opVariable]. + int colorOutput = 0; + + /// The ID for the fragment coordinate builtin. + /// Set by [opDecorate]. + int fragCoord = 0; + + /// The number of floats used by uniforms. + int uniformFloatCount = 0; + + /// Current indentation to prepend to new lines. + String indent = ''; + + /// Points to the source of the [currentFunction], or to [src] as a fallback. + /// The source of [currentFunction] is stored in [functionDefs]. + late StringBuffer out; + + /// Scan through all the words and populate [out] with source code, + /// or throw an exception. Calls to [parseInstruction] will affect + /// the state of the transpiler, including [position] and [out]. + void transpile() { + parseHeader(); + writeHeader(); + while (position < spirv.length) { + final int lastPosition = position; + parseInstruction(); + // If position is the same or smaller, the while loop may repeat forever. + assert(position > lastPosition); + } + + src.writeln(); + for (final StringBuffer def in functionDefs.values) { + src.write(def.toString()); + } + } + + TranspileException failure(String why) => + TranspileException._(currentOp, why); + + void writeHeader() { + switch (target) { + case TargetLanguage.glslES: + src.writeln('#version 100\n'); + src.writeln('precision mediump float;\n'); + break; + case TargetLanguage.glslES300: + src.writeln('#version 300 es\n'); + src.writeln('precision mediump float;\n'); + src.writeln('layout ( location = 0 ) out vec4 $_colorVariableName;\n'); + break; + default: + break; + } + } + + String resolveName(int id) { + if (alias.containsKey(id)) { + return resolveName(alias[id]!); + } + if (id == colorOutput) { + if (target == TargetLanguage.glslES) { + return _glslESColorName; + } else { + return _colorVariableName; + } + } else if (id == entryPoint) { + return _mainFunctionName; + } else if (id == fragCoord && target != TargetLanguage.sksl) { + return _glslFragCoord; + } + return 'i$id'; + } + + String resolveType(int type) { + final _Type? t = types[type]; + if (t == null) { + throw failure('The id "$type" has not been asgined a type'); + } + return _typeName(t, target); + } + + int readWord() { + if (nextPosition != 0 && position > nextPosition) { + throw failure('Read past the current instruction.'); + } + final int word = spirv[position]; + position++; + return word; + } + + void parseHeader() { + if (spirv[0] != _magicNumber) { + throw failure('Magic number not detected in the header'); + } + // Skip version, generator's magic word, bound, and reserved word. + position = 5; + } + + String readStringLiteral() { + final List literal = []; + while (position < nextPosition) { + final int word = readWord(); + for (int i = 0; i < 4; i++) { + final int octet = (word >> (i * 8)) & 0xFF; + if (octet == 0) { + return utf8.decode(literal); + } + literal.add(octet); + } + } + // Null terminating character not found. + throw failure('No null-terminating character found for string literal'); + } + + /// Read an instruction word, and handle the operation. + /// + /// SPIR-V instructions contain an op-code as well as a + /// word-size. This method parses both, calls the appropriate + /// operation-handler method, and then advances [position] + /// to the next instruction. + void parseInstruction() { + final int word = readWord(); + currentOp = word & 0xFFFF; + nextPosition = position + (word >> 16) - 1; + switch (currentOp) { + case _opExtInstImport: + opExtInstImport(); + break; + case _opExtInst: + opExtInst(); + break; + case _opMemoryModel: + opMemoryModel(); + break; + case _opEntryPoint: + opEntryPoint(); + break; + case _opExecutionMode: + opExecutionMode(); + break; + case _opCapability: + opCapability(); + break; + case _opTypeVoid: + opTypeVoid(); + break; + case _opTypeBool: + opTypeBool(); + break; + case _opTypeFloat: + opTypeFloat(); + break; + case _opTypeVector: + opTypeVector(); + break; + case _opTypeMatrix: + opTypeMatrix(); + break; + case _opTypePointer: + opTypePointer(); + break; + case _opTypeFunction: + opTypeFunction(); + break; + case _opConstant: + opConstant(); + break; + case _opConstantComposite: + opConstantComposite(); + break; + case _opFunction: + opFunction(); + break; + case _opFunctionParameter: + opFunctionParameter(); + break; + case _opFunctionEnd: + opFunctionEnd(); + break; + case _opFunctionCall: + opFunctionCall(); + break; + case _opVariable: + opVariable(); + break; + case _opLoad: + opLoad(); + break; + case _opStore: + opStore(); + break; + case _opAccessChain: + opAccessChain(); + break; + case _opDecorate: + opDecorate(); + break; + case _opVectorShuffle: + opVectorShuffle(); + break; + case _opCompositeConstruct: + opCompositeConstruct(); + break; + case _opCompositeExtract: + opCompositeExtract(); + break; + case _opFNegate: + opFNegate(); + break; + case _opFAdd: + parseOperatorInst('+'); + break; + case _opFSub: + parseOperatorInst('-'); + break; + case _opFMul: + parseOperatorInst('*'); + break; + case _opFDiv: + parseOperatorInst('/'); + break; + case _opFMod: + parseBuiltinFunction('mod'); + break; + case _opVectorTimesScalar: + case _opMatrixTimesScalar: + case _opVectorTimesMatrix: + case _opMatrixTimesVector: + case _opMatrixTimesMatrix: + parseOperatorInst('*'); + break; + case _opDot: + parseBuiltinFunction('dot'); + break; + case _opLabel: + opLabel(); + break; + case _opReturn: + opReturn(); + break; + case _opReturnValue: + opReturnValue(); + break; + default: + throw failure('Not a supported op.'); + } + position = nextPosition; + } + + void opExtInstImport() { + glslExtImport = readWord(); + final String ext = readStringLiteral(); + if (ext != _glslStd450) { + throw failure('only "$_glslStd450" is supported. Got "$ext".'); + } + } + + void opExtInst() { + final int type = readWord(); + final int id = readWord(); + final int set = readWord(); + if (set != glslExtImport) { + throw failure('only imported glsl instructions are supported'); + } + parseGLSLInst(id, type); + } + + void opMemoryModel() { + // addressing model + if (readWord() != _addressingModelLogical) { + throw failure('only the logical addressing model is supported'); + } + // memory model + if (readWord() != _memoryModelGLSL450) { + throw failure('only the GLSL450 memory model is supported'); + } + } + + void opEntryPoint() { + // skip execution model + position++; + entryPoint = readWord(); + } + + void opExecutionMode() { + // Skip entry point + position++; + final int executionMode = readWord(); + if (executionMode != _originLowerLeft) { + throw failure('only OriginLowerLeft is supported as an execution mode'); + } + } + + void opCapability() { + final int capability = readWord(); + switch (capability) { + case _capabilityMatrix: + case _capabilityShader: + return; + default: + throw failure('$capability is not a supported capability'); + } + } + + void opTypeVoid() { + types[readWord()] = _Type._void; + } + + void opTypeBool() { + types[readWord()] = _Type._bool; + } + + void opTypeFloat() { + final int id = readWord(); + types[id] = _Type.float; + floatType = id; + final int width = readWord(); + if (width != 32) { + throw failure('float width must be 32'); + } + } + + void opTypeVector() { + final int id = readWord(); + _Type t; + final int componentType = readWord(); + if (componentType != floatType) { + throw failure('only float vectors are supported'); + } + final int componentCount = readWord(); + switch (componentCount) { + case 2: + t = _Type.float2; + break; + case 3: + t = _Type.float3; + break; + case 4: + t = _Type.float4; + break; + default: + throw failure('$componentCount not a supported component count.'); + } + types[id] = t; + } + + void opTypeMatrix() { + final int id = readWord(); + _Type t; + final int columnType = readWord(); + final int columnCount = readWord(); + _Type expected = _Type.float2; + switch (columnCount) { + case 2: + t = _Type.float2x2; + break; + case 3: + t = _Type.float3x3; + expected = _Type.float3; + break; + case 4: + t = _Type.float4x4; + expected = _Type.float4; + break; + default: + throw failure('$columnCount is not a supported column count'); + } + if (types[columnType] != expected) { + throw failure('Only square matrix dimensions are supported'); + } + types[id] = t; + } + + void opTypePointer() { + final int id = readWord(); + // ignore storage class + position++; + final _Type? t = types[readWord()]; + if (t == null) { + throw failure('$t is not a registered type'); + } + types[id] = t; + } + + void opTypeFunction() { + final int id = readWord(); + final int returnType = readWord(); + final int paramCount = nextPosition - position; + final List params = List.filled(paramCount, 0); + for (int i = 0; i < paramCount; i++) { + params[i] = readWord(); + } + functionTypes[id] = _FunctionType(returnType, params); + } + + void opConstant() { + final int type = readWord(); + final String id = resolveName(readWord()); + final int value = readWord(); + String valueString = '$value'; + if (types[type] == _Type.float) { + final double v = Int32List.fromList([value]) + .buffer + .asByteData() + .getFloat32(0, Endian.little); + valueString = '$v'; + } + final String typeName = resolveType(type); + src.writeln('const $typeName $id = $valueString;'); + } + + void opConstantComposite() { + final String type = resolveType(readWord()); + final String id = resolveName(readWord()); + src.write('const $type $id = $type('); + final int count = nextPosition - position; + for (int i = 0; i < count; i++) { + src.write(resolveName(readWord())); + if (i < count - 1) { + src.write(', '); + } + } + src.writeln(');'); + } + + void opFunction() { + String returnType = resolveType(readWord()); + final int id = readWord(); + + if (target == TargetLanguage.sksl && id == entryPoint) { + returnType = 'half4'; + } + + // ignore function control + position++; + + final String name = resolveName(id); + final String opening = '$returnType $name('; + final StringBuffer def = StringBuffer(); + def.write(opening); + src.write(opening); + + if (target == TargetLanguage.sksl && id == entryPoint) { + const String fragParam = 'float2 $_fragParamName'; + def.write(fragParam); + src.write(fragParam); + } + + final int typeIndex = readWord(); + final _FunctionType? functionType = functionTypes[typeIndex]; + if (functionType == null) { + throw failure('$typeIndex is not a registered function type'); + } + + if (functionType.params.isEmpty) { + def.write(') '); + src.writeln(');'); + } + + currentFunction = id; + currentFunctionType = functionType; + declaredParams = 0; + out = def; + functionDefs[id] = def; + } + + void opFunctionParameter() { + if (declaredParams > 0) { + out.write(', '); + src.write(', '); + } + + final int type = readWord(); + final int id = readWord(); + final String decl = resolveType(type) + ' ' + resolveName(id); + out.write(decl); + src.write(decl); + declaredParams++; + + if (declaredParams == currentFunctionType?.params.length) { + out.write(') '); + src.writeln(');'); + } + } + + void opFunctionEnd() { + if (target == TargetLanguage.sksl && currentFunction == entryPoint) { + out.writeln('${indent}return $_colorVariableName;'); + } + out.writeln('}'); + out.writeln(); + // Remove trailing two space characters, if present. + indent = indent.substring(0, max(0, indent.length - 2)); + currentFunction = 0; + out = src; + currentFunctionType = null; + } + + void opFunctionCall() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String functionName = resolveName(readWord()); + final List args = + List.generate(nextPosition - position, (int i) { + return resolveName(readWord()); + }); + out.write('$indent$type $name = $functionName('); + for (int i = 0; i < args.length; i++) { + out.write(args[i]); + if (i < args.length - 1) { + out.write(', '); + } + } + out.writeln(');'); + } + + void opVariable() { + final int typeId = readWord(); + final String type = resolveType(typeId); + final int id = readWord(); + final String name = resolveName(id); + final int storageClass = readWord(); + + switch (storageClass) { + case _storageClassUniformConstant: + if (target == TargetLanguage.glslES300) { + final String location = locations[id].toString(); + src.write('layout ( location = $location ) '); + } + src.writeln('uniform $type $name;'); + final _Type? t = types[typeId]; + if (t == null) { + throw failure('$typeId is not a defined type'); + } + uniformFloatCount += _typeFloatCounts[t]!; + return; + case _storageClassInput: + return; + case _storageClassOutput: + if (locations[id] == 0) { + colorOutput = id; + } + return; + case _storageClassFunction: + out.writeln('$indent$type $name;'); + return; + default: + throw failure('$storageClass is an unsupported Storage Class'); + } + } + + void opLoad() { + // ignore type + position++; + final int id = readWord(); + final int pointer = readWord(); + alias[id] = pointer; + } + + void opStore() { + final String pointer = resolveName(readWord()); + final String object = resolveName(readWord()); + out.writeln('$indent$pointer = $object;'); + } + + void opAccessChain() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String base = resolveName(readWord()); + + // opAccessChain currently only supports indexed access. + // Once struct support is added, this will need to be updated. + // Currently, structs will be caught before this method is called, + // since using the instruction to define a struct type will throw + // an exception. + out.write('$indent$type $name = $base'); + final int count = nextPosition - position; + for (int i = 0; i < count; i++) { + final String index = resolveName(readWord()); + out.write('[$index]'); + } + out.writeln(';'); + } + + void opDecorate() { + final int target = readWord(); + final int decoration = readWord(); + switch (decoration) { + case _decorationBuiltIn: + if (readWord() == _builtinFragCoord) { + fragCoord = target; + } + return; + case _decorationLocation: + locations[target] = readWord(); + return; + default: + return; + } + } + + void opVectorShuffle() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String vector1Name = resolveName(readWord()); + // ignore second vector + position++; + + out.write('$indent$type $name = $type('); + + final int count = nextPosition - position; + for (int i = 0; i < count; i++) { + final int index = readWord(); + out.write('$vector1Name[$index]'); + if (i < count - 1) { + out.write(', '); + } + } + out.writeln(');'); + } + + void opCompositeConstruct() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + out.write('$indent$type $name = $type('); + final int count = nextPosition - position; + for (int i = 0; i < count; i++) { + out.write(resolveName(readWord())); + if (i < count - 1) { + out.write(', '); + } + } + out.writeln(');'); + } + + void opCompositeExtract() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String src = resolveName(readWord()); + out.write('$indent$type $name = $src'); + final int count = nextPosition - position; + for (int i = 0; i < count; i++) { + final int index = readWord(); + out.write('[$index]'); + } + out.writeln(';'); + } + + void opFNegate() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String operand = resolveName(readWord()); + out.writeln('$indent$type $name = -$operand;'); + } + + void opLabel() { + out.writeln('{'); + indent = indent + ' '; + if (target == TargetLanguage.sksl && currentFunction == entryPoint) { + final String ind = indent; + if (fragCoord > 0) { + final String fragName = resolveName(fragCoord); + out + ..write(ind) + ..writeln('float4 $fragName = float4($_fragParamName, 0, 0);'); + } + out + ..write(ind) + ..writeln('float4 $_colorVariableName;'); + } + } + + void opReturn() { + if (currentFunction == entryPoint) { + return; + } + out.writeln(indent + 'return;'); + } + + void opReturnValue() { + final String name = resolveName(readWord()); + out.writeln(indent + 'return $name;'); + } + + void parseOperatorInst(String op) { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String a = resolveName(readWord()); + final String b = resolveName(readWord()); + out.writeln('$indent$type $name = $a $op $b;'); + } + + void parseBuiltinFunction(String functionName) { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + out.write('$indent$type $name = $functionName('); + final int count = nextPosition - position; + for (int i = 0; i < count; i++) { + out.write(resolveName(readWord())); + if (i < count - 1) { + out.write(', '); + } + } + out.writeln(');'); + } + + void parseGLSLInst(int id, int type) { + final int inst = readWord(); + final String? opName = _glslStd450OpNames[inst]; + if (opName == null) { + throw failure('$id is not a supported GLSL instruction.'); + } + final int argc = _glslStd450OpArgc[inst]!; + parseGLSLOp(id, type, opName, argc); + } + + void parseGLSLOp(int id, int type, String name, int argCount) { + final String resultName = resolveName(id); + final String typeName = resolveType(type); + out.write('$indent$typeName $resultName = $name('); + for (int i = 0; i < argCount; i++) { + out.write(resolveName(readWord())); + if (i < argCount - 1) { + out.write(', '); + } + } + out.writeln(');'); + } +} diff --git a/lib/spirv/lib/src/types.dart b/lib/spirv/lib/src/types.dart new file mode 100644 index 0000000000000..3fd3dde5c2866 --- /dev/null +++ b/lib/spirv/lib/src/types.dart @@ -0,0 +1,73 @@ +// 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. + +part of spirv; + +enum _Type { + _void, + _bool, + _int, + float, + float2, + float3, + float4, + float2x2, + float3x3, + float4x4, +} + +class _FunctionType { + /// Result-id of the return type. + final int returnType; + + /// Type-id for each parameter. + final List params; + + _FunctionType(this.returnType, this.params); +} + +String _typeName(_Type t, TargetLanguage target) { + switch (target) { + case TargetLanguage.sksl: + return _skslTypeNames[t]!; + default: + return _glslTypeNames[t]!; + } +} + +const Map<_Type, String> _skslTypeNames = <_Type, String>{ + _Type._void: 'void', + _Type._bool: 'bool', + _Type._int: 'int', + _Type.float: 'float', + _Type.float2: 'float2', + _Type.float3: 'float3', + _Type.float4: 'float4', + _Type.float2x2: 'float2x2', + _Type.float3x3: 'float3x3', + _Type.float4x4: 'float4x4', +}; + +const Map<_Type, String> _glslTypeNames = <_Type, String>{ + _Type._void: 'void', + _Type._bool: 'bool', + _Type._int: 'int', + _Type.float: 'float', + _Type.float2: 'vec2', + _Type.float3: 'vec3 ', + _Type.float4: 'vec4', + _Type.float2x2: 'mat2', + _Type.float3x3: 'mat3', + _Type.float4x4: 'mat4', +}; + +const Map<_Type, int> _typeFloatCounts = <_Type, int>{ + _Type.float: 1, + _Type.float2: 2, + _Type.float3: 3, + _Type.float4: 4, + _Type.float2x2: 4, + _Type.float3x3: 9, + _Type.float4x4: 16, +}; diff --git a/lib/spirv/pubspec.yaml b/lib/spirv/pubspec.yaml new file mode 100644 index 0000000000000..a27ca2941ab0a --- /dev/null +++ b/lib/spirv/pubspec.yaml @@ -0,0 +1,9 @@ +# 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. + +name: spirv +publish_to: none + +environment: + sdk: '>=2.12.0 <3.0.0' diff --git a/lib/spirv/test/BUILD.gn b/lib/spirv/test/BUILD.gn new file mode 100644 index 0000000000000..ab3ba5b008486 --- /dev/null +++ b/lib/spirv/test/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +import("//flutter/testing/testing.gni") + +if (enable_unittests) { + executable("spirv_assembler") { + sources = [ "spirv_assembler.cc" ] + + configs += [ "//third_party/swiftshader_flutter:spvtools_public_config" ] + + deps = [ + "//third_party/swiftshader_flutter:spvtools", + "//third_party/swiftshader_flutter:spvtools_val", + ] + } +} diff --git a/lib/spirv/test/exception_shaders/BUILD.gn b/lib/spirv/test/exception_shaders/BUILD.gn new file mode 100644 index 0000000000000..17e371e5f3261 --- /dev/null +++ b/lib/spirv/test/exception_shaders/BUILD.gn @@ -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. +import("//build/compiled_action.gni") +import("//flutter/testing/testing.gni") + +if (enable_unittests) { + compiled_action_foreach("spirv_compile_exception_shaders") { + tool = "//flutter/lib/spirv/test:spirv_assembler" + + sources = [ + "unassigned_function_type.spvasm", + "unassigned_pointer_type.spvasm", + "unassigned_type.spvasm", + "unassigned_variable_type.spvasm", + "unsupported_addressing_model.spvasm", + "unsupported_capability.spvasm", + "unsupported_execution_mode.spvasm", + "unsupported_ext_inst_import.spvasm", + "unsupported_float_width.spvasm", + "unsupported_glsl_inst.spvasm", + "unsupported_matrix_column_count.spvasm", + "unsupported_matrix_non_square.spvasm", + "unsupported_memory_model.spvasm", + "unsupported_variable_storage_class.spvasm", + "unsupported_vector_component_count.spvasm", + "unsupported_vector_type.spvasm", + ] + + outputs = [ "$target_gen_dir/{{source_name_part}}.spv" ] + + args = [ + "{{source}}", + rebase_path(target_gen_dir, root_build_dir) + "/{{source_name_part}}.spv", + ] + } +} diff --git a/lib/spirv/test/exception_shaders/unassigned_function_type.spvasm b/lib/spirv/test/exception_shaders/unassigned_function_type.spvasm new file mode 100644 index 0000000000000..a757977bcd9d8 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unassigned_function_type.spvasm @@ -0,0 +1,31 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 13 +; Schema: 0 +; +; OpFunction is called with an unassigned type on line 27. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %13 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unassigned_pointer_type.spvasm b/lib/spirv/test/exception_shaders/unassigned_pointer_type.spvasm new file mode 100644 index 0000000000000..35b644e8e6342 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unassigned_pointer_type.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 14 +; Schema: 0 +; +; ID %13 is assigned a pointer type with an undefined underlying +; type %14. Line 20. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %13 = OpTypePointer Output %14 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unassigned_type.spvasm b/lib/spirv/test/exception_shaders/unassigned_type.spvasm new file mode 100644 index 0000000000000..c9bf556c135c0 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unassigned_type.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 13 +; Schema: 0 +; +; In the definition of %main, %void has been replaced with an undefined +; ID, representing an undefined type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %13 None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unassigned_variable_type.spvasm b/lib/spirv/test/exception_shaders/unassigned_variable_type.spvasm new file mode 100644 index 0000000000000..5905010d615a8 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unassigned_variable_type.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; Line 24 calls OpVariable to create a UniformConstant variable with an +; undefined type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %14 = OpVariable %13 UniformConstant + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_addressing_model.spvasm b/lib/spirv/test/exception_shaders/unsupported_addressing_model.spvasm new file mode 100644 index 0000000000000..42db7acd64fdb --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_addressing_model.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; OpMemoryModel is called with a non 'Logical' value, here +; Physical32 is used instead. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Physical32 GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_capability.spvasm b/lib/spirv/test/exception_shaders/unsupported_capability.spvasm new file mode 100644 index 0000000000000..3653af6987844 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_capability.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; A capability not in the set of {Matrix, Shader} is used. +; + OpCapability Shader + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_execution_mode.spvasm b/lib/spirv/test/exception_shaders/unsupported_execution_mode.spvasm new file mode 100644 index 0000000000000..00abb481689b8 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_execution_mode.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; OpExecutionMode is called with a non 'OriginLowerLeft' value, here +; PointMode is used instead. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main PointMode + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_ext_inst_import.spvasm b/lib/spirv/test/exception_shaders/unsupported_ext_inst_import.spvasm new file mode 100644 index 0000000000000..722ff319bdfeb --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_ext_inst_import.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 13 +; Schema: 0 +; +; ID %13 is assigned an unsupported external instruction set. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + %13 = OpExtInstImport "OpenCL.std" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_float_width.spvasm b/lib/spirv/test/exception_shaders/unsupported_float_width.spvasm new file mode 100644 index 0000000000000..4fabcfbf2e779 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_float_width.spvasm @@ -0,0 +1,31 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; A non-32 float width is used in OpTypeFloat. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 16 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_glsl_inst.spvasm b/lib/spirv/test/exception_shaders/unsupported_glsl_inst.spvasm new file mode 100644 index 0000000000000..23e3d1a0fdea6 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_glsl_inst.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 13 +; Schema: 0 +; +; A GLSL instruction not in the set defined in `constants.dart` is used +; on line 30. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpExtInst %v4float %1 Round %11 + OpStore %oColor %13 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_matrix_column_count.spvasm b/lib/spirv/test/exception_shaders/unsupported_matrix_column_count.spvasm new file mode 100644 index 0000000000000..b773de2b1cf66 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_matrix_column_count.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; A matrix type is defined with a component count not in the set of +; {2, 3, 4}, on line 24. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %13 = OpTypeMatrix %v4float 5 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_matrix_non_square.spvasm b/lib/spirv/test/exception_shaders/unsupported_matrix_non_square.spvasm new file mode 100644 index 0000000000000..d5ff3c432d432 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_matrix_non_square.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; A matrix type is defined with a non-square matrix size. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %13 = OpTypeMatrix %v4float 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_memory_model.spvasm b/lib/spirv/test/exception_shaders/unsupported_memory_model.spvasm new file mode 100644 index 0000000000000..ae6e51e97cbd7 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_memory_model.spvasm @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; OpMemoryModel is called with a non 'GLSL450' value, here +; 'Simple' is used instead. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_variable_storage_class.spvasm b/lib/spirv/test/exception_shaders/unsupported_variable_storage_class.spvasm new file mode 100644 index 0000000000000..fa8574767c903 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_variable_storage_class.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; OpVariable uses a Storage Class not in the set +; {UniformConstant, Input, Output, Function}, on line 25. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %13 = OpVariable %_ptr_Output_v4float Image + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_vector_component_count.spvasm b/lib/spirv/test/exception_shaders/unsupported_vector_component_count.spvasm new file mode 100644 index 0000000000000..317ec226ee716 --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_vector_component_count.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 12 +; Schema: 0 +; +; A vector type is defined with a component count not in the set of +; {2, 3, 4}, on line 24. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v8float = OpTypeVector %float 8 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/exception_shaders/unsupported_vector_type.spvasm b/lib/spirv/test/exception_shaders/unsupported_vector_type.spvasm new file mode 100644 index 0000000000000..8126cec49cd4b --- /dev/null +++ b/lib/spirv/test/exception_shaders/unsupported_vector_type.spvasm @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 14 +; Schema: 0 +; +; OpTypeVector is called with a base type that is not Float, +; here an undefined ID %13 is used, on line 24. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource ESSL 320 + OpName %main "main" + OpName %oColor "oColor" + OpDecorate %oColor RelaxedPrecision + OpDecorate %oColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %14 = OpTypeVector %13 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %oColor %11 + OpReturn + OpFunctionEnd diff --git a/lib/spirv/test/spirv_assembler.cc b/lib/spirv/test/spirv_assembler.cc new file mode 100644 index 0000000000000..e6a11c9c6b764 --- /dev/null +++ b/lib/spirv/test/spirv_assembler.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 +#include +#include +#include +#include + +#include "third_party/swiftshader/third_party/SPIRV-Tools/include/spirv-tools/libspirv.hpp" + +namespace fs = std::filesystem; + +int main(int argc, const char* argv[]) { + if (argc != 3) { + std::cerr << "Invalid argument count." << std::endl; + return -1; + } + + fs::path path(argv[1]); + if (!fs::exists(path)) { + std::cerr << "File does not exist." << std::endl; + return -1; + } + + std::fstream input; + input.open(argv[1]); + input.seekg(0, std::ios::end); + std::streampos size = input.tellg(); + input.seekg(0, std::ios::beg); + std::vector buf(size); + input.read(buf.data(), size); + input.close(); + + spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + std::vector assembled_spirv; + if (!tools.Assemble(buf.data(), size, &assembled_spirv)) { + std::cerr << "Failed to assemble " << argv[1] << std::endl; + return -1; + } + + std::fstream output; + output.open(argv[2], std::fstream::out | std::fstream::trunc); + if (!output.is_open()) { + output.close(); + std::cerr << "failed to open output file" << std::endl; + std::abort(); + } + + output.write(reinterpret_cast(assembled_spirv.data()), + sizeof(uint32_t) * assembled_spirv.size()); + output.close(); + return 0; +} diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 6ce9238b2738d..917cacdd56d81 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -42,6 +42,10 @@ source_set("ui") { "painting/image_encoding.h", "painting/image_filter.cc", "painting/image_filter.h", + "painting/image_generator.cc", + "painting/image_generator.h", + "painting/image_generator_registry.cc", + "painting/image_generator_registry.h", "painting/image_shader.cc", "painting/image_shader.h", "painting/immutable_buffer.cc", @@ -78,6 +82,8 @@ source_set("ui") { "semantics/semantics_update.h", "semantics/semantics_update_builder.cc", "semantics/semantics_update_builder.h", + "semantics/string_attribute.cc", + "semantics/string_attribute.h", "snapshot_delegate.h", "text/asset_manager_font_provider.cc", "text/asset_manager_font_provider.h", @@ -91,6 +97,12 @@ source_set("ui") { "text/text_box.h", "ui_dart_state.cc", "ui_dart_state.h", + "volatile_path_tracker.cc", + "volatile_path_tracker.h", + "window/key_data.cc", + "window/key_data.h", + "window/key_data_packet.cc", + "window/key_data_packet.h", "window/platform_configuration.cc", "window/platform_configuration.h", "window/platform_message.cc", @@ -133,23 +145,13 @@ source_set("ui") { if (flutter_enable_skshaper) { defines += [ "FLUTTER_ENABLE_SKSHAPER" ] } + if (flutter_always_use_skshaper) { + defines += [ "FLUTTER_ALWAYS_USE_SKSHAPER" ] + } if (is_win) { # Required for M_PI and others. defines += [ "_USE_MATH_DEFINES" ] } - - if (is_fuchsia && flutter_enable_legacy_fuchsia_embedder) { - sources += [ - "compositing/scene_host.cc", - "compositing/scene_host.h", - ] - - deps += [ - "$fuchsia_sdk_root/pkg:async-cpp", - "//flutter/shell/platform/fuchsia/dart-pkg/fuchsia", - "//flutter/shell/platform/fuchsia/dart-pkg/zircon", - ] - } } if (enable_unittests) { @@ -186,9 +188,15 @@ if (enable_unittests) { public_configs = [ "//flutter:export_dynamic_symbols" ] sources = [ + "compositing/scene_builder_unittests.cc", + "hooks_unittests.cc", "painting/image_dispose_unittests.cc", "painting/image_encoding_unittests.cc", + "painting/image_generator_registry_unittests.cc", + "painting/path_unittests.cc", + "painting/single_frame_codec_unittests.cc", "painting/vertices_unittests.cc", + "semantics/semantics_update_builder_unittests.cc", "window/platform_configuration_unittests.cc", "window/pointer_data_packet_converter_unittests.cc", ] diff --git a/lib/ui/analysis_options.yaml b/lib/ui/analysis_options.yaml new file mode 100644 index 0000000000000..df714af989148 --- /dev/null +++ b/lib/ui/analysis_options.yaml @@ -0,0 +1,6 @@ +include: ../../analysis_options.yaml + +analyzer: + exclude: + # fixtures/ depends on dart:ui and raises false positives. + - fixtures/** diff --git a/lib/ui/annotations.dart b/lib/ui/annotations.dart index b300af073e3be..a74adea962de2 100644 --- a/lib/ui/annotations.dart +++ b/lib/ui/annotations.dart @@ -4,8 +4,8 @@ // TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; // TODO(dnfield): Update this if/when we default this to on in the tool, @@ -40,8 +40,4 @@ part of dart.ui; /// } /// } /// ``` -const _KeepToString keepToString = _KeepToString(); - -class _KeepToString { - const _KeepToString(); -} +const pragma keepToString = pragma('flutter:keep-to-string'); diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a32a557e1f121..9b15db89cf448 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -2,214 +2,548 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// KEEP THIS SYNCHRONIZED WITH ../web_ui/lib/src/ui/channel_buffers.dart + +// @dart = 2.12 part of dart.ui; +/// Signature for [ChannelBuffers.drain]'s `callback` argument. +/// +/// The first argument is the data sent by the plugin. +/// +/// The second argument is a closure that, when called, will send messages +/// back to the plugin. +// TODO(ianh): deprecate this once the framework is migrated to [ChannelCallback]. +typedef DrainChannelCallback = Future Function(ByteData? data, PlatformMessageResponseCallback callback); + +/// Signature for [ChannelBuffers.setListener]'s `callback` argument. +/// +/// The first argument is the data sent by the plugin. +/// +/// The second argument is a closure that, when called, will send messages +/// back to the plugin. +/// +/// See also: +/// +/// * [PlatformMessageResponseCallback], the type used for replies. +typedef ChannelCallback = void Function(ByteData? data, PlatformMessageResponseCallback callback); + +/// The data and logic required to store and invoke a callback. +/// +/// This tracks (and applies) the [Zone]. +class _ChannelCallbackRecord { + _ChannelCallbackRecord(this._callback) : _zone = Zone.current; + final ChannelCallback _callback; + final Zone _zone; + + /// Call [callback] in [zone], using the given arguments. + void invoke(ByteData? dataArg, PlatformMessageResponseCallback callbackArg) { + _invoke2(_callback, _zone, dataArg, callbackArg); + } +} + /// A saved platform message for a channel with its callback. class _StoredMessage { - /// Default constructor, takes in a [ByteData] that represents the + /// Wraps the data and callback for a platform message into + /// a [_StoredMessage] instance. + /// + /// The first argument is a [ByteData] that represents the /// payload of the message and a [PlatformMessageResponseCallback] /// that represents the callback that will be called when the message /// is handled. - _StoredMessage(this._data, this._callback); + _StoredMessage(this.data, this._callback) : _zone = Zone.current; /// Representation of the message's payload. - final ByteData? _data; - ByteData? get data => _data; + final ByteData? data; - /// Callback to be called when the message is received. + /// Callback to be used when replying to the message. final PlatformMessageResponseCallback _callback; - PlatformMessageResponseCallback get callback => _callback; + + final Zone _zone; + + void invoke(ByteData? dataArg) { + _invoke1(_callback, _zone, dataArg); + } } -/// A fixed-size circular queue. -class _RingBuffer { - /// The underlying data for the RingBuffer. ListQueues dynamically resize, - /// [_RingBuffer]s do not. - final collection.ListQueue _queue; +/// The internal storage for a platform channel. +/// +/// This consists of a fixed-size circular queue of [_StoredMessage]s, +/// and the channel's callback, if any has been registered. +class _Channel { + _Channel([ this._capacity = ChannelBuffers.kDefaultBufferSize ]) + : _queue = collection.ListQueue<_StoredMessage>(_capacity); - _RingBuffer(this._capacity) - : _queue = collection.ListQueue(_capacity); + /// The underlying data for the buffered messages. + final collection.ListQueue<_StoredMessage> _queue; - /// Returns the number of items in the [_RingBuffer]. + /// The number of messages currently in the [_Channel]. + /// + /// This is equal to or less than the [capacity]. int get length => _queue.length; - /// The number of items that can be stored in the [_RingBuffer]. - int _capacity; - int get capacity => _capacity; - - /// Returns true if there are no items in the [_RingBuffer]. - bool get isEmpty => _queue.isEmpty; + /// Whether to dump messages to the console when a message is + /// discarded due to the channel overflowing. + /// + /// Has no effect in release builds. + bool debugEnableDiscardWarnings = true; - /// A callback that get's called when items are ejected from the [_RingBuffer] - /// by way of an overflow or a resizing. - Function(T)? _dropItemCallback; - set dropItemCallback(Function(T) callback) { - _dropItemCallback = callback; + /// The number of messages that _can_ be stored in the [_Channel]. + /// + /// When additional messages are stored, earlier ones are discarded, + /// in a first-in-first-out fashion. + int get capacity => _capacity; + int _capacity; + /// Set the [capacity] of the channel to the given size. + /// + /// If the new size is smaller than the [length], the oldest + /// messages are discarded until the capacity is reached. No + /// message is shown in case of overflow, regardless of the + /// value of [debugEnableDiscardWarnings]. + set capacity(int newSize) { + _capacity = newSize; + _dropOverflowMessages(newSize); } - /// Returns true on overflow. - bool push(T val) { + /// Whether a microtask is queued to call [_drainStep]. + /// + /// This is used to queue messages received while draining, rather + /// than sending them out of order. This generally cannot happen in + /// production but is possible in test scenarios. + /// + /// This is also necessary to avoid situations where multiple drains are + /// invoked simultaneously. For example, if a listener is set + /// (queuing a drain), then unset, then set again (which would queue + /// a drain again), all in one stack frame (not allowing the drain + /// itself an opportunity to check if a listener is set). + bool _draining = false; + + /// Adds a message to the channel. + /// + /// If the channel overflows, earlier messages are discarded, in a + /// first-in-first-out fashion. See [capacity]. If + /// [debugEnableDiscardWarnings] is true, this method returns true + /// on overflow. It is the responsibility of the caller to show the + /// warning message. + bool push(_StoredMessage message) { + if (!_draining && _channelCallbackRecord != null) { + assert(_queue.isEmpty); + _channelCallbackRecord!.invoke(message.data, message.invoke); + return false; + } if (_capacity <= 0) { - return true; - } else { - final int overflowCount = _dropOverflowItems(_capacity - 1); - _queue.addLast(val); - return overflowCount > 0; + return debugEnableDiscardWarnings; } + final bool result = _dropOverflowMessages(_capacity - 1); + _queue.addLast(message); + return result; } - /// Returns null when empty. - T? pop() { - return _queue.isEmpty ? null : _queue.removeFirst(); - } + /// Returns the first message in the channel and removes it. + /// + /// Throws when empty. + _StoredMessage pop() => _queue.removeFirst(); - /// Removes items until then length reaches [lengthLimit] and returns - /// the number of items removed. - int _dropOverflowItems(int lengthLimit) { - int result = 0; + /// Removes messages until [length] reaches `lengthLimit`. + /// + /// The callback of each removed message is invoked with null + /// as its argument. + /// + /// If any messages are removed, and [debugEnableDiscardWarnings] is + /// true, then returns true. The caller is responsible for showing + /// the warning message in that case. + bool _dropOverflowMessages(int lengthLimit) { + bool result = false; while (_queue.length > lengthLimit) { - final T item = _queue.removeFirst(); - _dropItemCallback?.call(item); - result += 1; + final _StoredMessage message = _queue.removeFirst(); + message.invoke(null); // send empty reply to the plugin side + result = true; } return result; } - /// Returns the number of discarded items resulting from resize. - int resize(int newSize) { - _capacity = newSize; - return _dropOverflowItems(newSize); + _ChannelCallbackRecord? _channelCallbackRecord; + + /// Sets the listener for this channel. + /// + /// When there is a listener, messages are sent immediately. + /// + /// If any messages were queued before the listener is added, + /// they are drained asynchronously after this method returns. + /// (See [_drain].) + /// + /// Only one listener may be set at a time. Setting a + /// new listener clears the previous one. + /// + /// Callbacks are invoked in their own stack frame and + /// use the zone that was current when the callback was + /// registered. + void setListener(ChannelCallback callback) { + final bool needDrain = _channelCallbackRecord == null; + _channelCallbackRecord = _ChannelCallbackRecord(callback); + if (needDrain && !_draining) + _drain(); } -} -/// Signature for [ChannelBuffers.drain]. -typedef DrainChannelCallback = Future Function(ByteData?, PlatformMessageResponseCallback); + /// Clears the listener for this channel. + /// + /// When there is no listener, messages are queued, up to [capacity], + /// and then discarded in a first-in-first-out fashion. + void clearListener() { + _channelCallbackRecord = null; + } + + /// Drains all the messages in the channel (invoking the currently + /// registered listener for each one). + /// + /// Each message is handled in its own microtask. No messages can + /// be queued by plugins while the queue is being drained, but any + /// microtasks queued by the handler itself will be processed before + /// the next message is handled. + /// + /// The draining stops if the listener is removed. + /// + /// See also: + /// + /// * [setListener], which is used to register the callback. + /// * [clearListener], which removes it. + void _drain() { + assert(!_draining); + _draining = true; + scheduleMicrotask(_drainStep); + } -/// Storage of channel messages until the channels are completely routed, -/// i.e. when a message handler is attached to the channel on the framework side. + /// Drains a single message and then reinvokes itself asynchronously. + /// + /// See [_drain] for more details. + void _drainStep() { + assert(_draining); + if (_queue.isNotEmpty && _channelCallbackRecord != null) { + final _StoredMessage message = pop(); + _channelCallbackRecord!.invoke(message.data, message.invoke); + scheduleMicrotask(_drainStep); + } else { + _draining = false; + } + } +} + +/// The buffering and dispatch mechanism for messages sent by plugins +/// on the engine side to their corresponding plugin code on the +/// framework side. +/// +/// Messages for a channel are stored until a listener is provided for that channel, +/// using [setListener]. Only one listener may be configured per channel. +/// +/// Typically these buffers are drained once a callback is set up on +/// the [BinaryMessenger] in the Flutter framework. (See [setListener].) /// -/// Each channel has a finite buffer capacity and in a FIFO manner messages will -/// be deleted if the capacity is exceeded. The intention is that these buffers -/// will be drained once a callback is setup on the BinaryMessenger in the -/// Flutter framework. +/// ## Buffer capacity and overflow /// -/// Clients of Flutter shouldn't need to allocate their own ChannelBuffers -/// and should only access this package's [channelBuffers] if they are writing -/// their own custom [BinaryMessenger]. +/// Each channel has a finite buffer capacity and messages will +/// be deleted in a first-in-first-out (FIFO) manner if the capacity is exceeded. +/// +/// By default buffers store one message per channel, and when a +/// message overflows, in debug mode, a message is printed to the +/// console. The message looks like the following: +/// +/// ``` +/// A message on the com.example channel was discarded before it could be +/// handled. +/// This happens when a plugin sends messages to the framework side before the +/// framework has had an opportunity to register a listener. See the +/// ChannelBuffers API documentation for details on how to configure the channel +/// to expect more messages, or to expect messages to get discarded: +/// https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html +/// ``` +/// +/// There are tradeoffs associated with any size. The correct size +/// should be chosen for the semantics of the channel. To change the +/// size a plugin can send a message using the control channel, +/// as described below. +/// +/// Size 0 is appropriate for channels where channels sent before +/// the engine and framework are ready should be ignored. For +/// example, a plugin that notifies the framework any time a +/// radiation sensor detects an ionization event might set its size +/// to zero since past ionization events are typically not +/// interesting, only instantaneous readings are worth tracking. +/// +/// Size 1 is appropriate for level-triggered plugins. For example, +/// a plugin that notifies the framework of the current value of a +/// pressure sensor might leave its size at one (the default), while +/// sending messages continually; once the framework side of the plugin +/// registers with the channel, it will immediately receive the most +/// up to date value and earlier messages will have been discarded. +/// +/// Sizes greater than one are appropriate for plugins where every +/// message is important. For example, a plugin that itself +/// registers with another system that has been buffering events, +/// and immediately forwards all the previously-buffered events, +/// would likely wish to avoid having any messages dropped on the +/// floor. In such situations, it is important to select a size that +/// will avoid overflows. It is also important to consider the +/// potential for the framework side to never fully initialize (e.g. if +/// the user starts the application, but terminates it soon +/// afterwards, leaving time for the platform side of a plugin to +/// run but not the framework side). +/// +/// ## The control channel +/// +/// A plugin can configure its channel's buffers by sending messages to the +/// control channel, `dev.flutter/channel-buffers` (see [kControlChannelName]). +/// +/// There are two messages that can be sent to this control channel, to adjust +/// the buffer size and to disable the overflow warnings. See [handleMessage] +/// for details on these messages. class ChannelBuffers { - /// By default we store one message per channel. There are tradeoffs associated - /// with any size. The correct size should be chosen for the semantics of your - /// channel. + /// Create a buffer pool for platform messages. /// - /// Size 0 implies you want to ignore any message that gets sent before the engine - /// is ready (keeping in mind there is no way to know when the engine is ready). - /// - /// Size 1 implies that you only care about the most recent value. - /// - /// Size >1 means you want to process every single message and want to chose a - /// buffer size that will avoid any overflows. + /// It is generally not necessary to create an instance of this class; + /// the global [channelBuffers] instance is the one used by the engine. + ChannelBuffers(); + + /// The number of messages that channel buffers will store by default. static const int kDefaultBufferSize = 1; + /// The name of the channel that plugins can use to communicate with the + /// channel buffers system. + /// + /// These messages are handled by [handleMessage]. static const String kControlChannelName = 'dev.flutter/channel-buffers'; - /// A mapping between a channel name and its associated [_RingBuffer]. - final Map?> _messages = - ?>{}; + /// A mapping between a channel name and its associated [_Channel]. + final Map _channels = {}; - _RingBuffer<_StoredMessage> _makeRingBuffer(int size) { - final _RingBuffer<_StoredMessage> result = _RingBuffer<_StoredMessage>(size); - result.dropItemCallback = _onDropItem; - return result; + /// Adds a message (`data`) to the named channel buffer (`name`). + /// + /// The `callback` argument is a closure that, when called, will send messages + /// back to the plugin. + /// + /// If a message overflows the channel, and the channel has not been + /// configured to expect overflow, then, in debug mode, a message + /// will be printed to the console warning about the overflow. + void push(String name, ByteData? data, PlatformMessageResponseCallback callback) { + final _Channel channel = _channels.putIfAbsent(name, () => _Channel()); + if (channel.push(_StoredMessage(data, callback))) { + _printDebug( + 'A message on the $name channel was discarded before it could be handled.\n' + 'This happens when a plugin sends messages to the framework side before the ' + 'framework has had an opportunity to register a listener. See the ChannelBuffers ' + 'API documentation for details on how to configure the channel to expect more ' + 'messages, or to expect messages to get discarded:\n' + ' https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html' + ); + } } - void _onDropItem(_StoredMessage message) { - message.callback(null); + /// Sets the listener for the specified channel. + /// + /// When there is a listener, messages are sent immediately. + /// + /// Each channel may have up to one listener set at a time. Setting + /// a new listener on a channel with an existing listener clears the + /// previous one. + /// + /// Callbacks are invoked in their own stack frame and + /// use the zone that was current when the callback was + /// registered. + /// + /// ## Draining + /// + /// If any messages were queued before the listener is added, + /// they are drained asynchronously after this method returns. + /// + /// Each message is handled in its own microtask. No messages can + /// be queued by plugins while the queue is being drained, but any + /// microtasks queued by the handler itself will be processed before + /// the next message is handled. + /// + /// The draining stops if the listener is removed. + void setListener(String name, ChannelCallback callback) { + final _Channel channel = _channels.putIfAbsent(name, () => _Channel()); + channel.setListener(callback); } - /// Returns true on overflow. - bool push(String channel, ByteData? data, PlatformMessageResponseCallback callback) { - _RingBuffer<_StoredMessage>? queue = _messages[channel]; - if (queue == null) { - queue = _makeRingBuffer(kDefaultBufferSize); - _messages[channel] = queue; - } - final bool didOverflow = queue.push(_StoredMessage(data, callback)); - if (didOverflow) { - // TODO(gaaclarke): Update this message to include instructions on how to resize - // the buffer once that is available to users and print in all engine builds - // after we verify that dropping messages isn't part of normal execution. - _printDebug('Overflow on channel: $channel. ' - 'Messages on this channel are being discarded in FIFO fashion. ' - 'The engine may not be running or you need to adjust ' - 'the buffer size of the channel.'); - } - return didOverflow; + /// Clears the listener for the specified channel. + /// + /// When there is no listener, messages on that channel are queued, + /// up to [kDefaultBufferSize] (or the size configured via the + /// control channel), and then discarded in a first-in-first-out + /// fashion. + void clearListener(String name) { + final _Channel? channel = _channels[name]; + if (channel != null) + channel.clearListener(); } - /// Returns null on underflow. - _StoredMessage? _pop(String channel) { - final _RingBuffer<_StoredMessage>? queue = _messages[channel]; - final _StoredMessage? result = queue?.pop(); - return result; + /// Remove and process all stored messages for a given channel. + /// + /// This should be called once a channel is prepared to handle messages + /// (i.e. when a message handler is set up in the framework). + /// + /// The messages are processed by calling the given `callback`. Each message + /// is processed in its own microtask. + // TODO(ianh): deprecate once framework uses [setListener]. + Future drain(String name, DrainChannelCallback callback) async { + final _Channel? channel = _channels[name]; + while (channel != null && !channel._queue.isEmpty) { + final _StoredMessage message = channel.pop(); + await callback(message.data, message.invoke); + } } - bool _isEmpty(String channel) { - final _RingBuffer<_StoredMessage>? queue = _messages[channel]; - return queue == null || queue.isEmpty; + /// Handle a control message. + /// + /// This is intended to be called by the platform messages dispatcher, forwarding + /// messages from plugins to the [kControlChannelName] channel. + /// + /// Messages use the [StandardMethodCodec] format. There are two methods + /// supported: `resize` and `overflow`. The `resize` method changes the size + /// of the buffer, and the `overflow` method controls whether overflow is + /// expected or not. + /// + /// ## `resize` + /// + /// The `resize` method takes as its argument a list with two values, first + /// the channel name (a UTF-8 string less than 254 bytes long), and second the + /// allowed size of the channel buffer (an integer between 0 and 2147483647). + /// + /// Upon receiving the message, the channel's buffer is resized. If necessary, + /// messages are silently discarded to ensure the buffer is no bigger than + /// specified. + /// + /// For historical reasons, this message can also be sent using a bespoke + /// format consisting of a UTF-8-encoded string with three parts separated + /// from each other by U+000D CARRIAGE RETURN (CR) characters, the three parts + /// being the string `resize`, the string giving the channel name, and then + /// the string giving the decimal serialization of the new channel buffer + /// size. For example: `resize\rchannel\r1` + /// + /// ## `overflow` + /// + /// The `overflow` method takes as its argument a list with two values, first + /// the channel name (a UTF-8 string less than 254 bytes long), and second a + /// boolean which is true if overflow is expected and false if it is not. + /// + /// This sets a flag on the channel in debug mode. In release mode the message + /// is silently ignored. The flag indicates whether overflow is expected on this + /// channel. When the flag is set, messages are discarded silently. When the + /// flag is cleared (the default), any overflow on the channel causes a message + /// to be printed to the console, warning that a message was lost. + void handleMessage(ByteData data) { + // We hard-code the deserialization here because the StandardMethodCodec class + // is part of the framework, not dart:ui. + final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + if (bytes[0] == 0x07) { // 7 = value code for string + final int methodNameLength = bytes[1]; + if (methodNameLength >= 254) // lengths greater than 253 have more elaborate encoding + throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); + int index = 2; // where we are in reading the bytes + final String methodName = utf8.decode(bytes.sublist(index, index + methodNameLength)); + index += methodNameLength; + switch (methodName) { + case 'resize': + if (bytes[index] != 0x0C) // 12 = value code for list + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)'); + index += 1; + if (bytes[index] < 0x02) // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)'); + index += 1; + if (bytes[index] != 0x07) // 7 = value code for string + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (first argument must be a string)'); + index += 1; + final int channelNameLength = bytes[index]; + if (channelNameLength >= 254) // lengths greater than 253 have more elaborate encoding + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (channel name must be less than 254 characters long)'); + index += 1; + final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); + index += channelNameLength; + if (bytes[index] != 0x03) // 3 = value code for uint32 + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)'); + index += 1; + resize(channelName, data.getUint32(index, Endian.host)); + break; + case 'overflow': + if (bytes[index] != 0x0C) // 12 = value code for list + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)'); + index += 1; + if (bytes[index] < 0x02) // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)'); + index += 1; + if (bytes[index] != 0x07) // 7 = value code for string + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (first argument must be a string)'); + index += 1; + final int channelNameLength = bytes[index]; + if (channelNameLength >= 254) // lengths greater than 253 have more elaborate encoding + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (channel name must be less than 254 characters long)'); + index += 1; + final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); + index += channelNameLength; + if (bytes[index] != 0x01 && bytes[index] != 0x02) // 1 = value code for true, 2 = value code for false + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (second argument must be a boolean)'); + allowOverflow(channelName, bytes[index] == 0x01); + break; + default: + throw Exception('Unrecognized method \'$methodName\' sent to $kControlChannelName'); + } + } else { + final List parts = utf8.decode(bytes).split('\r'); + if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') { + resize(parts[1], int.parse(parts[2])); + } else { + // If the message couldn't be decoded as UTF-8, a FormatException will + // have been thrown by utf8.decode() above. + throw Exception('Unrecognized message $parts sent to $kControlChannelName.'); + } + } } /// Changes the capacity of the queue associated with the given channel. /// /// This could result in the dropping of messages if newSize is less /// than the current length of the queue. - void _resize(String channel, int newSize) { - _RingBuffer<_StoredMessage>? queue = _messages[channel]; - if (queue == null) { - queue = _makeRingBuffer(newSize); - _messages[channel] = queue; + /// + /// This is expected to be called by platform-specific plugin code (indirectly + /// via the control channel), not by code on the framework side. See + /// [handleMessage]. + /// + /// Calling this from framework code is redundant since by the time framework + /// code can be running, it can just subscribe to the relevant channel and + /// there is therefore no need for any buffering. + void resize(String name, int newSize) { + _Channel? channel = _channels[name]; + if (channel == null) { + channel = _Channel(newSize); + _channels[name] = channel; } else { - final int numberOfDroppedMessages = queue.resize(newSize); - if (numberOfDroppedMessages > 0) { - _Logger._printString('Dropping messages on channel "$channel" as a result of shrinking the buffer size.'); - } + channel.capacity = newSize; } } - /// Remove and process all stored messages for a given channel. + /// Toggles whether the channel should show warning messages when discarding + /// messages due to overflow. /// - /// This should be called once a channel is prepared to handle messages - /// (i.e. when a message handler is setup in the framework). - Future drain(String channel, DrainChannelCallback callback) async { - while (!_isEmpty(channel)) { - final _StoredMessage message = _pop(channel)!; - await callback(message.data, message.callback); - } - } - - String _getString(ByteData data) { - final ByteBuffer buffer = data.buffer; - final Uint8List list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - return utf8.decode(list); - } - - /// Handle a control message. + /// This is expected to be called by platform-specific plugin code (indirectly + /// via the control channel), not by code on the framework side. See + /// [handleMessage]. /// - /// This is intended to be called by the platform messages dispatcher. + /// Calling this from framework code is redundant since by the time framework + /// code can be running, it can just subscribe to the relevant channel and + /// there is therefore no need for any messages to overflow. /// - /// Available messages: - /// - Name: resize - /// Arity: 2 - /// Format: `resize\r\r` - /// Description: Allows you to set the size of a channel's buffer. - void handleMessage(ByteData data) { - final List command = _getString(data).split('\r'); - if (command.length == /*arity=*/2 + 1 && command[0] == 'resize') { - _resize(command[1], int.parse(command[2])); - } else { - throw Exception('Unrecognized command $command sent to $kControlChannelName.'); - } + /// This method has no effect in release builds. + void allowOverflow(String name, bool allowed) { + assert(() { + _Channel? channel = _channels[name]; + if (channel == null && allowed) { + channel = _Channel(); + _channels[name] = channel; + } + channel?.debugEnableDiscardWarnings = !allowed; + return true; + }()); } } @@ -218,5 +552,6 @@ class ChannelBuffers { /// are stored here until the Framework is able to process them. /// /// See also: -/// * [BinaryMessenger] - The place where ChannelBuffers are typically read. +/// +/// * [BinaryMessenger], where [ChannelBuffers] are typically read. final ChannelBuffers channelBuffers = ChannelBuffers(); diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index f484f0e53c521..c0440c5a575cc 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// An opaque object representing a composited scene. /// /// To create a Scene object, use a [SceneBuilder]. /// -/// Scene objects can be displayed on the screen using the -/// [Window.render] method. +/// Scene objects can be displayed on the screen using the [FlutterView.render] +/// method. @pragma('vm:entry-point') -class Scene extends NativeFieldWrapperClass2 { +class Scene extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// @@ -31,13 +31,17 @@ class Scene extends NativeFieldWrapperClass2 { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } - return _futurize((_Callback callback) => _toImage(width, height, (_Image image) { - callback(Image._(image)); + return _futurize((_Callback callback) => _toImage(width, height, (_Image? image) { + if (image == null) { + callback(null); + } else { + callback(Image._(image)); + } }), ); } - String _toImage(int width, int height, _Callback<_Image> callback) native 'Scene_toImage'; + String? _toImage(int width, int height, _Callback<_Image?> callback) native 'Scene_toImage'; /// Releases the resources used by this scene. /// @@ -53,9 +57,19 @@ class Scene extends NativeFieldWrapperClass2 { // passed as `oldLayer` to `pushTransform`. This is achieved by having one // concrete subclass of this class per push method. abstract class _EngineLayerWrapper implements EngineLayer { - _EngineLayerWrapper._(this._nativeLayer); + _EngineLayerWrapper._(EngineLayer nativeLayer) : _nativeLayer = nativeLayer; + + EngineLayer? _nativeLayer; - EngineLayer _nativeLayer; + @override + void dispose() { + assert(_nativeLayer != null, 'Object disposed'); + _nativeLayer!.dispose(); + assert(() { + _nativeLayer = null; + return true; + }()); + } // Children of this layer. // @@ -186,12 +200,12 @@ class PhysicalShapeEngineLayer extends _EngineLayerWrapper { /// Builds a [Scene] containing the given visuals. /// -/// A [Scene] can then be rendered using [Window.render]. +/// A [Scene] can then be rendered using [FlutterView.render]. /// /// To draw graphical operations onto a [Scene], first create a /// [Picture] using a [PictureRecorder] and a [Canvas], and then add /// it to the scene using [addPicture]. -class SceneBuilder extends NativeFieldWrapperClass2 { +class SceneBuilder extends NativeFieldWrapperClass1 { /// Creates an empty [SceneBuilder] object. @pragma('vm:entry-point') SceneBuilder() { @@ -226,6 +240,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { if (layer == null) { return true; } + assert(layer._nativeLayer != null, 'Object disposed'); layer._debugCheckNotUsedAsOldLayer(); assert(_debugCheckUsedOnce(layer, 'oldLayer in $methodName')); layer._debugWasUsedAsOldLayer = true; @@ -279,20 +294,21 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@endtemplate} /// /// See [pop] for details about the operation stack. - TransformEngineLayer? pushTransform( + TransformEngineLayer pushTransform( Float64List matrix4, { TransformEngineLayer? oldLayer, }) { assert(_matrix4IsValid(matrix4)); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushTransform')); final EngineLayer engineLayer = EngineLayer._(); - _pushTransform(engineLayer, matrix4); + _pushTransform(engineLayer, matrix4, oldLayer?._nativeLayer); final TransformEngineLayer layer = TransformEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushTransform(EngineLayer layer, Float64List matrix4) native 'SceneBuilder_pushTransform'; + void _pushTransform(EngineLayer layer, Float64List matrix4, EngineLayer? oldLayer) + native 'SceneBuilder_pushTransform'; /// Pushes an offset operation onto the operation stack. /// @@ -303,20 +319,21 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - OffsetEngineLayer? pushOffset( + OffsetEngineLayer pushOffset( double dx, double dy, { OffsetEngineLayer? oldLayer, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset')); final EngineLayer engineLayer = EngineLayer._(); - _pushOffset(engineLayer, dx, dy); + _pushOffset(engineLayer, dx, dy, oldLayer?._nativeLayer); final OffsetEngineLayer layer = OffsetEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushOffset(EngineLayer layer, double dx, double dy) native 'SceneBuilder_pushOffset'; + void _pushOffset(EngineLayer layer, double dx, double dy, EngineLayer? oldLayer) + native 'SceneBuilder_pushOffset'; /// Pushes a rectangular clip operation onto the operation stack. /// @@ -328,23 +345,24 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). - ClipRectEngineLayer? pushClipRect( + ClipRectEngineLayer pushClipRect( Rect rect, { Clip clipBehavior = Clip.antiAlias, ClipRectEngineLayer? oldLayer, }) { - assert(clipBehavior != null); // ignore: unnecessary_null_comparison + assert(clipBehavior != null); assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRect')); final EngineLayer engineLayer = EngineLayer._(); - _pushClipRect(engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index); + _pushClipRect(engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index, + oldLayer?._nativeLayer); final ClipRectEngineLayer layer = ClipRectEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushClipRect(EngineLayer outEngineLayer, double left, double right, double top, double bottom, int clipBehavior) - native 'SceneBuilder_pushClipRect'; + void _pushClipRect(EngineLayer outEngineLayer, double left, double right, double top, + double bottom, int clipBehavior, EngineLayer? oldLayer) native 'SceneBuilder_pushClipRect'; /// Pushes a rounded-rectangular clip operation onto the operation stack. /// @@ -356,22 +374,22 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). - ClipRRectEngineLayer? pushClipRRect( + ClipRRectEngineLayer pushClipRRect( RRect rrect, { Clip clipBehavior = Clip.antiAlias, ClipRRectEngineLayer? oldLayer, }) { - assert(clipBehavior != null); // ignore: unnecessary_null_comparison + assert(clipBehavior != null); assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRRect')); final EngineLayer engineLayer = EngineLayer._(); - _pushClipRRect(engineLayer, rrect._value32, clipBehavior.index); + _pushClipRRect(engineLayer, rrect._value32, clipBehavior.index, oldLayer?._nativeLayer); final ClipRRectEngineLayer layer = ClipRRectEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushClipRRect(EngineLayer layer, Float32List rrect, int clipBehavior) + void _pushClipRRect(EngineLayer layer, Float32List rrect, int clipBehavior, EngineLayer? oldLayer) native 'SceneBuilder_pushClipRRect'; /// Pushes a path clip operation onto the operation stack. @@ -384,22 +402,23 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// See [pop] for details about the operation stack. See [Clip] for different clip modes. /// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]). - ClipPathEngineLayer? pushClipPath( + ClipPathEngineLayer pushClipPath( Path path, { Clip clipBehavior = Clip.antiAlias, ClipPathEngineLayer? oldLayer, }) { - assert(clipBehavior != null); // ignore: unnecessary_null_comparison + assert(clipBehavior != null); assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipPath')); final EngineLayer engineLayer = EngineLayer._(); - _pushClipPath(engineLayer, path, clipBehavior.index); + _pushClipPath(engineLayer, path, clipBehavior.index, oldLayer?._nativeLayer); final ClipPathEngineLayer layer = ClipPathEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushClipPath(EngineLayer layer, Path path, int clipBehavior) native 'SceneBuilder_pushClipPath'; + void _pushClipPath(EngineLayer layer, Path path, int clipBehavior, EngineLayer? oldLayer) + native 'SceneBuilder_pushClipPath'; /// Pushes an opacity operation onto the operation stack. /// @@ -413,20 +432,21 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - OpacityEngineLayer? pushOpacity( + OpacityEngineLayer pushOpacity( int alpha, { Offset? offset = Offset.zero, OpacityEngineLayer? oldLayer, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOpacity')); final EngineLayer engineLayer = EngineLayer._(); - _pushOpacity(engineLayer, alpha, offset!.dx, offset.dy); + _pushOpacity(engineLayer, alpha, offset!.dx, offset.dy, oldLayer?._nativeLayer); final OpacityEngineLayer layer = OpacityEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushOpacity(EngineLayer layer, int alpha, double dx, double dy) native 'SceneBuilder_pushOpacity'; + void _pushOpacity(EngineLayer layer, int alpha, double dx, double dy, EngineLayer? oldLayer) + native 'SceneBuilder_pushOpacity'; /// Pushes a color filter operation onto the operation stack. /// @@ -438,22 +458,23 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - ColorFilterEngineLayer? pushColorFilter( + ColorFilterEngineLayer pushColorFilter( ColorFilter filter, { ColorFilterEngineLayer? oldLayer, }) { - assert(filter != null); // ignore: unnecessary_null_comparison + assert(filter != null); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushColorFilter')); final _ColorFilter nativeFilter = filter._toNativeColorFilter()!; - assert(nativeFilter != null); // ignore: unnecessary_null_comparison + assert(nativeFilter != null); final EngineLayer engineLayer = EngineLayer._(); - _pushColorFilter(engineLayer, nativeFilter); + _pushColorFilter(engineLayer, nativeFilter, oldLayer?._nativeLayer); final ColorFilterEngineLayer layer = ColorFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushColorFilter(EngineLayer layer, _ColorFilter filter) native 'SceneBuilder_pushColorFilter'; + void _pushColorFilter(EngineLayer layer, _ColorFilter filter, EngineLayer? oldLayer) + native 'SceneBuilder_pushColorFilter'; /// Pushes an image filter operation onto the operation stack. /// @@ -465,46 +486,50 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - ImageFilterEngineLayer? pushImageFilter( + ImageFilterEngineLayer pushImageFilter( ImageFilter filter, { ImageFilterEngineLayer? oldLayer, }) { - assert(filter != null); // ignore: unnecessary_null_comparison + assert(filter != null); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushImageFilter')); final _ImageFilter nativeFilter = filter._toNativeImageFilter(); - assert(nativeFilter != null); // ignore: unnecessary_null_comparison + assert(nativeFilter != null); final EngineLayer engineLayer = EngineLayer._(); - _pushImageFilter(engineLayer, nativeFilter); + _pushImageFilter(engineLayer, nativeFilter, oldLayer?._nativeLayer); final ImageFilterEngineLayer layer = ImageFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushImageFilter(EngineLayer outEngineLayer, _ImageFilter filter) native 'SceneBuilder_pushImageFilter'; + void _pushImageFilter(EngineLayer outEngineLayer, _ImageFilter filter, EngineLayer? oldLayer) + native 'SceneBuilder_pushImageFilter'; /// Pushes a backdrop filter operation onto the operation stack. /// - /// The given filter is applied to the current contents of the scene prior to - /// rasterizing the given objects. + /// The given filter is applied to the current contents of the scene as far back as + /// the most recent save layer and rendered back to the scene using the indicated + /// [blendMode] prior to rasterizing the child layers. /// /// {@macro dart.ui.sceneBuilder.oldLayer} /// /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - BackdropFilterEngineLayer? pushBackdropFilter( + BackdropFilterEngineLayer pushBackdropFilter( ImageFilter filter, { + BlendMode blendMode = BlendMode.srcOver, BackdropFilterEngineLayer? oldLayer, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter')); final EngineLayer engineLayer = EngineLayer._(); - _pushBackdropFilter(engineLayer, filter._toNativeImageFilter()); + _pushBackdropFilter(engineLayer, filter._toNativeImageFilter(), blendMode.index, oldLayer?._nativeLayer); final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter) native 'SceneBuilder_pushBackdropFilter'; + void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, EngineLayer? oldLayer) + native 'SceneBuilder_pushBackdropFilter'; /// Pushes a shader mask operation onto the operation stack. /// @@ -516,11 +541,12 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - ShaderMaskEngineLayer? pushShaderMask( + ShaderMaskEngineLayer pushShaderMask( Shader shader, Rect maskRect, BlendMode blendMode, { ShaderMaskEngineLayer? oldLayer, + FilterQuality filterQuality = FilterQuality.low, }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushShaderMask')); final EngineLayer engineLayer = EngineLayer._(); @@ -532,20 +558,24 @@ class SceneBuilder extends NativeFieldWrapperClass2 { maskRect.top, maskRect.bottom, blendMode.index, + filterQuality.index, + oldLayer?._nativeLayer, ); final ShaderMaskEngineLayer layer = ShaderMaskEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - EngineLayer _pushShaderMask( + void _pushShaderMask( EngineLayer engineLayer, Shader shader, double maskRectLeft, double maskRectRight, double maskRectTop, double maskRectBottom, - int blendMode) native 'SceneBuilder_pushShaderMask'; + int blendMode, + int filterQualityIndex, + EngineLayer? oldLayer) native 'SceneBuilder_pushShaderMask'; /// Pushes a physical layer operation for an arbitrary shape onto the /// operation stack. @@ -563,8 +593,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. - // ignore: deprecated_member_use - PhysicalShapeEngineLayer? pushPhysicalShape({ + PhysicalShapeEngineLayer pushPhysicalShape({ required Path path, required double elevation, required Color color, @@ -574,21 +603,21 @@ class SceneBuilder extends NativeFieldWrapperClass2 { }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushPhysicalShape')); final EngineLayer engineLayer = EngineLayer._(); - _pushPhysicalShape( - engineLayer, - path, - elevation, - color.value, - shadowColor?.value ?? 0xFF000000, - clipBehavior.index, - ); + _pushPhysicalShape(engineLayer, path, elevation, color.value, shadowColor?.value ?? 0xFF000000, + clipBehavior.index, oldLayer?._nativeLayer); final PhysicalShapeEngineLayer layer = PhysicalShapeEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - EngineLayer _pushPhysicalShape(EngineLayer outEngineLayer, Path path, double elevation, int color, int shadowColor, - int clipBehavior) native 'SceneBuilder_pushPhysicalShape'; + void _pushPhysicalShape( + EngineLayer outEngineLayer, + Path path, + double elevation, + int color, + int shadowColor, + int clipBehavior, + EngineLayer? oldLayer) native 'SceneBuilder_pushPhysicalShape'; /// Ends the effect of the most recently pushed operation. /// @@ -620,6 +649,8 @@ class SceneBuilder extends NativeFieldWrapperClass2 { assert(() { final _EngineLayerWrapper layer = retainedLayer as _EngineLayerWrapper; + assert(layer._nativeLayer != null); + void recursivelyCheckChildrenUsedOnce(_EngineLayerWrapper parentLayer) { _debugCheckUsedOnce(parentLayer, 'retained layer'); parentLayer._debugCheckNotUsedAsOldLayer(); @@ -637,7 +668,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { }()); final _EngineLayerWrapper wrapper = retainedLayer as _EngineLayerWrapper; - _addRetained(wrapper._nativeLayer); + _addRetained(wrapper._nativeLayer!); } void _addRetained(EngineLayer retainedLayer) native 'SceneBuilder_addRetained'; @@ -655,13 +686,13 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. /// - /// The "UI thread" is the thread that includes all the execution of - /// the main Dart isolate (the isolate that can call - /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "raster - /// thread" is the thread (running on the CPU) that subsequently - /// processes the [Scene] provided by the Dart code to turn it into - /// GPU commands and send it to the GPU. + /// The "UI thread" is the thread that includes all the execution of the main + /// Dart isolate (the isolate that can call [FlutterView.render]). The UI + /// thread frame time is the total time spent executing the + /// [PlatformDispatcher.onBeginFrame] callback. The "raster thread" is the + /// thread (running on the CPU) that subsequently processes the [Scene] + /// provided by the Dart code to turn it into GPU commands and send it to the + /// GPU. /// /// See also the [PerformanceOverlayOption] enum in the rendering library. /// for more details. @@ -713,7 +744,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { bool freeze = false, FilterQuality filterQuality = FilterQuality.low, }) { - assert(offset != null, 'Offset argument was null'); // ignore: unnecessary_null_comparison + assert(offset != null, 'Offset argument was null'); _addTexture(offset.dx, offset.dy, width, height, textureId, freeze, filterQuality.index); } @@ -742,28 +773,13 @@ class SceneBuilder extends NativeFieldWrapperClass2 { double width = 0.0, double height = 0.0, }) { - assert(offset != null, 'Offset argument was null'); // ignore: unnecessary_null_comparison + assert(offset != null, 'Offset argument was null'); _addPlatformView(offset.dx, offset.dy, width, height, viewId); } void _addPlatformView(double dx, double dy, double width, double height, int viewId) native 'SceneBuilder_addPlatformView'; - /// (Fuchsia-only) Adds a scene rendered by another application to the scene - /// for this application. - void addChildScene({ - Offset offset = Offset.zero, - double width = 0.0, - double height = 0.0, - required SceneHost sceneHost, - bool hitTestable = true, - }) { - _addChildScene(offset.dx, offset.dy, width, height, sceneHost, hitTestable); - } - - void _addChildScene(double dx, double dy, double width, double height, SceneHost sceneHost, - bool hitTestable) native 'SceneBuilder_addChildScene'; - /// Sets a threshold after which additional debugging information should be recorded. /// /// Currently this interface is difficult to use by end-developers. If you're @@ -802,7 +818,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the - /// screen with [Window.render]. + /// screen with [FlutterView.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. @@ -814,51 +830,3 @@ class SceneBuilder extends NativeFieldWrapperClass2 { void _build(Scene outScene) native 'SceneBuilder_build'; } - -/// (Fuchsia-only) Hosts content provided by another application. -class SceneHost extends NativeFieldWrapperClass2 { - /// Creates a host for a child scene's content. - /// - /// The ViewHolder token is bound to a ViewHolder scene graph node which acts - /// as a container for the child's content. The creator of the SceneHost is - /// responsible for sending the corresponding ViewToken to the child. - /// - /// The ViewHolder token is a dart:zircon Handle, but that type isn't - /// available here. This is called by ChildViewConnection in - /// //topaz/public/dart/fuchsia_scenic_flutter/. - /// - /// The SceneHost takes ownership of the provided ViewHolder token. - SceneHost( - dynamic viewHolderToken, - void Function()? viewConnectedCallback, - void Function()? viewDisconnectedCallback, - void Function(bool)? viewStateChangedCallback, - ) { - _constructor( - viewHolderToken, viewConnectedCallback, viewDisconnectedCallback, viewStateChangedCallback); - } - - void _constructor( - dynamic viewHolderToken, - void Function()? viewConnectedCallback, - void Function()? viewDisconnectedCallback, - void Function(bool)? viewStateChangedCallbac, - ) native 'SceneHost_constructor'; - - /// Releases the resources associated with the SceneHost. - /// - /// After calling this function, the SceneHost cannot be used further. - void dispose() native 'SceneHost_dispose'; - - /// Set properties on the linked scene. These properties include its bounds, - /// as well as whether it can be the target of focus events or not. - void setProperties( - double width, - double height, - double insetTop, - double insetRight, - double insetBottom, - double insetLeft, - bool focusable, - ) native 'SceneHost_setProperties'; -} diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 96eac5885d083..a6448970f662d 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -42,9 +42,10 @@ Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { + // Currently only supports a single window. auto viewport_metrics = UIDartState::Current() ->platform_configuration() - ->window() + ->get_window(0) ->viewport_metrics(); layer_tree_ = std::make_unique( @@ -61,6 +62,7 @@ Scene::Scene(std::shared_ptr rootLayer, Scene::~Scene() {} void Scene::dispose() { + layer_tree_.reset(); ClearDartWrapper(); } diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index 1c0fa9bd5597f..e7c2b3ea151cc 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -10,6 +10,7 @@ #include "flutter/flow/layers/clip_rrect_layer.h" #include "flutter/flow/layers/color_filter_layer.h" #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/display_list_layer.h" #include "flutter/flow/layers/image_filter_layer.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/layers/layer_tree.h" @@ -30,10 +31,6 @@ #include "third_party/tonic/dart_binding_macros.h" #include "third_party/tonic/dart_library_natives.h" -#if defined(LEGACY_FUCHSIA_EMBEDDER) -#include "flutter/flow/layers/child_scene_layer.h" // nogncheck -#endif - namespace flutter { static void SceneBuilder_constructor(Dart_NativeArguments args) { @@ -67,18 +64,11 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, SceneBuilder); V(SceneBuilder, build) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) -#if defined(LEGACY_FUCHSIA_EMBEDDER) -DART_NATIVE_CALLBACK(SceneBuilder, addChildScene) -#endif void SceneBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) { - natives->Register({ - {"SceneBuilder_constructor", SceneBuilder_constructor, 1, true}, - FOR_EACH_BINDING(DART_REGISTER_NATIVE) -#if defined(LEGACY_FUCHSIA_EMBEDDER) - DART_REGISTER_NATIVE(SceneBuilder, addChildScene) -#endif - }); + natives->Register( + {{"SceneBuilder_constructor", SceneBuilder_constructor, 1, true}, + FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); } SceneBuilder::SceneBuilder() { @@ -90,20 +80,32 @@ SceneBuilder::SceneBuilder() { SceneBuilder::~SceneBuilder() = default; void SceneBuilder::pushTransform(Dart_Handle layer_handle, - tonic::Float64List& matrix4) { + tonic::Float64List& matrix4, + fml::RefPtr oldLayer) { SkMatrix sk_matrix = ToSkMatrix(matrix4); auto layer = std::make_shared(sk_matrix); PushLayer(layer); // matrix4 has to be released before we can return another Dart object matrix4.Release(); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } -void SceneBuilder::pushOffset(Dart_Handle layer_handle, double dx, double dy) { +void SceneBuilder::pushOffset(Dart_Handle layer_handle, + double dx, + double dy, + fml::RefPtr oldLayer) { SkMatrix sk_matrix = SkMatrix::Translate(dx, dy); auto layer = std::make_shared(sk_matrix); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushClipRect(Dart_Handle layer_handle, @@ -111,67 +113,104 @@ void SceneBuilder::pushClipRect(Dart_Handle layer_handle, double right, double top, double bottom, - int clipBehavior) { + int clipBehavior, + fml::RefPtr oldLayer) { SkRect clipRect = SkRect::MakeLTRB(left, top, right, bottom); flutter::Clip clip_behavior = static_cast(clipBehavior); auto layer = std::make_shared(clipRect, clip_behavior); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushClipRRect(Dart_Handle layer_handle, const RRect& rrect, - int clipBehavior) { + int clipBehavior, + fml::RefPtr oldLayer) { flutter::Clip clip_behavior = static_cast(clipBehavior); auto layer = std::make_shared(rrect.sk_rrect, clip_behavior); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushClipPath(Dart_Handle layer_handle, const CanvasPath* path, - int clipBehavior) { + int clipBehavior, + fml::RefPtr oldLayer) { flutter::Clip clip_behavior = static_cast(clipBehavior); FML_DCHECK(clip_behavior != flutter::Clip::none); auto layer = std::make_shared(path->path(), clip_behavior); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushOpacity(Dart_Handle layer_handle, int alpha, double dx, - double dy) { + double dy, + fml::RefPtr oldLayer) { auto layer = std::make_shared(alpha, SkPoint::Make(dx, dy)); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushColorFilter(Dart_Handle layer_handle, - const ColorFilter* color_filter) { + const ColorFilter* color_filter, + fml::RefPtr oldLayer) { auto layer = std::make_shared(color_filter->filter()); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushImageFilter(Dart_Handle layer_handle, - const ImageFilter* image_filter) { + const ImageFilter* image_filter, + fml::RefPtr oldLayer) { auto layer = std::make_shared(image_filter->filter()); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushBackdropFilter(Dart_Handle layer_handle, - ImageFilter* filter) { - auto layer = std::make_shared(filter->filter()); + ImageFilter* filter, + int blendMode, + fml::RefPtr oldLayer) { + auto layer = std::make_shared( + filter->filter(), static_cast(blendMode)); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushShaderMask(Dart_Handle layer_handle, @@ -180,13 +219,20 @@ void SceneBuilder::pushShaderMask(Dart_Handle layer_handle, double maskRectRight, double maskRectTop, double maskRectBottom, - int blendMode) { + int blendMode, + int filterQualityIndex, + fml::RefPtr oldLayer) { SkRect rect = SkRect::MakeLTRB(maskRectLeft, maskRectTop, maskRectRight, maskRectBottom); + auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); auto layer = std::make_shared( - shader->shader(), rect, static_cast(blendMode)); + shader->shader(sampling), rect, static_cast(blendMode)); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::pushPhysicalShape(Dart_Handle layer_handle, @@ -194,13 +240,18 @@ void SceneBuilder::pushPhysicalShape(Dart_Handle layer_handle, double elevation, int color, int shadow_color, - int clipBehavior) { + int clipBehavior, + fml::RefPtr oldLayer) { auto layer = std::make_shared( static_cast(color), static_cast(shadow_color), static_cast(elevation), path->path(), static_cast(clipBehavior)); PushLayer(layer); EngineLayer::MakeRetained(layer_handle, layer); + + if (oldLayer && oldLayer->Layer()) { + layer->AssignOldLayer(oldLayer->Layer().get()); + } } void SceneBuilder::addRetained(fml::RefPtr retainedLayer) { @@ -215,13 +266,17 @@ void SceneBuilder::addPicture(double dx, double dy, Picture* picture, int hints) { - SkPoint offset = SkPoint::Make(dx, dy); - SkRect pictureRect = picture->picture()->cullRect(); - pictureRect.offset(offset.x(), offset.y()); - auto layer = std::make_unique( - offset, UIDartState::CreateGPUObject(picture->picture()), !!(hints & 1), - !!(hints & 2)); - AddLayer(std::move(layer)); + if (picture->picture()) { + auto layer = std::make_unique( + SkPoint::Make(dx, dy), UIDartState::CreateGPUObject(picture->picture()), + !!(hints & 1), !!(hints & 2)); + AddLayer(std::move(layer)); + } else { + auto layer = std::make_unique( + SkPoint::Make(dx, dy), picture->display_list(), !!(hints & 1), + !!(hints & 2)); + AddLayer(std::move(layer)); + } } void SceneBuilder::addTexture(double dx, @@ -230,10 +285,11 @@ void SceneBuilder::addTexture(double dx, double height, int64_t textureId, bool freeze, - int filterQuality) { + int filterQualityIndex) { + auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); auto layer = std::make_unique( SkPoint::Make(dx, dy), SkSize::Make(width, height), textureId, freeze, - static_cast(filterQuality)); + sampling); AddLayer(std::move(layer)); } @@ -247,20 +303,6 @@ void SceneBuilder::addPlatformView(double dx, AddLayer(std::move(layer)); } -#if defined(LEGACY_FUCHSIA_EMBEDDER) -void SceneBuilder::addChildScene(double dx, - double dy, - double width, - double height, - SceneHost* sceneHost, - bool hitTestable) { - auto layer = std::make_unique( - sceneHost->id(), SkPoint::Make(dx, dy), SkSize::Make(width, height), - hitTestable); - AddLayer(std::move(layer)); -} -#endif - void SceneBuilder::addPerformanceOverlay(uint64_t enabledOptions, double left, double right, @@ -288,9 +330,10 @@ void SceneBuilder::setCheckerboardOffscreenLayers(bool checkerboard) { void SceneBuilder::build(Dart_Handle scene_handle) { FML_DCHECK(layer_stack_.size() >= 1); - Scene::create(scene_handle, layer_stack_[0], rasterizer_tracing_threshold_, - checkerboard_raster_cache_images_, - checkerboard_offscreen_layers_); + Scene::create( + scene_handle, std::move(layer_stack_[0]), rasterizer_tracing_threshold_, + checkerboard_raster_cache_images_, checkerboard_offscreen_layers_); + layer_stack_.clear(); ClearDartWrapper(); // may delete this object. } diff --git a/lib/ui/compositing/scene_builder.h b/lib/ui/compositing/scene_builder.h index 37f229429e679..fe9d3c76cbe05 100644 --- a/lib/ui/compositing/scene_builder.h +++ b/lib/ui/compositing/scene_builder.h @@ -21,10 +21,6 @@ #include "flutter/lib/ui/painting/shader.h" #include "third_party/tonic/typed_data/typed_list.h" -#if defined(LEGACY_FUCHSIA_EMBEDDER) -#include "flutter/lib/ui/compositing/scene_host.h" // nogncheck -#endif - namespace flutter { class SceneBuilder : public RefCountedDartWrappable { @@ -37,42 +33,59 @@ class SceneBuilder : public RefCountedDartWrappable { } ~SceneBuilder() override; - void pushTransform(Dart_Handle layer_handle, tonic::Float64List& matrix4); - void pushOffset(Dart_Handle layer_handle, double dx, double dy); + void pushTransform(Dart_Handle layer_handle, + tonic::Float64List& matrix4, + fml::RefPtr oldLayer); + void pushOffset(Dart_Handle layer_handle, + double dx, + double dy, + fml::RefPtr oldLayer); void pushClipRect(Dart_Handle layer_handle, double left, double right, double top, double bottom, - int clipBehavior); + int clipBehavior, + fml::RefPtr oldLayer); void pushClipRRect(Dart_Handle layer_handle, const RRect& rrect, - int clipBehavior); + int clipBehavior, + fml::RefPtr oldLayer); void pushClipPath(Dart_Handle layer_handle, const CanvasPath* path, - int clipBehavior); + int clipBehavior, + fml::RefPtr oldLayer); void pushOpacity(Dart_Handle layer_handle, int alpha, - double dx = 0, - double dy = 0); + double dx, + double dy, + fml::RefPtr oldLayer); void pushColorFilter(Dart_Handle layer_handle, - const ColorFilter* color_filter); + const ColorFilter* color_filter, + fml::RefPtr oldLayer); void pushImageFilter(Dart_Handle layer_handle, - const ImageFilter* image_filter); - void pushBackdropFilter(Dart_Handle layer_handle, ImageFilter* filter); + const ImageFilter* image_filter, + fml::RefPtr oldLayer); + void pushBackdropFilter(Dart_Handle layer_handle, + ImageFilter* filter, + int blendMode, + fml::RefPtr oldLayer); void pushShaderMask(Dart_Handle layer_handle, Shader* shader, double maskRectLeft, double maskRectRight, double maskRectTop, double maskRectBottom, - int blendMode); + int blendMode, + int filterQualityIndex, + fml::RefPtr oldLayer); void pushPhysicalShape(Dart_Handle layer_handle, const CanvasPath* path, double elevation, int color, int shadowColor, - int clipBehavior); + int clipBehavior, + fml::RefPtr oldLayer); void addRetained(fml::RefPtr retainedLayer); @@ -100,21 +113,16 @@ class SceneBuilder : public RefCountedDartWrappable { double height, int64_t viewId); -#if defined(LEGACY_FUCHSIA_EMBEDDER) - void addChildScene(double dx, - double dy, - double width, - double height, - SceneHost* sceneHost, - bool hitTestable); -#endif - void setRasterizerTracingThreshold(uint32_t frameInterval); void setCheckerboardRasterCacheImages(bool checkerboard); void setCheckerboardOffscreenLayers(bool checkerboard); void build(Dart_Handle scene_handle); + const std::vector>& layer_stack() { + return layer_stack_; + } + static void RegisterNatives(tonic::DartLibraryNatives* natives); private: diff --git a/lib/ui/compositing/scene_builder_unittests.cc b/lib/ui/compositing/scene_builder_unittests.cc new file mode 100644 index 0000000000000..b87f9b5776396 --- /dev/null +++ b/lib/ui/compositing/scene_builder_unittests.cc @@ -0,0 +1,158 @@ +// 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/lib/ui/compositing/scene_builder.h" + +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +TEST_F(ShellTest, SceneBuilderBuildAndSceneDisposeReleasesLayerStack) { + auto message_latch = std::make_shared(); + + // prevent ClearDartWrapper() from deleting the scene builder. + SceneBuilder* retained_scene_builder; + Scene* retained_scene; + + auto validate_builder_has_layers = + [&retained_scene_builder](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + ASSERT_FALSE(Dart_IsError(result)); + retained_scene_builder = reinterpret_cast(peer); + retained_scene_builder->AddRef(); + ASSERT_TRUE(retained_scene_builder); + ASSERT_EQ(retained_scene_builder->layer_stack().size(), 2ul); + }; + + auto validate_builder_has_no_layers = + [&retained_scene_builder](Dart_NativeArguments args) { + ASSERT_EQ(retained_scene_builder->layer_stack().size(), 0ul); + retained_scene_builder->Release(); + retained_scene_builder = nullptr; + }; + + auto capture_scene = [&retained_scene](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + ASSERT_FALSE(Dart_IsError(result)); + retained_scene = reinterpret_cast(peer); + retained_scene->AddRef(); + ASSERT_TRUE(retained_scene); + }; + + auto validate_scene_has_no_layers = + [message_latch, &retained_scene](Dart_NativeArguments args) { + EXPECT_FALSE(retained_scene->takeLayerTree()); + retained_scene->Release(); + retained_scene = nullptr; + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("ValidateBuilderHasLayers", + CREATE_NATIVE_ENTRY(validate_builder_has_layers)); + AddNativeCallback("ValidateBuilderHasNoLayers", + CREATE_NATIVE_ENTRY(validate_builder_has_no_layers)); + + AddNativeCallback("CaptureScene", CREATE_NATIVE_ENTRY(capture_scene)); + AddNativeCallback("ValidateSceneHasNoLayers", + CREATE_NATIVE_ENTRY(validate_scene_has_no_layers)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("validateSceneBuilderAndScene"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + +TEST_F(ShellTest, EngineLayerDisposeReleasesReference) { + auto message_latch = std::make_shared(); + + std::shared_ptr root_layer; + + auto capture_root_layer = [&root_layer](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + ASSERT_FALSE(Dart_IsError(result)); + SceneBuilder* scene_builder = reinterpret_cast(peer); + ASSERT_TRUE(scene_builder); + root_layer = scene_builder->layer_stack()[0]; + ASSERT_TRUE(root_layer); + }; + + auto validate_layer_tree_counts = [&root_layer](Dart_NativeArguments args) { + // One for the EngineLayer, one for our pointer in the test. + EXPECT_EQ(root_layer->layers().front().use_count(), 2); + }; + + auto validate_engine_layer_dispose = + [message_latch, &root_layer](Dart_NativeArguments args) { + // Just one for our pointer now. + EXPECT_EQ(root_layer->layers().front().use_count(), 1); + root_layer.reset(); + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("CaptureRootLayer", + CREATE_NATIVE_ENTRY(capture_root_layer)); + AddNativeCallback("ValidateLayerTreeCounts", + CREATE_NATIVE_ENTRY(validate_layer_tree_counts)); + AddNativeCallback("ValidateEngineLayerDispose", + CREATE_NATIVE_ENTRY(validate_engine_layer_dispose)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("validateEngineLayerDispose"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/compositing/scene_host.cc b/lib/ui/compositing/scene_host.cc deleted file mode 100644 index 25cd5525e19b6..0000000000000 --- a/lib/ui/compositing/scene_host.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/lib/ui/compositing/scene_host.h" - -#include -#include - -#include "flutter/flow/view_holder.h" -#include "flutter/fml/thread_local.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "third_party/dart/runtime/include/dart_api.h" -#include "third_party/tonic/dart_args.h" -#include "third_party/tonic/dart_binding_macros.h" -#include "third_party/tonic/logging/dart_invoke.h" - -namespace { - -struct SceneHostBindingKey { - std::string isolate_service_id; - zx_koid_t koid; - - SceneHostBindingKey(const zx_koid_t koid, - const std::string isolate_service_id) { - this->koid = koid; - this->isolate_service_id = isolate_service_id; - } - - bool operator==(const SceneHostBindingKey& other) const { - return isolate_service_id == other.isolate_service_id && koid == other.koid; - } -}; - -struct SceneHostBindingKeyHasher { - std::size_t operator()(const SceneHostBindingKey& key) const { - std::size_t koid_hash = std::hash()(key.koid); - std::size_t isolate_hash = std::hash()(key.isolate_service_id); - return koid_hash ^ isolate_hash; - } -}; - -using SceneHostBindings = std::unordered_map; - -static SceneHostBindings scene_host_bindings; - -void SceneHost_constructor(Dart_NativeArguments args) { - flutter::UIDartState::ThrowIfUIOperationsProhibited(); - tonic::DartCallConstructor(&flutter::SceneHost::Create, args); -} - -flutter::SceneHost* GetSceneHost(scenic::ResourceId id, - std::string isolate_service_id) { - auto binding = - scene_host_bindings.find(SceneHostBindingKey(id, isolate_service_id)); - if (binding == scene_host_bindings.end()) { - return nullptr; - } else { - return binding->second; - } -} - -flutter::SceneHost* GetSceneHostForCurrentIsolate(scenic::ResourceId id) { - auto isolate = Dart_CurrentIsolate(); - if (!isolate) { - return nullptr; - } else { - std::string isolate_service_id = Dart_IsolateServiceId(isolate); - return GetSceneHost(id, isolate_service_id); - } -} - -void InvokeDartClosure(const tonic::DartPersistentValue& closure) { - auto dart_state = closure.dart_state().lock(); - if (!dart_state) { - return; - } - - tonic::DartState::Scope scope(dart_state); - auto dart_handle = closure.value(); - - FML_DCHECK(dart_handle && !Dart_IsNull(dart_handle) && - Dart_IsClosure(dart_handle)); - tonic::DartInvoke(dart_handle, {}); -} - -template -void InvokeDartFunction(const tonic::DartPersistentValue& function, T& arg) { - auto dart_state = function.dart_state().lock(); - if (!dart_state) { - return; - } - - tonic::DartState::Scope scope(dart_state); - auto dart_handle = function.value(); - - FML_DCHECK(dart_handle && !Dart_IsNull(dart_handle) && - Dart_IsClosure(dart_handle)); - tonic::DartInvoke(dart_handle, {tonic::ToDart(arg)}); -} - -zx_koid_t GetKoid(zx_handle_t handle) { - zx_info_handle_basic_t info; - zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, - sizeof(info), nullptr, nullptr); - return status == ZX_OK ? info.koid : ZX_KOID_INVALID; -} - -} // namespace - -namespace flutter { - -IMPLEMENT_WRAPPERTYPEINFO(ui, SceneHost); - -#define FOR_EACH_BINDING(V) \ - V(SceneHost, dispose) \ - V(SceneHost, setProperties) - -FOR_EACH_BINDING(DART_NATIVE_CALLBACK) - -void SceneHost::RegisterNatives(tonic::DartLibraryNatives* natives) { - natives->Register({{"SceneHost_constructor", SceneHost_constructor, 5, true}, - FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); -} - -fml::RefPtr SceneHost::Create( - fml::RefPtr viewHolderToken, - Dart_Handle viewConnectedCallback, - Dart_Handle viewDisconnectedCallback, - Dart_Handle viewStateChangedCallback) { - return fml::MakeRefCounted(viewHolderToken, viewConnectedCallback, - viewDisconnectedCallback, - viewStateChangedCallback); -} - -void SceneHost::OnViewConnected(scenic::ResourceId id) { - auto* scene_host = GetSceneHostForCurrentIsolate(id); - - if (scene_host && !scene_host->view_connected_callback_.is_empty()) { - InvokeDartClosure(scene_host->view_connected_callback_); - } -} - -void SceneHost::OnViewDisconnected(scenic::ResourceId id) { - auto* scene_host = GetSceneHostForCurrentIsolate(id); - - if (scene_host && !scene_host->view_disconnected_callback_.is_empty()) { - InvokeDartClosure(scene_host->view_disconnected_callback_); - } -} - -void SceneHost::OnViewStateChanged(scenic::ResourceId id, bool state) { - auto* scene_host = GetSceneHostForCurrentIsolate(id); - - if (scene_host && !scene_host->view_state_changed_callback_.is_empty()) { - InvokeDartFunction(scene_host->view_state_changed_callback_, state); - } -} - -SceneHost::SceneHost(fml::RefPtr viewHolderToken, - Dart_Handle viewConnectedCallback, - Dart_Handle viewDisconnectedCallback, - Dart_Handle viewStateChangedCallback) - : raster_task_runner_( - UIDartState::Current()->GetTaskRunners().GetRasterTaskRunner()), - koid_(GetKoid(viewHolderToken->handle())) { - auto dart_state = UIDartState::Current(); - isolate_service_id_ = Dart_IsolateServiceId(Dart_CurrentIsolate()); - - // Initialize callbacks it they are non-null in Dart. - if (!Dart_IsNull(viewConnectedCallback)) { - view_connected_callback_.Set(dart_state, viewConnectedCallback); - } - if (!Dart_IsNull(viewDisconnectedCallback)) { - view_disconnected_callback_.Set(dart_state, viewDisconnectedCallback); - } - if (!Dart_IsNull(viewStateChangedCallback)) { - view_state_changed_callback_.Set(dart_state, viewStateChangedCallback); - } - - // This callback will be posted as a task when the |scenic::ViewHolder| - // resource is created and given an id by the raster thread. - auto bind_callback = [scene_host = this, - isolate_service_id = - isolate_service_id_](scenic::ResourceId id) { - const auto key = SceneHostBindingKey(id, isolate_service_id); - scene_host_bindings.emplace(std::make_pair(key, scene_host)); - }; - - // Pass the raw handle to the raster thread; destroying a - // |zircon::dart::Handle| on that thread can cause a race condition. - raster_task_runner_->PostTask( - [id = koid_, - ui_task_runner = - UIDartState::Current()->GetTaskRunners().GetUITaskRunner(), - raw_handle = viewHolderToken->ReleaseHandle(), bind_callback]() { - flutter::ViewHolder::Create( - id, std::move(ui_task_runner), - scenic::ToViewHolderToken(zx::eventpair(raw_handle)), - std::move(bind_callback)); - }); -} - -SceneHost::~SceneHost() { - scene_host_bindings.erase(SceneHostBindingKey(koid_, isolate_service_id_)); - - raster_task_runner_->PostTask( - [id = koid_]() { flutter::ViewHolder::Destroy(id); }); -} - -void SceneHost::dispose() { - ClearDartWrapper(); -} - -void SceneHost::setProperties(double width, - double height, - double insetTop, - double insetRight, - double insetBottom, - double insetLeft, - bool focusable) { - raster_task_runner_->PostTask([id = koid_, width, height, insetTop, - insetRight, insetBottom, insetLeft, - focusable]() { - auto* view_holder = flutter::ViewHolder::FromId(id); - FML_DCHECK(view_holder); - - view_holder->SetProperties(width, height, insetTop, insetRight, insetBottom, - insetLeft, focusable); - }); -} - -} // namespace flutter diff --git a/lib/ui/compositing/scene_host.h b/lib/ui/compositing/scene_host.h deleted file mode 100644 index 9283ad40d61ea..0000000000000 --- a/lib/ui/compositing/scene_host.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_LIB_UI_COMPOSITING_SCENE_HOST_H_ -#define FLUTTER_LIB_UI_COMPOSITING_SCENE_HOST_H_ - -#include -#include - -#include - -#include "dart-pkg/zircon/sdk_ext/handle.h" -#include "flutter/fml/memory/ref_counted.h" -#include "flutter/fml/task_runner.h" -#include "flutter/lib/ui/dart_wrapper.h" -#include "third_party/tonic/dart_library_natives.h" -#include "third_party/tonic/dart_persistent_value.h" - -namespace flutter { - -class SceneHost : public RefCountedDartWrappable { - DEFINE_WRAPPERTYPEINFO(); - FML_FRIEND_MAKE_REF_COUNTED(SceneHost); - - public: - static void RegisterNatives(tonic::DartLibraryNatives* natives); - static fml::RefPtr Create( - fml::RefPtr viewHolderToken, - Dart_Handle viewConnectedCallback, - Dart_Handle viewDisconnectedCallback, - Dart_Handle viewStateChangedCallback); - static void OnViewConnected(scenic::ResourceId id); - static void OnViewDisconnected(scenic::ResourceId id); - static void OnViewStateChanged(scenic::ResourceId id, bool state); - - ~SceneHost() override; - - zx_koid_t id() const { return koid_; } - - // These are visible to Dart. - void dispose(); - void setProperties(double width, - double height, - double insetTop, - double insetRight, - double insetBottom, - double insetLeft, - bool focusable); - - private: - SceneHost(fml::RefPtr viewHolderToken, - Dart_Handle viewConnectedCallback, - Dart_Handle viewDisconnectedCallback, - Dart_Handle viewStateChangedCallback); - - fml::RefPtr raster_task_runner_; - tonic::DartPersistentValue view_connected_callback_; - tonic::DartPersistentValue view_disconnected_callback_; - tonic::DartPersistentValue view_state_changed_callback_; - std::string isolate_service_id_; - zx_koid_t koid_ = ZX_KOID_INVALID; -}; - -} // namespace flutter - -#endif // FLUTTER_LIB_UI_COMPOSITING_SCENE_HOST_H_ diff --git a/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index 3512f9b446cb8..dd43b37d855ba 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -27,15 +27,6 @@ #include "third_party/tonic/scopes/dart_api_scope.h" #include "third_party/tonic/scopes/dart_isolate_scope.h" -#if defined(OS_ANDROID) -#include -#elif defined(OS_IOS) -extern "C" { -// Cannot import the syslog.h header directly because of macro collision. -extern void syslog(int, const char*, ...); -} -#endif - using tonic::DartConverter; using tonic::LogIfError; using tonic::ToDart; @@ -49,7 +40,6 @@ namespace flutter { #define BUILTIN_NATIVE_LIST(V) \ V(Logger_PrintString, 1) \ V(Logger_PrintDebugString, 1) \ - V(SaveCompilationTrace, 0) \ V(ScheduleMicrotask, 1) \ V(GetCallbackHandle, 1) \ V(GetCallbackFromHandle, 1) @@ -62,17 +52,19 @@ void DartRuntimeHooks::RegisterNatives(tonic::DartLibraryNatives* natives) { static void PropagateIfError(Dart_Handle result) { if (Dart_IsError(result)) { + FML_LOG(ERROR) << "Dart Error: " << ::Dart_GetError(result); Dart_PropagateError(result); } } -static Dart_Handle GetFunction(Dart_Handle builtin_library, const char* name) { +static Dart_Handle InvokeFunction(Dart_Handle builtin_library, + const char* name) { Dart_Handle getter_name = ToDart(name); return Dart_Invoke(builtin_library, getter_name, 0, nullptr); } static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) { - Dart_Handle print = GetFunction(builtin_library, "_getPrintClosure"); + Dart_Handle print = InvokeFunction(builtin_library, "_getPrintClosure"); Dart_Handle internal_library = Dart_LookupLibrary(ToDart("dart:_internal")); @@ -112,7 +104,7 @@ static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) { Dart_Handle schedule_microtask; if (is_ui_isolate) { schedule_microtask = - GetFunction(builtin_library, "_getScheduleMicrotaskClosure"); + InvokeFunction(builtin_library, "_getScheduleMicrotaskClosure"); } else { Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); Dart_Handle method_name = @@ -130,21 +122,24 @@ static void InitDartIO(Dart_Handle builtin_library, const std::string& script_uri) { Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io")); Dart_Handle platform_type = - Dart_GetType(io_lib, ToDart("_Platform"), 0, nullptr); + Dart_GetNonNullableType(io_lib, ToDart("_Platform"), 0, nullptr); if (!script_uri.empty()) { Dart_Handle result = Dart_SetField(platform_type, ToDart("_nativeScript"), ToDart(script_uri)); PropagateIfError(result); } - Dart_Handle locale_closure = - GetFunction(builtin_library, "_getLocaleClosure"); + // typedef _LocaleClosure = String Function(); + Dart_Handle /* _LocaleClosure? */ locale_closure = + InvokeFunction(builtin_library, "_getLocaleClosure"); + PropagateIfError(locale_closure); + // static String Function()? _localeClosure; Dart_Handle result = Dart_SetField(platform_type, ToDart("_localeClosure"), locale_closure); PropagateIfError(result); // Register dart:io service extensions used for network profiling. Dart_Handle network_profiling_type = - Dart_GetType(io_lib, ToDart("_NetworkProfiling"), 0, nullptr); + Dart_GetNonNullableType(io_lib, ToDart("_NetworkProfiling"), 0, nullptr); PropagateIfError(network_profiling_type); result = Dart_Invoke(network_profiling_type, ToDart("_registerServiceExtension"), 0, nullptr); @@ -169,18 +164,8 @@ void Logger_PrintDebugString(Dart_NativeArguments args) { // Implementation of native functions which are used for some // test/debug functionality in standalone dart mode. void Logger_PrintString(Dart_NativeArguments args) { - std::stringstream stream; - const auto& logger_prefix = UIDartState::Current()->logger_prefix(); - -#if !OS_ANDROID - // Prepend all logs with the isolate debug name except on Android where that - // prefix is specified in the log tag. - if (logger_prefix.size() > 0) { - stream << logger_prefix << ": "; - } -#endif // !OS_ANDROID - - // Append the log buffer obtained from Dart code. + // Obtain the log buffer from Dart code. + std::string message; { Dart_Handle str = Dart_GetNativeArgument(args, 0); uint8_t* chars = nullptr; @@ -191,70 +176,31 @@ void Logger_PrintString(Dart_NativeArguments args) { return; } if (length > 0) { - stream << std::string{reinterpret_cast(chars), + message = std::string{reinterpret_cast(chars), static_cast(length)}; } } - const auto log_string = stream.str(); - const char* chars = log_string.c_str(); - const size_t length = log_string.size(); - - // Log using platform specific mechanisms - { -#if defined(OS_ANDROID) - // Write to the logcat on Android. - __android_log_print(ANDROID_LOG_INFO, logger_prefix.c_str(), "%.*s", - (int)length, chars); -#elif defined(OS_IOS) - // Write to syslog on iOS. - // - // TODO(cbracken): replace with dedicated communication channel and bypass - // iOS logging APIs altogether. - syslog(1 /* LOG_ALERT */, "%.*s", (int)length, chars); -#else - std::cout << log_string << std::endl; -#endif - } + const auto& tag = UIDartState::Current()->logger_prefix(); + UIDartState::Current()->LogMessage(tag, message); if (dart::bin::ShouldCaptureStdout()) { + std::stringstream stream; + if (tag.size() > 0) { + stream << tag << ": "; + } + stream << message; + std::string log = stream.str(); + // For now we report print output on the Stdout stream. uint8_t newline[] = {'\n'}; Dart_ServiceSendDataEvent("Stdout", "WriteEvent", - reinterpret_cast(chars), length); + reinterpret_cast(log.c_str()), + log.size()); Dart_ServiceSendDataEvent("Stdout", "WriteEvent", newline, sizeof(newline)); } } -void SaveCompilationTrace(Dart_NativeArguments args) { - uint8_t* buffer = nullptr; - intptr_t length = 0; - Dart_Handle result = Dart_SaveCompilationTrace(&buffer, &length); - if (Dart_IsError(result)) { - Dart_SetReturnValue(args, result); - return; - } - - result = Dart_NewTypedData(Dart_TypedData_kUint8, length); - if (Dart_IsError(result)) { - Dart_SetReturnValue(args, result); - return; - } - - Dart_TypedData_Type type; - void* data = nullptr; - intptr_t size = 0; - Dart_Handle status = Dart_TypedDataAcquireData(result, &type, &data, &size); - if (Dart_IsError(status)) { - Dart_SetReturnValue(args, status); - return; - } - - memcpy(data, buffer, length); - Dart_TypedDataReleaseData(result); - Dart_SetReturnValue(args, result); -} - void ScheduleMicrotask(Dart_NativeArguments args) { Dart_Handle closure = Dart_GetNativeArgument(args, 0); UIDartState::Current()->ScheduleMicrotask(closure); diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index c22b26b17d3f6..d2958c838420c 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -26,6 +26,7 @@ #include "flutter/lib/ui/painting/vertices.h" #include "flutter/lib/ui/semantics/semantics_update.h" #include "flutter/lib/ui/semantics/semantics_update_builder.h" +#include "flutter/lib/ui/semantics/string_attribute.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/text/paragraph.h" #include "flutter/lib/ui/text/paragraph_builder.h" @@ -33,10 +34,6 @@ #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/logging/dart_error.h" -#if defined(LEGACY_FUCHSIA_EMBEDDER) -#include "flutter/lib/ui/compositing/scene_host.h" // nogncheck -#endif - using tonic::ToDart; namespace flutter { @@ -74,6 +71,7 @@ void DartUI::InitForGlobal() { ImageShader::RegisterNatives(g_natives); ImmutableBuffer::RegisterNatives(g_natives); IsolateNameServerNatives::RegisterNatives(g_natives); + NativeStringAttribute::RegisterNatives(g_natives); Paragraph::RegisterNatives(g_natives); ParagraphBuilder::RegisterNatives(g_natives); Picture::RegisterNatives(g_natives); @@ -84,9 +82,6 @@ void DartUI::InitForGlobal() { SemanticsUpdateBuilder::RegisterNatives(g_natives); Vertices::RegisterNatives(g_natives); PlatformConfiguration::RegisterNatives(g_natives); -#if defined(LEGACY_FUCHSIA_EMBEDDER) - SceneHost::RegisterNatives(g_natives); -#endif } } diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index 28cb82e9cd125..96a7b06fbd918 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -10,9 +10,11 @@ dart_ui_files = [ "//flutter/lib/ui/hash_codes.dart", "//flutter/lib/ui/hooks.dart", "//flutter/lib/ui/isolate_name_server.dart", + "//flutter/lib/ui/key.dart", "//flutter/lib/ui/lerp.dart", "//flutter/lib/ui/natives.dart", "//flutter/lib/ui/painting.dart", + "//flutter/lib/ui/platform_dispatcher.dart", "//flutter/lib/ui/plugins.dart", "//flutter/lib/ui/pointer.dart", "//flutter/lib/ui/semantics.dart", diff --git a/lib/ui/fixtures/README.md b/lib/ui/fixtures/README.md new file mode 100644 index 0000000000000..d31d74d60a3bf --- /dev/null +++ b/lib/ui/fixtures/README.md @@ -0,0 +1,11 @@ +# ui_unittest Fixtures + +The files in this directory are used by the ui_unittests binary. + +The `ui_test.dart` file is either JIT or AOT compiled depending on the runtime +mode of the test binary. Other files in this folder are used by tests to verify +functionality. + +See `//lib/ui/BUILD.gn` and `//testing/testing.gni` for the build rules and +GN template definitions that determine which files get included and compiled +here. diff --git a/lib/ui/fixtures/ui_test.dart b/lib/ui/fixtures/ui_test.dart index 87effaad4f5c7..146800776ad8d 100644 --- a/lib/ui/fixtures/ui_test.dart +++ b/lib/ui/fixtures/ui_test.dart @@ -1,4 +1,3 @@ -// @dart = 2.6 // 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. @@ -9,6 +8,63 @@ import 'dart:ui'; void main() {} +/// Mutiple tests use this to signal to the C++ side that they are ready for +/// validation. +void _finish() native 'Finish'; + +@pragma('vm:entry-point') +void validateSceneBuilderAndScene() { + final SceneBuilder builder = SceneBuilder(); + builder.pushOffset(10, 10); + _validateBuilderHasLayers(builder); + final Scene scene = builder.build(); + _validateBuilderHasNoLayers(); + _captureScene(scene); + scene.dispose(); + _validateSceneHasNoLayers(); +} +_validateBuilderHasLayers(SceneBuilder builder) native 'ValidateBuilderHasLayers'; +_validateBuilderHasNoLayers() native 'ValidateBuilderHasNoLayers'; +_captureScene(Scene scene) native 'CaptureScene'; +_validateSceneHasNoLayers() native 'ValidateSceneHasNoLayers'; + +@pragma('vm:entry-point') +void validateEngineLayerDispose() { + final SceneBuilder builder = SceneBuilder(); + final EngineLayer layer = builder.pushOffset(10, 10); + _captureRootLayer(builder); + final Scene scene = builder.build(); + scene.dispose(); + _validateLayerTreeCounts(); + layer.dispose(); + _validateEngineLayerDispose(); +} +_captureRootLayer(SceneBuilder sceneBuilder) native 'CaptureRootLayer'; +_validateLayerTreeCounts() native 'ValidateLayerTreeCounts'; +_validateEngineLayerDispose() native 'ValidateEngineLayerDispose'; + +@pragma('vm:entry-point') +Future createSingleFrameCodec() async { + final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(Uint8List.fromList(List.filled(4, 100))); + final ImageDescriptor descriptor = ImageDescriptor.raw( + buffer, + width: 1, + height: 1, + pixelFormat: PixelFormat.rgba8888, + ); + final Codec codec = await descriptor.instantiateCodec(); + _validateCodec(codec); + final FrameInfo info = await codec.getNextFrame(); + info.image.dispose(); + _validateCodec(codec); + codec.dispose(); + descriptor.dispose(); + buffer.dispose(); + assert(buffer.debugDisposed); + _finish(); +} +void _validateCodec(Codec codec) native 'ValidateCodec'; + @pragma('vm:entry-point') void createVertices() { const int uint16max = 65535; @@ -35,9 +91,111 @@ void createVertices() { ); _validateVertices(vertices); } - void _validateVertices(Vertices vertices) native 'ValidateVertices'; +@pragma('vm:entry-point') +void sendSemanticsUpdate() { + final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder(); + final String label = "label"; + final List labelAttributes = [ + SpellOutStringAttribute(range: TextRange(start: 1, end: 2)), + ]; + + final String value = "value"; + final List valueAttributes = [ + SpellOutStringAttribute(range: TextRange(start: 2, end: 3)), + ]; + + final String increasedValue = "increasedValue"; + final List increasedValueAttributes = [ + SpellOutStringAttribute(range: TextRange(start: 4, end: 5)), + ]; + + final String decreasedValue = "decreasedValue"; + final List decreasedValueAttributes = [ + SpellOutStringAttribute(range: TextRange(start: 5, end: 6)), + ]; + + final String hint = "hint"; + final List hintAttributes = [ + LocaleStringAttribute( + locale: Locale('en', 'MX'), range: TextRange(start: 0, end: 1), + ), + ]; + + final Float64List transform = Float64List(16); + final Int32List childrenInTraversalOrder = Int32List(0); + final Int32List childrenInHitTestOrder = Int32List(0); + final Int32List additionalActions = Int32List(0); + transform[0] = 1; + transform[1] = 0; + transform[2] = 0; + transform[3] = 0; + + transform[4] = 0; + transform[5] = 1; + transform[6] = 0; + transform[7] = 0; + + transform[8] = 0; + transform[9] = 0; + transform[10] = 1; + transform[11] = 0; + + transform[12] = 0; + transform[13] = 0; + transform[14] = 0; + transform[15] = 0; + builder.updateNode( + id: 0, + flags: 0, + actions: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: -1, + textSelectionExtent: -1, + platformViewId: -1, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0, + scrollExtentMax: 0, + scrollExtentMin: 0, + rect: Rect.fromLTRB(0, 0, 10, 10), + elevation: 0, + thickness: 0, + label: label, + labelAttributes: labelAttributes, + value: value, + valueAttributes: valueAttributes, + increasedValue: increasedValue, + increasedValueAttributes: increasedValueAttributes, + decreasedValue: decreasedValue, + decreasedValueAttributes: decreasedValueAttributes, + hint: hint, + hintAttributes: hintAttributes, + textDirection: TextDirection.ltr, + transform: transform, + childrenInTraversalOrder: childrenInTraversalOrder, + childrenInHitTestOrder: childrenInHitTestOrder, + additionalActions: additionalActions + ); + _semanticsUpdate(builder.build()); +} + +void _semanticsUpdate(SemanticsUpdate update) native 'SemanticsUpdate'; + +@pragma('vm:entry-point') +void createPath() { + final Path path = Path()..lineTo(10, 10); + _validatePath(path); + // Arbitrarily hold a reference to the path to make sure it does not get + // garbage collected. + Future.delayed(const Duration(days: 100)).then((_) { + path.lineTo(100, 100); + }); +} +void _validatePath(Path path) native 'ValidatePath'; + @pragma('vm:entry-point') void frameCallback(FrameInfo info) { print('called back'); @@ -77,8 +235,8 @@ void _validateExternal(Uint8List result) native 'ValidateExternal'; @pragma('vm:entry-point') Future pumpImage() async { - const int width = 6000; - const int height = 6000; + const int width = 60; + const int height = 60; final Completer completer = Completer(); decodeImageFromPixels( Uint8List.fromList(List.filled(width * height * 4, 0xFF)), @@ -88,34 +246,37 @@ Future pumpImage() async { (Image image) => completer.complete(image), ); final Image image = await completer.future; + late Picture picture; + late OffsetEngineLayer layer; - final FrameCallback renderBlank = (Duration duration) { + void renderBlank(Duration duration) { image.dispose(); + picture.dispose(); + layer.dispose(); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect(Rect.largest, Paint()); - final Picture picture = recorder.endRecording(); - + canvas.drawPaint(Paint()); + picture = recorder.endRecording(); final SceneBuilder builder = SceneBuilder(); + layer = builder.pushOffset(0, 0); builder.addPicture(Offset.zero, picture); final Scene scene = builder.build(); window.render(scene); scene.dispose(); - window.onBeginFrame = (Duration duration) { - window.onDrawFrame = _onBeginFrameDone; - }; - window.scheduleFrame(); - }; - final FrameCallback renderImage = (Duration duration) { + _finish(); + } + + void renderImage(Duration duration) { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawImage(image, Offset.zero, Paint()); - final Picture picture = recorder.endRecording(); + picture = recorder.endRecording(); final SceneBuilder builder = SceneBuilder(); + layer = builder.pushOffset(0, 0); builder.addPicture(Offset.zero, picture); _captureImageAndPicture(image, picture); @@ -123,12 +284,472 @@ Future pumpImage() async { final Scene scene = builder.build(); window.render(scene); scene.dispose(); + window.onBeginFrame = renderBlank; window.scheduleFrame(); - }; + } window.onBeginFrame = renderImage; window.scheduleFrame(); } void _captureImageAndPicture(Image image, Picture picture) native 'CaptureImageAndPicture'; -Future _onBeginFrameDone() native 'OnBeginFrameDone'; + +@pragma('vm:entry-point') +void hooksTests() { + void test(String name, VoidCallback testFunction) { + try { + testFunction(); + } catch (e) { + print('Test "$name" failed!'); + rethrow; + } + } + + void expectEquals(Object? value, Object? expected) { + if (value != expected) { + throw 'Expected $value to be $expected.'; + } + } + + void expectIdentical(Zone originalZone, Zone callbackZone) { + if (!identical(callbackZone, originalZone)) { + throw 'Callback called in wrong zone.'; + } + } + + void expectNotEquals(Object? value, Object? expected) { + if (value == expected) { + throw 'Expected $value to not be $expected.'; + } + } + + test('onMetricsChanged preserves callback zone', () { + late Zone originalZone; + late Zone callbackZone; + late double devicePixelRatio; + + runZoned(() { + originalZone = Zone.current; + window.onMetricsChanged = () { + callbackZone = Zone.current; + devicePixelRatio = window.devicePixelRatio; + }; + }); + + window.onMetricsChanged!(); + _callHook( + '_updateWindowMetrics', + 17, + 0, // window Id + 0.1234, // device pixel ratio + 0.0, // width + 0.0, // height + 0.0, // padding top + 0.0, // padding right + 0.0, // padding bottom + 0.0, // padding left + 0.0, // inset top + 0.0, // inset right + 0.0, // inset bottom + 0.0, // inset left + 0.0, // system gesture inset top + 0.0, // system gesture inset right + 0.0, // system gesture inset bottom + 0.0, // system gesture inset left, + 22.0, // physicalTouchSlop + ); + + expectIdentical(originalZone, callbackZone); + if (devicePixelRatio != 0.1234) { + throw 'Expected devicePixelRatio to be 0.1234 but got $devicePixelRatio.'; + } + }); + + test('updateUserSettings can handle an empty object', () { + _callHook('_updateUserSettingsData', 1, '{}'); + }); + + test('PlatformDispatcher.locale returns unknown locale when locales is set to empty list', () { + late Locale locale; + int callCount = 0; + runZoned(() { + window.onLocaleChanged = () { + locale = PlatformDispatcher.instance.locale; + callCount += 1; + }; + }); + + const Locale fakeLocale = Locale.fromSubtags(languageCode: '1', countryCode: '2', scriptCode: '3'); + _callHook('_updateLocales', 1, [fakeLocale.languageCode, fakeLocale.countryCode!, fakeLocale.scriptCode!, '']); + if (callCount != 1) { + throw 'Expected 1 call, have $callCount'; + } + if (locale != fakeLocale) { + throw 'Expected $locale to match $fakeLocale'; + } + _callHook('_updateLocales', 1, []); + if (callCount != 2) { + throw 'Expected 2 calls, have $callCount'; + } + + if (locale != const Locale.fromSubtags()) { + throw '$locale did not equal ${Locale.fromSubtags()}'; + } + if (locale.languageCode != 'und') { + throw '${locale.languageCode} did not equal "und"'; + } + }); + + test('Window padding/insets/viewPadding/systemGestureInsets', () { + _callHook( + '_updateWindowMetrics', + 17, + 0, // window Id + 1.0, // devicePixelRatio + 800.0, // width + 600.0, // height + 50.0, // paddingTop + 0.0, // paddingRight + 40.0, // paddingBottom + 0.0, // paddingLeft + 0.0, // insetTop + 0.0, // insetRight + 0.0, // insetBottom + 0.0, // insetLeft + 0.0, // systemGestureInsetTop + 0.0, // systemGestureInsetRight + 0.0, // systemGestureInsetBottom + 0.0, // systemGestureInsetLeft + 22.0, // physicalTouchSlop + ); + + expectEquals(window.viewInsets.bottom, 0.0); + expectEquals(window.viewPadding.bottom, 40.0); + expectEquals(window.padding.bottom, 40.0); + expectEquals(window.systemGestureInsets.bottom, 0.0); + + _callHook( + '_updateWindowMetrics', + 17, + 0, // window Id + 1.0, // devicePixelRatio + 800.0, // width + 600.0, // height + 50.0, // paddingTop + 0.0, // paddingRight + 40.0, // paddingBottom + 0.0, // paddingLeft + 0.0, // insetTop + 0.0, // insetRight + 400.0, // insetBottom + 0.0, // insetLeft + 0.0, // systemGestureInsetTop + 0.0, // systemGestureInsetRight + 44.0, // systemGestureInsetBottom + 0.0, // systemGestureInsetLeft + 22.0, // physicalTouchSlop + ); + + expectEquals(window.viewInsets.bottom, 400.0); + expectEquals(window.viewPadding.bottom, 40.0); + expectEquals(window.padding.bottom, 0.0); + expectEquals(window.systemGestureInsets.bottom, 44.0); + }); + + test('Window physical touch slop', () { + _callHook( + '_updateWindowMetrics', + 17, + 0, // window Id + 1.0, // devicePixelRatio + 800.0, // width + 600.0, // height + 50.0, // paddingTop + 0.0, // paddingRight + 40.0, // paddingBottom + 0.0, // paddingLeft + 0.0, // insetTop + 0.0, // insetRight + 0.0, // insetBottom + 0.0, // insetLeft + 0.0, // systemGestureInsetTop + 0.0, // systemGestureInsetRight + 0.0, // systemGestureInsetBottom + 0.0, // systemGestureInsetLeft + 11.0, // physicalTouchSlop + ); + + expectEquals(window.viewConfiguration.gestureSettings, + GestureSettings(physicalTouchSlop: 11.0)); + + _callHook( + '_updateWindowMetrics', + 17, + 0, // window Id + 1.0, // devicePixelRatio + 800.0, // width + 600.0, // height + 50.0, // paddingTop + 0.0, // paddingRight + 40.0, // paddingBottom + 0.0, // paddingLeft + 0.0, // insetTop + 0.0, // insetRight + 400.0, // insetBottom + 0.0, // insetLeft + 0.0, // systemGestureInsetTop + 0.0, // systemGestureInsetRight + 44.0, // systemGestureInsetBottom + 0.0, // systemGestureInsetLeft + -1.0, // physicalTouchSlop + ); + + expectEquals(window.viewConfiguration.gestureSettings, + GestureSettings(physicalTouchSlop: null)); + + _callHook( + '_updateWindowMetrics', + 17, + 0, // window Id + 1.0, // devicePixelRatio + 800.0, // width + 600.0, // height + 50.0, // paddingTop + 0.0, // paddingRight + 40.0, // paddingBottom + 0.0, // paddingLeft + 0.0, // insetTop + 0.0, // insetRight + 400.0, // insetBottom + 0.0, // insetLeft + 0.0, // systemGestureInsetTop + 0.0, // systemGestureInsetRight + 44.0, // systemGestureInsetBottom + 0.0, // systemGestureInsetLeft + 22.0, // physicalTouchSlop + ); + + expectEquals(window.viewConfiguration.gestureSettings, + GestureSettings(physicalTouchSlop: 22.0)); + }); + + test('onLocaleChanged preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + Locale? locale; + + runZoned(() { + innerZone = Zone.current; + window.onLocaleChanged = () { + runZone = Zone.current; + locale = window.locale; + }; + }); + + _callHook('_updateLocales', 1, ['en', 'US', '', '']); + expectIdentical(runZone, innerZone); + expectEquals(locale, const Locale('en', 'US')); + }); + + test('onBeginFrame preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late Duration start; + + runZoned(() { + innerZone = Zone.current; + window.onBeginFrame = (Duration value) { + runZone = Zone.current; + start = value; + }; + }); + + _callHook('_beginFrame', 2, 1234, 1); + expectIdentical(runZone, innerZone); + expectEquals(start, const Duration(microseconds: 1234)); + }); + + test('onDrawFrame preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + + runZoned(() { + innerZone = Zone.current; + window.onDrawFrame = () { + runZone = Zone.current; + }; + }); + + _callHook('_drawFrame'); + expectIdentical(runZone, innerZone); + }); + + test('onReportTimings preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + + runZoned(() { + innerZone = Zone.current; + window.onReportTimings = (List timings) { + runZone = Zone.current; + }; + }); + + _callHook('_reportTimings', 1, []); + expectIdentical(runZone, innerZone); + }); + + test('onPointerDataPacket preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late PointerDataPacket data; + + runZoned(() { + innerZone = Zone.current; + window.onPointerDataPacket = (PointerDataPacket value) { + runZone = Zone.current; + data = value; + }; + }); + + final ByteData testData = ByteData.view(Uint8List(0).buffer); + _callHook('_dispatchPointerDataPacket', 1, testData); + expectIdentical(runZone, innerZone); + expectEquals(data.data.length, 0); + }); + + test('onSemanticsEnabledChanged preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late bool enabled; + + runZoned(() { + innerZone = Zone.current; + window.onSemanticsEnabledChanged = () { + runZone = Zone.current; + enabled = window.semanticsEnabled; + }; + }); + + final bool newValue = !window.semanticsEnabled; // needed? + _callHook('_updateSemanticsEnabled', 1, newValue); + expectIdentical(runZone, innerZone); + expectEquals(enabled, newValue); + }); + + test('onSemanticsAction preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late int id; + late int action; + + runZoned(() { + innerZone = Zone.current; + window.onSemanticsAction = (int i, SemanticsAction a, ByteData? _) { + runZone = Zone.current; + action = a.index; + id = i; + }; + }); + + _callHook('_dispatchSemanticsAction', 3, 1234, 4, null); + expectIdentical(runZone, innerZone); + expectEquals(id, 1234); + expectEquals(action, 4); + }); + + test('onPlatformMessage preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late String name; + + runZoned(() { + innerZone = Zone.current; + window.onPlatformMessage = (String value, _, __) { + runZone = Zone.current; + name = value; + }; + }); + + _callHook('_dispatchPlatformMessage', 3, 'testName', null, 123456789); + expectIdentical(runZone, innerZone); + expectEquals(name, 'testName'); + }); + + test('onTextScaleFactorChanged preserves callback zone', () { + late Zone innerZone; + late Zone runZoneTextScaleFactor; + late Zone runZonePlatformBrightness; + late double? textScaleFactor; + late Brightness? platformBrightness; + + runZoned(() { + innerZone = Zone.current; + window.onTextScaleFactorChanged = () { + runZoneTextScaleFactor = Zone.current; + textScaleFactor = window.textScaleFactor; + }; + window.onPlatformBrightnessChanged = () { + runZonePlatformBrightness = Zone.current; + platformBrightness = window.platformBrightness; + }; + }); + + window.onTextScaleFactorChanged!(); + + _callHook('_updateUserSettingsData', 1, '{"textScaleFactor": 0.5, "platformBrightness": "light", "alwaysUse24HourFormat": true}'); + expectIdentical(runZoneTextScaleFactor, innerZone); + expectEquals(textScaleFactor, 0.5); + + textScaleFactor = null; + platformBrightness = null; + + window.onPlatformBrightnessChanged!(); + _callHook('_updateUserSettingsData', 1, '{"textScaleFactor": 0.5, "platformBrightness": "dark", "alwaysUse24HourFormat": true}'); + expectIdentical(runZonePlatformBrightness, innerZone); + expectEquals(platformBrightness, Brightness.dark); + }); + + test('onFrameDataChanged preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late int frameNumber; + + runZoned(() { + innerZone = Zone.current; + window.onFrameDataChanged = () { + runZone = Zone.current; + frameNumber = window.frameData.frameNumber; + }; + }); + + _callHook('_beginFrame', 2, 0, 2); + expectNotEquals(runZone, null); + expectIdentical(runZone, innerZone); + expectEquals(frameNumber, 2); + }); + + _finish(); +} + +void _callHook( + String name, [ + int argCount = 0, + Object? arg0, + Object? arg1, + Object? arg2, + Object? arg3, + Object? arg4, + Object? arg5, + Object? arg6, + Object? arg8, + Object? arg9, + Object? arg10, + Object? arg11, + Object? arg12, + Object? arg13, + Object? arg14, + Object? arg15, + Object? arg16, + Object? arg17, +]) native 'CallHook'; diff --git a/lib/ui/geometry.dart b/lib/ui/geometry.dart index 6d3b1ac5b5936..e273ec8bc0514 100644 --- a/lib/ui/geometry.dart +++ b/lib/ui/geometry.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// Base class for [Size] and [Offset], which are both ways to describe @@ -15,8 +15,8 @@ abstract class OffsetBase { /// The first argument sets the horizontal component, and the second the /// vertical component. const OffsetBase(this._dx, this._dy) - : assert(_dx != null), // ignore: unnecessary_null_comparison - assert(_dy != null); // ignore: unnecessary_null_comparison + : assert(_dx != null), + assert(_dy != null); final double _dx; final double _dy; @@ -316,7 +316,7 @@ class Offset extends OffsetBase { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Offset? lerp(Offset? a, Offset? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; @@ -587,7 +587,7 @@ class Size extends OffsetBase { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Size? lerp(Size? a, Size? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; @@ -632,10 +632,10 @@ class Rect { /// Construct a rectangle from its left, top, right, and bottom edges. @pragma('vm:entry-point') const Rect.fromLTRB(this.left, this.top, this.right, this.bottom) - : assert(left != null), // ignore: unnecessary_null_comparison - assert(top != null), // ignore: unnecessary_null_comparison - assert(right != null), // ignore: unnecessary_null_comparison - assert(bottom != null); // ignore: unnecessary_null_comparison + : assert(left != null), + assert(top != null), + assert(right != null), + assert(bottom != null); /// Construct a rectangle from its left and top edges, its width, and its /// height. @@ -864,7 +864,7 @@ class Rect { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Rect? lerp(Rect? a, Rect? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; @@ -993,7 +993,7 @@ class Radius { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Radius? lerp(Radius? a, Radius? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; @@ -1179,18 +1179,18 @@ class RRect { this.brRadiusY = 0.0, this.blRadiusX = 0.0, this.blRadiusY = 0.0, - }) : assert(left != null), // ignore: unnecessary_null_comparison - assert(top != null), // ignore: unnecessary_null_comparison - assert(right != null), // ignore: unnecessary_null_comparison - assert(bottom != null), // ignore: unnecessary_null_comparison - assert(tlRadiusX != null), // ignore: unnecessary_null_comparison - assert(tlRadiusY != null), // ignore: unnecessary_null_comparison - assert(trRadiusX != null), // ignore: unnecessary_null_comparison - assert(trRadiusY != null), // ignore: unnecessary_null_comparison - assert(brRadiusX != null), // ignore: unnecessary_null_comparison - assert(brRadiusY != null), // ignore: unnecessary_null_comparison - assert(blRadiusX != null), // ignore: unnecessary_null_comparison - assert(blRadiusY != null); // ignore: unnecessary_null_comparison + }) : assert(left != null), + assert(top != null), + assert(right != null), + assert(bottom != null), + assert(tlRadiusX != null), + assert(tlRadiusY != null), + assert(trRadiusX != null), + assert(trRadiusY != null), + assert(brRadiusX != null), + assert(brRadiusY != null), + assert(blRadiusX != null), + assert(blRadiusY != null); Float32List get _value32 => Float32List.fromList([ left, @@ -1558,7 +1558,7 @@ class RRect { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static RRect? lerp(RRect? a, RRect? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; diff --git a/lib/ui/hash_codes.dart b/lib/ui/hash_codes.dart index 7da175a27312f..71da5b3642b2e 100644 --- a/lib/ui/hash_codes.dart +++ b/lib/ui/hash_codes.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; class _HashEnd { const _HashEnd(); } @@ -116,7 +116,7 @@ int hashValues( int hashList(Iterable? arguments) { int result = 0; if (arguments != null) { - for (Object? argument in arguments) + for (final Object? argument in arguments) result = _Jenkins.combine(result, argument); } return _Jenkins.finish(result); diff --git a/lib/ui/hint_freed_delegate.h b/lib/ui/hint_freed_delegate.h deleted file mode 100644 index 0e4a38b41454c..0000000000000 --- a/lib/ui/hint_freed_delegate.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_LIB_UI_HINT_FREED_DELEGATE_H_ -#define FLUTTER_LIB_UI_HINT_FREED_DELEGATE_H_ - -namespace flutter { - -class HintFreedDelegate { - public: - //---------------------------------------------------------------------------- - /// @brief Notifies the engine that native bytes might be freed if a - /// garbage collection ran at the next NotifyIdle period. - /// - /// @param[in] size The number of bytes freed. This size adds to any - /// previously supplied value, rather than replacing. - /// - virtual void HintFreed(size_t size) = 0; -}; - -} // namespace flutter - -#endif // FLUTTER_LIB_UI_HINT_FREED_DELEGATE_H_ diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index a911d79f63a8d..c0b78e7a291cf 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -4,13 +4,14 @@ // TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; @pragma('vm:entry-point') // ignore: unused_element void _updateWindowMetrics( + Object id, double devicePixelRatio, double width, double height, @@ -26,206 +27,119 @@ void _updateWindowMetrics( double systemGestureInsetRight, double systemGestureInsetBottom, double systemGestureInsetLeft, + double physicalTouchSlop, ) { - window - .._devicePixelRatio = devicePixelRatio - .._physicalSize = Size(width, height) - .._viewPadding = WindowPadding._( - top: viewPaddingTop, - right: viewPaddingRight, - bottom: viewPaddingBottom, - left: viewPaddingLeft) - .._viewInsets = WindowPadding._( - top: viewInsetTop, - right: viewInsetRight, - bottom: viewInsetBottom, - left: viewInsetLeft) - .._padding = WindowPadding._( - top: math.max(0.0, viewPaddingTop - viewInsetTop), - right: math.max(0.0, viewPaddingRight - viewInsetRight), - bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), - left: math.max(0.0, viewPaddingLeft - viewInsetLeft)) - .._systemGestureInsets = WindowPadding._( - top: math.max(0.0, systemGestureInsetTop), - right: math.max(0.0, systemGestureInsetRight), - bottom: math.max(0.0, systemGestureInsetBottom), - left: math.max(0.0, systemGestureInsetLeft)); - _invoke(window.onMetricsChanged, window._onMetricsChangedZone); + PlatformDispatcher.instance._updateWindowMetrics( + id, + devicePixelRatio, + width, + height, + viewPaddingTop, + viewPaddingRight, + viewPaddingBottom, + viewPaddingLeft, + viewInsetTop, + viewInsetRight, + viewInsetBottom, + viewInsetLeft, + systemGestureInsetTop, + systemGestureInsetRight, + systemGestureInsetBottom, + systemGestureInsetLeft, + physicalTouchSlop, + ); } -typedef _LocaleClosure = String? Function(); - -String? _localeClosure() { - if (window.locale == null) { - return null; - } - return window.locale.toString(); -} +typedef _LocaleClosure = String Function(); @pragma('vm:entry-point') // ignore: unused_element -_LocaleClosure? _getLocaleClosure() => _localeClosure; +_LocaleClosure? _getLocaleClosure() => PlatformDispatcher.instance._localeClosure; @pragma('vm:entry-point') // ignore: unused_element void _updateLocales(List locales) { - const int stringsPerLocale = 4; - final int numLocales = locales.length ~/ stringsPerLocale; - final List newLocales = []; - for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { - final String countryCode = locales[localeIndex * stringsPerLocale + 1]; - final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; - - newLocales.add(Locale.fromSubtags( - languageCode: locales[localeIndex * stringsPerLocale], - countryCode: countryCode.isEmpty ? null : countryCode, - scriptCode: scriptCode.isEmpty ? null : scriptCode, - )); - } - window._locales = newLocales; - _invoke(window.onLocaleChanged, window._onLocaleChangedZone); + PlatformDispatcher.instance._updateLocales(locales); } @pragma('vm:entry-point') // ignore: unused_element void _updateUserSettingsData(String jsonData) { - final Map data = json.decode(jsonData) as Map; - if (data.isEmpty) { - return; - } - _updateTextScaleFactor((data['textScaleFactor'] as num).toDouble()); - _updateAlwaysUse24HourFormat(data['alwaysUse24HourFormat'] as bool); - _updatePlatformBrightness(data['platformBrightness'] as String); + PlatformDispatcher.instance._updateUserSettingsData(jsonData); } @pragma('vm:entry-point') // ignore: unused_element void _updateLifecycleState(String state) { - // We do not update the state if the state has already been used to initialize - // the lifecycleState. - if (!window._initialLifecycleStateAccessed) - window._initialLifecycleState = state; -} - - -void _updateTextScaleFactor(double textScaleFactor) { - window._textScaleFactor = textScaleFactor; - _invoke(window.onTextScaleFactorChanged, window._onTextScaleFactorChangedZone); -} - -void _updateAlwaysUse24HourFormat(bool alwaysUse24HourFormat) { - window._alwaysUse24HourFormat = alwaysUse24HourFormat; -} - -void _updatePlatformBrightness(String brightnessName) { - window._platformBrightness = brightnessName == 'dark' ? Brightness.dark : Brightness.light; - _invoke(window.onPlatformBrightnessChanged, window._onPlatformBrightnessChangedZone); + PlatformDispatcher.instance._updateLifecycleState(state); } @pragma('vm:entry-point') // ignore: unused_element void _updateSemanticsEnabled(bool enabled) { - window._semanticsEnabled = enabled; - _invoke(window.onSemanticsEnabledChanged, window._onSemanticsEnabledChangedZone); + PlatformDispatcher.instance._updateSemanticsEnabled(enabled); } @pragma('vm:entry-point') // ignore: unused_element void _updateAccessibilityFeatures(int values) { - final AccessibilityFeatures newFeatures = AccessibilityFeatures._(values); - if (newFeatures == window._accessibilityFeatures) - return; - window._accessibilityFeatures = newFeatures; - _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFeaturesChangedZone); + PlatformDispatcher.instance._updateAccessibilityFeatures(values); } @pragma('vm:entry-point') // ignore: unused_element void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { - if (name == ChannelBuffers.kControlChannelName) { - try { - channelBuffers.handleMessage(data!); - } catch (ex) { - _printDebug('Message to "$name" caused exception $ex'); - } finally { - window._respondToPlatformMessage(responseId, null); - } - } else if (window.onPlatformMessage != null) { - _invoke3( - window.onPlatformMessage, - window._onPlatformMessageZone, - name, - data, - (ByteData? responseData) { - window._respondToPlatformMessage(responseId, responseData); - }, - ); - } else { - channelBuffers.push(name, data, (ByteData? responseData) { - window._respondToPlatformMessage(responseId, responseData); - }); - } + PlatformDispatcher.instance._dispatchPlatformMessage(name, data, responseId); } @pragma('vm:entry-point') // ignore: unused_element void _dispatchPointerDataPacket(ByteData packet) { - if (window.onPointerDataPacket != null) - _invoke1(window.onPointerDataPacket, window._onPointerDataPacketZone, _unpackPointerDataPacket(packet)); + PlatformDispatcher.instance._dispatchPointerDataPacket(packet); +} + +@pragma('vm:entry-point') +// ignore: unused_element +void _dispatchKeyData(ByteData packet, int responseId) { + PlatformDispatcher.instance._dispatchKeyData(packet, responseId); } @pragma('vm:entry-point') // ignore: unused_element void _dispatchSemanticsAction(int id, int action, ByteData? args) { - _invoke3( - window.onSemanticsAction, - window._onSemanticsActionZone, - id, - SemanticsAction.values[action]!, - args, - ); + PlatformDispatcher.instance._dispatchSemanticsAction(id, action, args); } @pragma('vm:entry-point') // ignore: unused_element -void _beginFrame(int microseconds) { - _invoke1(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds)); +void _beginFrame(int microseconds, int frameNumber) { + PlatformDispatcher.instance._beginFrame(microseconds); + PlatformDispatcher.instance._updateFrameData(frameNumber); } @pragma('vm:entry-point') // ignore: unused_element void _reportTimings(List timings) { - assert(timings.length % FramePhase.values.length == 0); - final List frameTimings = []; - for (int i = 0; i < timings.length; i += FramePhase.values.length) { - frameTimings.add(FrameTiming._(timings.sublist(i, i + FramePhase.values.length))); - } - _invoke1(window.onReportTimings, window._onReportTimingsZone, frameTimings); + PlatformDispatcher.instance._reportTimings(timings); } @pragma('vm:entry-point') // ignore: unused_element void _drawFrame() { - _invoke(window.onDrawFrame, window._onDrawFrameZone); + PlatformDispatcher.instance._drawFrame(); } // ignore: always_declare_return_types, prefer_generic_function_type_aliases -typedef _UnaryFunction(Null args); -// ignore: always_declare_return_types, prefer_generic_function_type_aliases -typedef _BinaryFunction(Null args, Null message); +typedef _ListStringArgFunction(List args); @pragma('vm:entry-point') // ignore: unused_element void _runMainZoned(Function startMainIsolateFunction, Function userMainFunction, List args) { - startMainIsolateFunction((){ + startMainIsolateFunction(() { runZonedGuarded(() { - if (userMainFunction is _BinaryFunction) { - // This seems to be undocumented but supported by the command line VM. - // Let's do the same in case old entry-points are ported to Flutter. - (userMainFunction as dynamic)(args, ''); - } else if (userMainFunction is _UnaryFunction) { + if (userMainFunction is _ListStringArgFunction) { (userMainFunction as dynamic)(args); } else { userMainFunction(); @@ -239,11 +153,12 @@ void _runMainZoned(Function startMainIsolateFunction, void _reportUnhandledException(String error, String stackTrace) native 'PlatformConfiguration_reportUnhandledException'; /// Invokes [callback] inside the given [zone]. -void _invoke(void callback()?, Zone zone) { - if (callback == null) +void _invoke(void Function()? callback, Zone zone) { + if (callback == null) { return; + } - assert(zone != null); // ignore: unnecessary_null_comparison + assert(zone != null); if (identical(zone, Zone.current)) { callback(); @@ -253,11 +168,16 @@ void _invoke(void callback()?, Zone zone) { } /// Invokes [callback] inside the given [zone] passing it [arg]. -void _invoke1(void callback(A a)?, Zone zone, A arg) { - if (callback == null) +/// +/// The 1 in the name refers to the number of arguments expected by +/// the callback (and thus passed to this function, in addition to the +/// callback itself and the zone in which the callback is executed). +void _invoke1(void Function(A a)? callback, Zone zone, A arg) { + if (callback == null) { return; + } - assert(zone != null); // ignore: unnecessary_null_comparison + assert(zone != null); if (identical(zone, Zone.current)) { callback(arg); @@ -266,12 +186,38 @@ void _invoke1(void callback(A a)?, Zone zone, A arg) { } } +/// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. +/// +/// The 2 in the name refers to the number of arguments expected by +/// the callback (and thus passed to this function, in addition to the +/// callback itself and the zone in which the callback is executed). +void _invoke2(void Function(A1 a1, A2 a2)? callback, Zone zone, A1 arg1, A2 arg2) { + if (callback == null) { + return; + } + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2); + } else { + zone.runGuarded(() { + callback(arg1, arg2); + }); + } +} + /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void _invoke3(void callback(A1 a1, A2 a2, A3 a3)?, Zone zone, A1 arg1, A2 arg2, A3 arg3) { - if (callback == null) +/// +/// The 3 in the name refers to the number of arguments expected by +/// the callback (and thus passed to this function, in addition to the +/// callback itself and the zone in which the callback is executed). +void _invoke3(void Function(A1 a1, A2 a2, A3 a3)? callback, Zone zone, A1 arg1, A2 arg2, A3 arg3) { + if (callback == null) { return; + } - assert(zone != null); // ignore: unnecessary_null_comparison + assert(zone != null); if (identical(zone, Zone.current)) { callback(arg1, arg2, arg3); @@ -282,53 +228,43 @@ void _invoke3(void callback(A1 a1, A2 a2, A3 a3)?, Zone zone, A1 arg } } -// If this value changes, update the encoding code in the following files: -// -// * pointer_data.cc -// * pointer.dart -// * AndroidTouchProcessor.java -const int _kPointerDataFieldCount = 29; - -PointerDataPacket _unpackPointerDataPacket(ByteData packet) { - const int kStride = Int64List.bytesPerElement; - const int kBytesPerPointerData = _kPointerDataFieldCount * kStride; - final int length = packet.lengthInBytes ~/ kBytesPerPointerData; - assert(length * kBytesPerPointerData == packet.lengthInBytes); - final List data = []; - for (int i = 0; i < length; ++i) { - int offset = i * _kPointerDataFieldCount; - data.add(PointerData( - embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian), - timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)), - change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], - kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], - signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], - device: packet.getInt64(kStride * offset++, _kFakeHostEndian), - pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian), - physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), - obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, - synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, - pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - size: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), - scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian) - )); - assert(offset == (i + 1) * _kPointerDataFieldCount); +bool _isLoopback(String host) { + if (host.isEmpty) { + return false; } - return PointerDataPacket(data: data); + if ('localhost' == host) { + return true; + } + try { + return InternetAddress(host).isLoopback; + } on ArgumentError { + return false; + } +} + +/// Loopback connections are always allowed. +/// Zone override with 'flutter.io.allow_http' takes first priority. +/// If zone override is not provided, engine setting is checked. +@pragma('vm:entry-point') +// ignore: unused_element +void Function(Uri) _getHttpConnectionHookClosure(bool mayInsecurelyConnectToAllDomains) { + return (Uri uri) { + if (_isLoopback(uri.host)) { + return; + } + final dynamic zoneOverride = Zone.current[#flutter.io.allow_http]; + if (zoneOverride == true) { + return; + } + if (zoneOverride == false && uri.isScheme('http')) { + // Going to throw + } else if (mayInsecurelyConnectToAllDomains || uri.isScheme('https')) { + // In absence of zone override, if engine setting allows the connection + // or if connection is to `https`, allow the connection. + return; + } + throw UnsupportedError( + 'Non-https connection "$uri" is not supported by the platform. ' + 'Refer to https://flutter.dev/docs/release/breaking-changes/network-policy-ios-android.'); + }; } diff --git a/lib/ui/hooks_unittests.cc b/lib/ui/hooks_unittests.cc new file mode 100644 index 0000000000000..6cee7e6f3c286 --- /dev/null +++ b/lib/ui/hooks_unittests.cc @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/testing/testing.h" +#include "third_party/dart/runtime/include/dart_api.h" + +namespace flutter { +namespace testing { + +using HooksTest = ShellTest; + +#define CHECK_DART_ERROR(name) \ + EXPECT_FALSE(Dart_IsError(name)) << Dart_GetError(name) + +TEST_F(HooksTest, HooksUnitTests) { + auto settings = CreateSettingsForFixture(); + + TaskRunners task_runners(GetCurrentTestName(), // label + GetCurrentTaskRunner(), // platform + CreateNewThread("raster"), // raster + CreateNewThread("ui"), // ui + CreateNewThread("io") // io + ); + + auto message_latch = std::make_shared(); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + ASSERT_TRUE(shell->IsSetup()); + + auto call_hook = [](Dart_NativeArguments args) { + Dart_Handle hook_name = Dart_GetNativeArgument(args, 0); + CHECK_DART_ERROR(hook_name); + + Dart_Handle ui_library = Dart_LookupLibrary(tonic::ToDart("dart:ui")); + CHECK_DART_ERROR(ui_library); + + Dart_Handle hook = Dart_GetField(ui_library, hook_name); + CHECK_DART_ERROR(hook); + + Dart_Handle arg_count_handle = Dart_GetNativeArgument(args, 1); + CHECK_DART_ERROR(arg_count_handle); + + int64_t arg_count; + Dart_IntegerToInt64(arg_count_handle, &arg_count); + + Dart_Handle hook_args[arg_count]; + for (int i = 0; i < static_cast(arg_count); i++) { + hook_args[i] = Dart_GetNativeArgument(args, 2 + i); + CHECK_DART_ERROR(hook_args[i]); + } + + Dart_Handle hook_result = + Dart_InvokeClosure(hook, static_cast(arg_count), hook_args); + CHECK_DART_ERROR(hook_result); + }; + + auto finished = [&message_latch](Dart_NativeArguments args) { + message_latch->Signal(); + }; + AddNativeCallback("CallHook", CREATE_NATIVE_ENTRY(call_hook)); + AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(finished)); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("hooksTests"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/io_manager.h b/lib/ui/io_manager.h index f799a841bfa4f..3118f35721442 100644 --- a/lib/ui/io_manager.h +++ b/lib/ui/io_manager.h @@ -24,7 +24,8 @@ class IOManager { virtual fml::RefPtr GetSkiaUnrefQueue() const = 0; - virtual std::shared_ptr GetIsGpuDisabledSyncSwitch() = 0; + virtual std::shared_ptr + GetIsGpuDisabledSyncSwitch() = 0; }; } // namespace flutter diff --git a/lib/ui/isolate_name_server.dart b/lib/ui/isolate_name_server.dart index 4b5c2c84d016c..c2ef18b3a5830 100644 --- a/lib/ui/isolate_name_server.dart +++ b/lib/ui/isolate_name_server.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// Static methods to allow for simple sharing of [SendPort]s across [Isolate]s. @@ -33,7 +33,7 @@ class IsolateNameServer { /// /// The `name` argument must not be null. static SendPort? lookupPortByName(String name) { - assert(name != null, "'name' cannot be null."); // ignore: unnecessary_null_comparison + assert(name != null, "'name' cannot be null."); return _lookupPortByName(name); } @@ -51,8 +51,8 @@ class IsolateNameServer { /// /// The `port` and `name` arguments must not be null. static bool registerPortWithName(SendPort port, String name) { - assert(port != null, "'port' cannot be null."); // ignore: unnecessary_null_comparison - assert(name != null, "'name' cannot be null."); // ignore: unnecessary_null_comparison + assert(port != null, "'port' cannot be null."); + assert(name != null, "'name' cannot be null."); return _registerPortWithName(port, name); } @@ -68,7 +68,7 @@ class IsolateNameServer { /// /// The `name` argument must not be null. static bool removePortNameMapping(String name) { - assert(name != null, "'name' cannot be null."); // ignore: unnecessary_null_comparison + assert(name != null, "'name' cannot be null."); return _removePortNameMapping(name); } diff --git a/lib/ui/key.dart b/lib/ui/key.dart new file mode 100644 index 0000000000000..f3ced0774ac2a --- /dev/null +++ b/lib/ui/key.dart @@ -0,0 +1,201 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.12 + +part of dart.ui; + +/// The type of a key event. +// Must match the KeyEventType enum in ui/window/key_data.h. +enum KeyEventType { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about a key event. +class KeyData { + /// Creates an object that represents a key event. + const KeyData({ + required this.timeStamp, + required this.type, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For synthesized events, the [timeStamp] might not be the actual time that + /// the key press or release happens. + final Duration timeStamp; + + /// The type of the event. + final KeyEventType type; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Ignored for up events. + final String? character; + + /// If [synthesized] is true, this event does not correspond to a native event. + /// + /// Although most of Flutter's keyboard events are transformed from native + /// events, some events are not based on native events, and are synthesized + /// only to conform Flutter's key event model (as documented in + /// the `HardwareKeyboard` class in the framework). + /// + /// For example, some key downs or ups might be lost when the window loses + /// focus. Some platforms provides ways to query whether a key is being held. + /// If the embedder detects an inconsistency between its internal record and + /// the state returned by the system, the embedder will synthesize a + /// corresponding event to synchronize the state without breaking the event + /// model. + /// + /// As another example, macOS treats CapsLock in a special way by sending + /// down and up events at the down of alterate presses to indicate the + /// direction in which the lock is toggled instead of that the physical key is + /// going. A macOS embedder should normalize the behavior by converting a + /// native down event into a down event followed immediately by a synthesized + /// up event, and the native up event also into a down event followed + /// immediately by a synthesized up event. + /// + /// Synthesized events do not have a trustworthy [timeStamp], and should not be + /// processed as if the key actually went down or up at the time of the + /// callback. + /// + /// [KeyRepeatEvent] is never synthesized. + final bool synthesized; + + // Returns the bits that are not included in [valueMask], shifted to the + // right. + // + // For example, if the input is 0x12abcdabcd, then the result is 0x12. + // + // This is mostly equivalent to a right shift, resolving the problem that + // JavaScript only support 32-bit bitwise operations and needs to use division + // instead. + static int _nonValueBits(int n) { + const int valueMask = 0x000FFFFFFFF; + // `n >> valueMaskWidth` is equivalent to `n / divisorForValueMask`. + const int divisorForValueMask = valueMask + 1; + const int valueMaskWidth = 32; + + // Equivalent to assert(divisorForValueMask == (1 << valueMaskWidth)). + const int _firstDivisorWidth = 28; + assert(divisorForValueMask == + (1 << _firstDivisorWidth) * (1 << (valueMaskWidth - _firstDivisorWidth))); + + // JS only supports up to 2^53 - 1, therefore non-value bits can only + // contain (maxSafeIntegerWidth - valueMaskWidth) bits. + const int maxSafeIntegerWidth = 52; + const int nonValueMask = (1 << (maxSafeIntegerWidth - valueMaskWidth)) - 1; + + if (identical(0, 0.0)) { // Detects if we are on the web. + return (n / divisorForValueMask).floor() & nonValueMask; + } else { + return (n >> valueMaskWidth) & nonValueMask; + } + } + + String _logicalToString() { + final String result = '0x${logical.toRadixString(16)}'; + final int planeNum = _nonValueBits(logical) & 0x0FF; + final String planeDescription = (() { + switch (planeNum) { + case 0x000: + return ' (Unicode)'; + case 0x001: + return ' (Unprintable)'; + case 0x002: + return ' (Flutter)'; + case 0x011: + return ' (Android)'; + case 0x012: + return ' (Fuchsia)'; + case 0x013: + return ' (iOS)'; + case 0x014: + return ' (macOS)'; + case 0x015: + return ' (GTK)'; + case 0x016: + return ' (Windows)'; + case 0x017: + return ' (Web)'; + case 0x018: + return ' (GLFW)'; + } + return ''; + })(); + return '$result$planeDescription'; + } + + String? _escapeCharacter() { + if (character == null) { + return character ?? ''; + } + switch (character!) { + case '\n': + return r'"\n"'; + case '\t': + return r'"\t"'; + case '\r': + return r'"\r"'; + case '\b': + return r'"\b"'; + case '\f': + return r'"\f"'; + default: + return '"$character"'; + } + } + + String? _quotedCharCode() { + if (character == null) + return ''; + final Iterable hexChars = character!.codeUnits + .map((int code) => code.toRadixString(16).padLeft(2, '0')); + return ' (0x${hexChars.join(' ')})'; + } + + @override + String toString() => 'KeyData(key ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' + 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${_quotedCharCode()}${synthesized ? ', synthesized' : ''})'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + 'type: ${_typeToString(type)}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: ${_escapeCharacter()}, ' + 'synthesized: $synthesized' + ')'; + } + + static String _typeToString(KeyEventType type) { + switch (type) { + case KeyEventType.up: + return 'up'; + case KeyEventType.down: + return 'down'; + case KeyEventType.repeat: + return 'repeat'; + } + } +} diff --git a/lib/ui/lerp.dart b/lib/ui/lerp.dart index 9c219dbb4c543..96565023b215a 100644 --- a/lib/ui/lerp.dart +++ b/lib/ui/lerp.dart @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// Linearly interpolate between two numbers, `a` and `b`, by an extrapolation /// factor `t`. /// -/// When `a` and `b` are equal or both NaN, `a` is returned. Otherwise, if +/// When `a` and `b` are equal or both NaN, `a` is returned. Otherwise, /// `a`, `b`, and `t` are required to be finite or null, and the result of `a + /// (b - a) * t` is returned, where nulls are defaulted to 0.0. double? lerpDouble(num? a, num? b, double t) { @@ -34,7 +34,7 @@ double _lerpDouble(double a, double b, double t) { /// /// Same as [lerpDouble] but specialized for non-null `int` type. double _lerpInt(int a, int b, double t) { - return a * (1.0 - t) + b * t; + return a + (b - a) * t; } /// Same as [num.clamp] but specialized for non-null [int]. diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index 0f2939592add1..20b0145f5fccb 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -4,8 +4,8 @@ // TODO(dnfield): remove unused_element ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; // Corelib 'print' implementation. @@ -34,7 +34,7 @@ Future _scheduleFrame( Map parameters ) async { // Schedule the frame. - window.scheduleFrame(); + PlatformDispatcher.instance.scheduleFrame(); // Always succeed. return developer.ServiceExtensionResponse.result(json.encode({ 'type': 'Success', @@ -70,15 +70,10 @@ void _setupHooks() { // ignore: unused_element /// /// This function is only effective in debug and dynamic modes, and will throw in AOT mode. List saveCompilationTrace() { - final dynamic result = _saveCompilationTrace(); - if (result is Error) - throw result; - return result as List; + throw UnimplementedError(); } -dynamic _saveCompilationTrace() native 'SaveCompilationTrace'; - -void _scheduleMicrotask(void callback()) native 'ScheduleMicrotask'; +void _scheduleMicrotask(void Function() callback) native 'ScheduleMicrotask'; int? _getCallbackHandle(Function closure) native 'GetCallbackHandle'; Function? _getCallbackFromHandle(int handle) native 'GetCallbackFromHandle'; diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index a8ebd7a632004..086b2df63e5c5 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; // Some methods in this file assert that their arguments are not null. These @@ -19,6 +19,15 @@ part of dart.ui; // which can not be rendered by Skia. // Update this list when changing the list of supported codecs. +/// {@template dart.ui.imageFormats} +/// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP. Additional +/// formats may be supported by the underlying platform. Flutter will +/// attempt to call platform API to decode unrecognized formats, and if the +/// platform API supports decoding the image Flutter will be able to render it. +/// {@endtemplate} + +// TODO(gspencergoog): remove this template block once the framework templates +// are renamed to not reference it. /// {@template flutter.dart:ui.imageFormats} /// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP. Additional /// formats may be supported by the underlying platform. Flutter will @@ -27,32 +36,32 @@ part of dart.ui; /// {@endtemplate} bool _rectIsValid(Rect rect) { - assert(rect != null, 'Rect argument was null.'); // ignore: unnecessary_null_comparison + assert(rect != null, 'Rect argument was null.'); assert(!rect.hasNaN, 'Rect argument contained a NaN value.'); return true; } bool _rrectIsValid(RRect rrect) { - assert(rrect != null, 'RRect argument was null.'); // ignore: unnecessary_null_comparison + assert(rrect != null, 'RRect argument was null.'); assert(!rrect.hasNaN, 'RRect argument contained a NaN value.'); return true; } bool _offsetIsValid(Offset offset) { - assert(offset != null, 'Offset argument was null.'); // ignore: unnecessary_null_comparison + assert(offset != null, 'Offset argument was null.'); assert(!offset.dx.isNaN && !offset.dy.isNaN, 'Offset argument contained a NaN value.'); return true; } bool _matrix4IsValid(Float64List matrix4) { - assert(matrix4 != null, 'Matrix4 argument was null.'); // ignore: unnecessary_null_comparison + assert(matrix4 != null, 'Matrix4 argument was null.'); assert(matrix4.length == 16, 'Matrix4 must have 16 entries.'); assert(matrix4.every((double value) => value.isFinite), 'Matrix4 entries must be finite.'); return true; } bool _radiusIsValid(Radius radius) { - assert(radius != null, 'Radius argument was null.'); // ignore: unnecessary_null_comparison + assert(radius != null, 'Radius argument was null.'); assert(!radius.x.isNaN && !radius.y.isNaN, 'Radius argument contained a NaN value.'); return true; } @@ -89,7 +98,7 @@ Color _scaleAlpha(Color a, double factor) { /// /// See also: /// -/// * [Colors](https://docs.flutter.io/flutter/material/Colors-class.html), which +/// * [Colors](https://api.flutter.dev/flutter/material/Colors-class.html), which /// defines the colors found in the Material Design specification. class Color { /// Construct a color from the lower 32 bits of an [int]. @@ -262,7 +271,7 @@ class Color { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static Color? lerp(Color? a, Color? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; @@ -322,7 +331,7 @@ class Color { /// /// The [opacity] value may not be null. static int getAlphaFromOpacity(double opacity) { - assert(opacity != null); // ignore: unnecessary_null_comparison + assert(opacity != null); return (opacity.clamp(0.0, 1.0) * 255).round(); } @@ -814,32 +823,81 @@ enum BlendMode { luminosity, } -/// Quality levels for image filters. +/// Quality levels for image sampling in [ImageFilter] and [Shader] objects that sample +/// images and for [Canvas] operations that render images. +/// +/// When scaling up typically the quality is lowest at [none], higher at [low] and [medium], +/// and for very large scale factors (over 10x) the highest at [high]. +/// +/// When scaling down, [medium] provides the best quality especially when scaling an +/// image to less than half its size or for animating the scale factor between such +/// reductions. Otherwise, [low] and [high] provide similar effects for reductions of +/// between 50% and 100% but the image may lose detail and have dropouts below 50%. +/// +/// To get high quality when scaling images up and down, or when the scale is +/// unknown, [medium] is typically a good balanced choice. +/// +/// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/filter_quality.png) /// -/// See [Paint.filterQuality]. +/// When building for the web using the `--web-renderer=html` option, filter +/// quality has no effect. All images are rendered using the respective +/// browser's default setting. +/// +/// See also: +/// +/// * [Paint.filterQuality], which is used to pass [FilterQuality] to the +/// engine while using drawImage calls on a [Canvas]. +/// * [ImageShader]. +/// * [ImageFilter.matrix]. +/// * [Canvas.drawImage]. +/// * [Canvas.drawImageRect]. +/// * [Canvas.drawImageNine]. +/// * [Canvas.drawAtlas]. enum FilterQuality { - // This list comes from Skia's SkFilterQuality.h and the values (order) should - // be kept in sync. + // This list and the values (order) should be kept in sync with the equivalent list + // in lib/ui/painting/image_filter.cc - /// Fastest possible filtering, albeit also the lowest quality. + /// The fastest filtering method, albeit also the lowest quality. /// - /// Typically this implies nearest-neighbor filtering. + /// This value results in a "Nearest Neighbor" algorithm which just + /// repeats or eliminates pixels as an image is scaled up or down. none, /// Better quality than [none], faster than [medium]. /// - /// Typically this implies bilinear interpolation. + /// This value results in a "Bilinear" algorithm which smoothly + /// interpolates between pixels in an image. low, - /// Better quality than [low], faster than [high]. + /// The best all around filtering method that is only worse than [high] + /// at extremely large scale factors. /// - /// Typically this implies a combination of bilinear interpolation and - /// pyramidal parametric pre-filtering (mipmaps). + /// This value improves upon the "Bilinear" algorithm specified by [low] + /// by utilizing a Mipmap that pre-computes high quality lower resolutions + /// of the image at half (and quarter and eighth, etc.) sizes and then + /// blends between those to prevent loss of detail at small scale sizes. + /// + /// {@template dart.ui.filterQuality.seeAlso} + /// See also: + /// + /// * [FilterQuality] class-level documentation that goes into detail about + /// relative qualities of the constant values. + /// {@endtemplate} medium, - /// Best possible quality filtering, albeit also the slowest. + /// Best possible quality when scaling up images by scale factors larger than + /// 5-10x. + /// + /// When images are scaled down, this can be worse than [medium] for scales + /// smaller than 0.5x, or when animating the scale factor. + /// + /// This option is also the slowest. /// - /// Typically this implies bicubic interpolation or better. + /// This value results in a standard "Bicubic" algorithm which uses a 3rd order + /// equation to smooth the abrupt transitions between pixels while preserving + /// some of the sense of an edge and avoiding sharp peaks in the result. + /// + /// {@macro dart.ui.filterQuality.seeAlso} high, } @@ -1144,7 +1202,7 @@ class Paint { return Color(encoded ^ _kColorDefault); } set color(Color value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final int encoded = value.value ^ _kColorDefault; _data.setInt32(_kColorOffset, encoded, _kFakeHostEndian); } @@ -1175,7 +1233,7 @@ class Paint { return BlendMode.values[encoded ^ _kBlendModeDefault]; } set blendMode(BlendMode value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final int encoded = value.index ^ _kBlendModeDefault; _data.setInt32(_kBlendModeOffset, encoded, _kFakeHostEndian); } @@ -1187,7 +1245,7 @@ class Paint { return PaintingStyle.values[_data.getInt32(_kStyleOffset, _kFakeHostEndian)]; } set style(PaintingStyle value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final int encoded = value.index; _data.setInt32(_kStyleOffset, encoded, _kFakeHostEndian); } @@ -1201,7 +1259,7 @@ class Paint { return _data.getFloat32(_kStrokeWidthOffset, _kFakeHostEndian); } set strokeWidth(double value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final double encoded = value; _data.setFloat32(_kStrokeWidthOffset, encoded, _kFakeHostEndian); } @@ -1214,7 +1272,7 @@ class Paint { return StrokeCap.values[_data.getInt32(_kStrokeCapOffset, _kFakeHostEndian)]; } set strokeCap(StrokeCap value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final int encoded = value.index; _data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian); } @@ -1248,7 +1306,7 @@ class Paint { return StrokeJoin.values[_data.getInt32(_kStrokeJoinOffset, _kFakeHostEndian)]; } set strokeJoin(StrokeJoin value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final int encoded = value.index; _data.setInt32(_kStrokeJoinOffset, encoded, _kFakeHostEndian); } @@ -1286,7 +1344,7 @@ class Paint { return _data.getFloat32(_kStrokeMiterLimitOffset, _kFakeHostEndian); } set strokeMiterLimit(double value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final double encoded = value - _kStrokeMiterLimitDefault; _data.setFloat32(_kStrokeMiterLimitOffset, encoded, _kFakeHostEndian); } @@ -1321,9 +1379,9 @@ class Paint { } } - /// Controls the performance vs quality trade-off to use when applying - /// filters, such as [maskFilter], or when drawing images, as with - /// [Canvas.drawImageRect] or [Canvas.drawImageNine]. + /// Controls the performance vs quality trade-off to use when sampling bitmaps, + /// as with an [ImageShader], or when drawing images, as with [Canvas.drawImage], + /// [Canvas.drawImageRect], [Canvas.drawImageNine] or [Canvas.drawAtlas]. /// /// Defaults to [FilterQuality.none]. // TODO(ianh): verify that the image drawing methods actually respect this @@ -1331,7 +1389,7 @@ class Paint { return FilterQuality.values[_data.getInt32(_kFilterQualityOffset, _kFakeHostEndian)]; } set filterQuality(FilterQuality value) { - assert(value != null); // ignore: unnecessary_null_comparison + assert(value != null); final int encoded = value.index; _data.setInt32(_kFilterQualityOffset, encoded, _kFakeHostEndian); } @@ -1568,7 +1626,7 @@ enum PixelFormat { /// /// A class or method that receives an image object must call [dispose] on the /// handle when it is no longer needed. To create a shareable reference to the -/// underlying image, call [clone]. The method or object that recieves +/// underlying image, call [clone]. The method or object that receives /// the new instance will then be responsible for disposing it, and the /// underlying image itself will be disposed when all outstanding handles are /// disposed. @@ -1632,6 +1690,19 @@ class Image { } } + /// Whether this reference to the underlying image is [dispose]d. + /// + /// This only returns a valid value if asserts are enabled, and must not be + /// used otherwise. + bool get debugDisposed { + bool? disposed; + assert(() { + disposed = _disposed; + return true; + }()); + return disposed ?? (throw StateError('Image.debugDisposed is only available when asserts are enabled.')); + } + /// Converts the [Image] object into a byte array. /// /// The [format] argument specifies the format in which the bytes will be @@ -1671,7 +1742,7 @@ class Image { /// It is safe to pass an [Image] handle to another object or method if the /// current holder no longer needs it. /// - /// To check whether two [Image] references are refering to the same + /// To check whether two [Image] references are referring to the same /// underlying image memory, use [isCloneOf] rather than the equality operator /// or [identical]. /// @@ -1760,7 +1831,7 @@ class Image { } @pragma('vm:entry-point') -class _Image extends NativeFieldWrapperClass2 { +class _Image extends NativeFieldWrapperClass1 { // This class is created by the engine, and should not be instantiated // or extended directly. // @@ -1882,7 +1953,7 @@ class FrameInfo { /// To obtain an instance of the [Codec] interface, see /// [instantiateImageCodec]. @pragma('vm:entry-point') -class Codec extends NativeFieldWrapperClass2 { +class Codec extends NativeFieldWrapperClass1 { // // This class is created by the engine, and should not be instantiated // or extended directly. @@ -1917,17 +1988,18 @@ class Codec extends NativeFieldWrapperClass2 { final Completer completer = Completer.sync(); final String? error = _getNextFrame((_Image? image, int durationMilliseconds) { if (image == null) { - throw Exception('Codec failed to produce an image, possibly due to invalid image data.'); + completer.completeError(Exception('Codec failed to produce an image, possibly due to invalid image data.')); + } else { + completer.complete(FrameInfo._( + image: Image._(image), + duration: Duration(milliseconds: durationMilliseconds), + )); } - completer.complete(FrameInfo._( - image: Image._(image), - duration: Duration(milliseconds: durationMilliseconds), - )); }); if (error != null) { throw Exception(error); } - return await completer.future; + return completer.future; } /// Returns an error message on failure, null on success. @@ -1947,7 +2019,7 @@ class Codec extends NativeFieldWrapperClass2 { /// /// The `list` parameter is the binary image data (e.g a PNG or GIF binary data). /// The data can be for either static or animated images. The following image -/// formats are supported: {@macro flutter.dart:ui.imageFormats} +/// formats are supported: {@macro dart.ui.imageFormats} /// /// The `targetWidth` and `targetHeight` arguments specify the size of the /// output image, in image pixels. If they are not equal to the intrinsic @@ -1982,6 +2054,7 @@ Future instantiateImageCodec( targetHeight = descriptor.height; } } + buffer.dispose(); return descriptor.instantiateCodec( targetWidth: targetWidth, targetHeight: targetHeight, @@ -2069,8 +2142,17 @@ void decodeImageFromPixels( targetWidth: targetWidth, targetHeight: targetHeight, ) - .then((Codec codec) => codec.getNextFrame()) - .then((FrameInfo frameInfo) => callback(frameInfo.image)); + .then((Codec codec) { + final Future frameInfo = codec.getNextFrame(); + codec.dispose(); + return frameInfo; + }) + .then((FrameInfo frameInfo) { + buffer.dispose(); + descriptor.dispose(); + + return callback(frameInfo.image); + }); }); } @@ -2156,11 +2238,25 @@ enum PathOperation { /// A handle for the framework to hold and retain an engine layer across frames. @pragma('vm:entry-point') -class EngineLayer extends NativeFieldWrapperClass2 { +class EngineLayer extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @pragma('vm:entry-point') EngineLayer._(); + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + /// + /// EngineLayers indirectly retain platform specific graphics resources. Some + /// of these resources, such as images, may be memory intensive. It is + /// important to dispose of EngineLayer objects that will no longer be used as + /// soon as possible to avoid retaining these resources until the next + /// garbage collection. + /// + /// Once this EngineLayer is disposed, it is no longer eligible for use as a + /// retained layer, and must not be passed as an `oldLayer` to any of the + /// [SceneBuilder] methods which accept that parameter. + void dispose() native 'EngineLayer_dispose'; } /// A complex, one-dimensional subset of a plane. @@ -2181,7 +2277,7 @@ class EngineLayer extends NativeFieldWrapperClass2 { /// Paths can be drawn on canvases using [Canvas.drawPath], and can /// used to create clip regions using [Canvas.clipPath]. @pragma('vm:entry-point') -class Path extends NativeFieldWrapperClass2 { +class Path extends NativeFieldWrapperClass1 { /// Create a new empty [Path] object. @pragma('vm:entry-point') Path() { _constructor(); } @@ -2385,7 +2481,7 @@ class Path extends NativeFieldWrapperClass2 { /// /// The `points` argument is interpreted as offsets from the origin. void addPolygon(List points, bool close) { - assert(points != null); // ignore: unnecessary_null_comparison + assert(points != null); _addPolygon(_encodePointList(points), close); } void _addPolygon(Float32List points, bool close) native 'Path_addPolygon'; @@ -2399,14 +2495,12 @@ class Path extends NativeFieldWrapperClass2 { } void _addRRect(Float32List rrect) native 'Path_addRRect'; - /// Adds a new sub-path that consists of the given `path` offset by the given - /// `offset`. + /// Adds the sub-paths of `path`, offset by `offset`, to this path. /// /// If `matrix4` is specified, the path will be transformed by this matrix /// after the matrix is translated by the given offset. The matrix is a 4x4 /// matrix stored in column major order. void addPath(Path path, Offset offset, {Float64List? matrix4}) { - // ignore: unnecessary_null_comparison assert(path != null); // path is checked on the engine side assert(_offsetIsValid(offset)); if (matrix4 != null) { @@ -2419,14 +2513,14 @@ class Path extends NativeFieldWrapperClass2 { void _addPath(Path path, double dx, double dy) native 'Path_addPath'; void _addPathWithMatrix(Path path, double dx, double dy, Float64List matrix) native 'Path_addPathWithMatrix'; - /// Adds the given path to this path by extending the current segment of this - /// path with the first segment of the given path. + /// Adds the sub-paths of `path`, offset by `offset`, to this path. + /// The current sub-path is extended with the first sub-path + /// of `path`, connecting them with a lineTo if necessary. /// /// If `matrix4` is specified, the path will be transformed by this matrix /// after the matrix is translated by the given `offset`. The matrix is a 4x4 /// matrix stored in column major order. void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) { - // ignore: unnecessary_null_comparison assert(path != null); // path is checked on the engine side assert(_offsetIsValid(offset)); if (matrix4 != null) { @@ -2509,8 +2603,8 @@ class Path extends NativeFieldWrapperClass2 { /// curve order is reduced where possible so that cubics may be turned into /// quadratics, and quadratics maybe turned into lines. static Path combine(PathOperation operation, Path path1, Path path2) { - assert(path1 != null); // ignore: unnecessary_null_comparison - assert(path2 != null); // ignore: unnecessary_null_comparison + assert(path1 != null); + assert(path2 != null); final Path path = Path(); if (path._op(path1, path2, operation.index)) { return path; @@ -2564,8 +2658,8 @@ class Tangent { /// /// The arguments must not be null. const Tangent(this.position, this.vector) - : assert(position != null), // ignore: unnecessary_null_comparison - assert(vector != null); // ignore: unnecessary_null_comparison + : assert(position != null), + assert(vector != null); /// Creates a [Tangent] based on the angle rather than the vector. /// @@ -2631,7 +2725,7 @@ class PathMetrics extends collection.IterableBase { /// Used by [PathMetrics] to track iteration from one segment of a path to the /// next for measurement. class PathMetricIterator implements Iterator { - PathMetricIterator._(this._pathMeasure) : assert(_pathMeasure != null); // ignore: unnecessary_null_comparison + PathMetricIterator._(this._pathMeasure) : assert(_pathMeasure != null); PathMetric? _pathMetric; _PathMeasure _pathMeasure; @@ -2642,7 +2736,7 @@ class PathMetricIterator implements Iterator { if (currentMetric == null) { throw RangeError( 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' - '- The iteration has not started yet. If so, call "moveNext" to start iteration.' + '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".' ); } @@ -2675,7 +2769,7 @@ class PathMetricIterator implements Iterator { /// the path. class PathMetric { PathMetric._(this._measure) - : assert(_measure != null), // ignore: unnecessary_null_comparison + : assert(_measure != null), length = _measure.length(_measure.currentContourIndex), isClosed = _measure.isClosed(_measure.currentContourIndex), contourIndex = _measure.currentContourIndex; @@ -2722,12 +2816,11 @@ class PathMetric { return _measure.getTangentForOffset(contourIndex, distance); } - /// Given a start and stop distance, return the intervening segment(s). + /// Given a start and end distance, return the intervening segment(s). /// /// `start` and `end` are clamped to legal values (0..[length]) - /// Returns null if the segment is 0 length or `start` > `stop`. /// Begin the segment with a moveTo if `startWithMoveTo` is true. - Path? extractPath(double start, double end, {bool startWithMoveTo = true}) { + Path extractPath(double start, double end, {bool startWithMoveTo = true}) { return _measure.extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo); } @@ -2735,7 +2828,7 @@ class PathMetric { String toString() => '$runtimeType{length: $length, isClosed: $isClosed, contourIndex:$contourIndex}'; } -class _PathMeasure extends NativeFieldWrapperClass2 { +class _PathMeasure extends NativeFieldWrapperClass1 { _PathMeasure(Path path, bool forceClosed) { _constructor(path, forceClosed); } @@ -2846,8 +2939,8 @@ class MaskFilter { const MaskFilter.blur( this._style, this._sigma, - ) : assert(_style != null), // ignore: unnecessary_null_comparison - assert(_sigma != null); // ignore: unnecessary_null_comparison + ) : assert(_style != null), + assert(_sigma != null); final BlurStyle _style; final double _sigma; @@ -2879,7 +2972,7 @@ class MaskFilter { /// /// Instances of this class are used with [Paint.colorFilter] on [Paint] /// objects. -class ColorFilter { +class ColorFilter implements ImageFilter { /// Creates a color filter that applies the blend mode given as the second /// argument. The source color is the one given as the first argument, and the /// destination color is the one from the layer being composited. @@ -2891,7 +2984,7 @@ class ColorFilter { : _color = color, _blendMode = blendMode, _matrix = null, - _type = _TypeMode; + _type = _kTypeMode; /// Construct a color filter that transforms a color by a 5x5 matrix, where /// the fifth row is implicitly added in an identity configuration. @@ -2957,7 +3050,7 @@ class ColorFilter { : _color = null, _blendMode = null, _matrix = matrix, - _type = _TypeMatrix; + _type = _kTypeMatrix; /// Construct a color filter that applies the sRGB gamma curve to the RGB /// channels. @@ -2965,7 +3058,7 @@ class ColorFilter { : _color = null, _blendMode = null, _matrix = null, - _type = _TypeLinearToSrgbGamma; + _type = _kTypeLinearToSrgbGamma; /// Creates a color filter that applies the inverse of the sRGB gamma curve /// to the RGB channels. @@ -2973,7 +3066,7 @@ class ColorFilter { : _color = null, _blendMode = null, _matrix = null, - _type = _TypeSrgbToLinearGamma; + _type = _kTypeSrgbToLinearGamma; final Color? _color; final BlendMode? _blendMode; @@ -2981,55 +3074,77 @@ class ColorFilter { final int _type; // The type of SkColorFilter class to create for Skia. - static const int _TypeMode = 1; // MakeModeFilter - static const int _TypeMatrix = 2; // MakeMatrixFilterRowMajor255 - static const int _TypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma - static const int _TypeSrgbToLinearGamma = 4; // MakeSRGBToLinearGamma + static const int _kTypeMode = 1; // MakeModeFilter + static const int _kTypeMatrix = 2; // MakeMatrixFilterRowMajor255 + static const int _kTypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma + static const int _kTypeSrgbToLinearGamma = 4; // MakeSRGBToLinearGamma + // SkImageFilters::ColorFilter @override - bool operator ==(Object other) { - return other is ColorFilter - && other._type == _type - && _listEquals(other._matrix, _matrix) - && other._color == _color - && other._blendMode == _blendMode; - } + _ImageFilter _toNativeImageFilter() => _ImageFilter.fromColorFilter(this); _ColorFilter? _toNativeColorFilter() { switch (_type) { - case _TypeMode: + case _kTypeMode: if (_color == null || _blendMode == null) { return null; } return _ColorFilter.mode(this); - case _TypeMatrix: + case _kTypeMatrix: if (_matrix == null) { return null; } assert(_matrix!.length == 20, 'Color Matrix must have 20 entries.'); return _ColorFilter.matrix(this); - case _TypeLinearToSrgbGamma: + case _kTypeLinearToSrgbGamma: return _ColorFilter.linearToSrgbGamma(this); - case _TypeSrgbToLinearGamma: + case _kTypeSrgbToLinearGamma: return _ColorFilter.srgbToLinearGamma(this); default: throw StateError('Unknown mode $_type for ColorFilter.'); } } + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is ColorFilter + && other._type == _type + && _listEquals(other._matrix, _matrix) + && other._color == _color + && other._blendMode == _blendMode; + } + @override int get hashCode => hashValues(_color, _blendMode, hashList(_matrix), _type); + @override + String get _shortDescription { + switch (_type) { + case _kTypeMode: + return 'ColorFilter.mode($_color, $_blendMode)'; + case _kTypeMatrix: + return 'ColorFilter.matrix($_matrix)'; + case _kTypeLinearToSrgbGamma: + return 'ColorFilter.linearToSrgbGamma()'; + case _kTypeSrgbToLinearGamma: + return 'ColorFilter.srgbToLinearGamma()'; + default: + return 'unknow ColorFilter'; + } + } + @override String toString() { switch (_type) { - case _TypeMode: + case _kTypeMode: return 'ColorFilter.mode($_color, $_blendMode)'; - case _TypeMatrix: + case _kTypeMatrix: return 'ColorFilter.matrix($_matrix)'; - case _TypeLinearToSrgbGamma: + case _kTypeLinearToSrgbGamma: return 'ColorFilter.linearToSrgbGamma()'; - case _TypeSrgbToLinearGamma: + case _kTypeSrgbToLinearGamma: return 'ColorFilter.srgbToLinearGamma()'; default: return 'Unknown ColorFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.'; @@ -3043,30 +3158,30 @@ class ColorFilter { /// ColorFilter, because we want ColorFilter to be const constructible and /// efficiently comparable, so that widgets can check for ColorFilter equality to /// avoid repainting. -class _ColorFilter extends NativeFieldWrapperClass2 { +class _ColorFilter extends NativeFieldWrapperClass1 { _ColorFilter.mode(this.creator) - : assert(creator != null), // ignore: unnecessary_null_comparison - assert(creator._type == ColorFilter._TypeMode) { + : assert(creator != null), + assert(creator._type == ColorFilter._kTypeMode) { _constructor(); _initMode(creator._color!.value, creator._blendMode!.index); } _ColorFilter.matrix(this.creator) - : assert(creator != null), // ignore: unnecessary_null_comparison - assert(creator._type == ColorFilter._TypeMatrix) { + : assert(creator != null), + assert(creator._type == ColorFilter._kTypeMatrix) { _constructor(); _initMatrix(Float32List.fromList(creator._matrix!)); } _ColorFilter.linearToSrgbGamma(this.creator) - : assert(creator != null), // ignore: unnecessary_null_comparison - assert(creator._type == ColorFilter._TypeLinearToSrgbGamma) { + : assert(creator != null), + assert(creator._type == ColorFilter._kTypeLinearToSrgbGamma) { _constructor(); _initLinearToSrgbGamma(); } _ColorFilter.srgbToLinearGamma(this.creator) - : assert(creator != null), // ignore: unnecessary_null_comparison - assert(creator._type == ColorFilter._TypeSrgbToLinearGamma) { + : assert(creator != null), + assert(creator._type == ColorFilter._kTypeSrgbToLinearGamma) { _constructor(); _initSrgbToLinearGamma(); } @@ -3092,80 +3207,146 @@ class _ColorFilter extends NativeFieldWrapperClass2 { /// this class as a backdrop filter. /// * [SceneBuilder.pushImageFilter], which is the low-level API for using /// this class as a child layer filter. -class ImageFilter { +abstract class ImageFilter { /// Creates an image filter that applies a Gaussian blur. - ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0 }) - : assert(sigmaX != null), // ignore: unnecessary_null_comparison - assert(sigmaY != null), // ignore: unnecessary_null_comparison - _data = _makeList(sigmaX, sigmaY), - _filterQuality = null, - _type = _kTypeBlur; + factory ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0, TileMode tileMode = TileMode.clamp }) { + assert(sigmaX != null); + assert(sigmaY != null); + assert(tileMode != null); + return _GaussianBlurImageFilter(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + } /// Creates an image filter that applies a matrix transformation. /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. - ImageFilter.matrix(Float64List matrix4, - { FilterQuality filterQuality = FilterQuality.low }) - : assert(matrix4 != null), // ignore: unnecessary_null_comparison - _data = Float64List.fromList(matrix4), - _filterQuality = filterQuality, - _type = _kTypeMatrix { + factory ImageFilter.matrix(Float64List matrix4, + { FilterQuality filterQuality = FilterQuality.low }) { + assert(matrix4 != null); + assert(filterQuality != null); if (matrix4.length != 16) throw ArgumentError('"matrix4" must have 16 entries.'); + return _MatrixImageFilter(data: Float64List.fromList(matrix4), filterQuality: filterQuality); } - static Float64List _makeList(double a, double b) { - final Float64List list = Float64List(2); - list[0] = a; - list[1] = b; - return list; + /// Composes the `inner` filter with `outer`, to combine their effects. + /// + /// Creates a single [ImageFilter] that when applied, has the same effect as + /// subsequently applying `inner` and `outer`, i.e., + /// result = outer(inner(source)). + factory ImageFilter.compose({ required ImageFilter outer, required ImageFilter inner }) { + assert (inner != null && outer != null); + return _ComposeImageFilter(innerFilter: inner, outerFilter: outer); } - final Float64List _data; - final FilterQuality? _filterQuality; - final int _type; - _ImageFilter? _nativeFilter; + // Converts this to a native SkImageFilter. See the comments of this method in + // subclasses for the exact type of SkImageFilter this method converts to. + _ImageFilter _toNativeImageFilter(); - // The type of SkImageFilter class to create for Skia. - static const int _kTypeBlur = 0; // MakeBlurFilter - static const int _kTypeMatrix = 1; // MakeMatrixFilterRowMajor255 + // The description text to show when the filter is part of a composite + // [ImageFilter] created using [ImageFilter.compose]. + String get _shortDescription; +} + +class _MatrixImageFilter implements ImageFilter { + _MatrixImageFilter({ required this.data, required this.filterQuality }); + + final Float64List data; + final FilterQuality filterQuality; + + // MakeMatrixFilterRowMajor255 + late final _ImageFilter nativeFilter = _ImageFilter.matrix(this); + @override + _ImageFilter _toNativeImageFilter() => nativeFilter; + + @override + String get _shortDescription => 'matrix($data, $filterQuality)'; + + @override + String toString() => 'ImageFilter.matrix($data, $filterQuality)'; @override bool operator ==(Object other) { - return other is ImageFilter - && other._type == _type - && _listEquals(other._data, _data) - && other._filterQuality == _filterQuality; + if (other.runtimeType != runtimeType) + return false; + return other is _MatrixImageFilter + && other.filterQuality == filterQuality + && _listEquals(other.data, data); } - _ImageFilter _toNativeImageFilter() => _nativeFilter ??= _makeNativeImageFilter(); + @override + int get hashCode => hashValues(filterQuality, hashList(data)); +} - _ImageFilter _makeNativeImageFilter() { - switch (_type) { - case _kTypeBlur: - return _ImageFilter.blur(this); - case _kTypeMatrix: - return _ImageFilter.matrix(this); - default: - throw StateError('Unknown mode $_type for ImageFilter.'); +class _GaussianBlurImageFilter implements ImageFilter { + _GaussianBlurImageFilter({ required this.sigmaX, required this.sigmaY, required this.tileMode }); + + final double sigmaX; + final double sigmaY; + final TileMode tileMode; + + // MakeBlurFilter + late final _ImageFilter nativeFilter = _ImageFilter.blur(this); + @override + _ImageFilter _toNativeImageFilter() => nativeFilter; + + String get _modeString { + switch(tileMode) { + case TileMode.clamp: return 'clamp'; + case TileMode.mirror: return 'mirror'; + case TileMode.repeated: return 'repeated'; + case TileMode.decal: return 'decal'; } } @override - int get hashCode => hashValues(_filterQuality, hashList(_data), _type); + String get _shortDescription => 'blur($sigmaX, $sigmaY, $_modeString)'; @override - String toString() { - switch (_type) { - case _kTypeBlur: - return 'ImageFilter.blur(${_data[0]}, ${_data[1]})'; - case _kTypeMatrix: - return 'ImageFilter.matrix($_data, $_filterQuality)'; - default: - return 'Unknown ImageFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.'; - } + String toString() => 'ImageFilter.blur($sigmaX, $sigmaY, $_modeString)'; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is _GaussianBlurImageFilter + && other.sigmaX == sigmaX + && other.sigmaY == sigmaY + && other.tileMode == tileMode; } + + @override + int get hashCode => hashValues(sigmaX, sigmaY); +} + +class _ComposeImageFilter implements ImageFilter { + _ComposeImageFilter({ required this.innerFilter, required this.outerFilter }); + + final ImageFilter innerFilter; + final ImageFilter outerFilter; + + // SkImageFilters::Compose + late final _ImageFilter nativeFilter = _ImageFilter.composed(this); + @override + _ImageFilter _toNativeImageFilter() => nativeFilter; + + @override + String get _shortDescription => '${innerFilter._shortDescription} -> ${outerFilter._shortDescription}'; + + @override + String toString() => 'ImageFilter.compose(source -> $_shortDescription -> result)'; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is _ComposeImageFilter + && other.innerFilter == innerFilter + && other.outerFilter == outerFilter; + } + + @override + int get hashCode => hashValues(innerFilter, outerFilter); } /// An [ImageFilter] that is backed by a native SkImageFilter. @@ -3173,32 +3354,52 @@ class ImageFilter { /// This is a private class, rather than being the implementation of the public /// ImageFilter, because we want ImageFilter to be efficiently comparable, so that /// widgets can check for ImageFilter equality to avoid repainting. -class _ImageFilter extends NativeFieldWrapperClass2 { +class _ImageFilter extends NativeFieldWrapperClass1 { void _constructor() native 'ImageFilter_constructor'; /// Creates an image filter that applies a Gaussian blur. - _ImageFilter.blur(this.creator) - : assert(creator != null), // ignore: unnecessary_null_comparison - assert(creator._type == ImageFilter._kTypeBlur) { + _ImageFilter.blur(_GaussianBlurImageFilter filter) + : assert(filter != null), + creator = filter { // ignore: prefer_initializing_formals _constructor(); - _initBlur(creator._data[0], creator._data[1]); + _initBlur(filter.sigmaX, filter.sigmaY, filter.tileMode.index); } - void _initBlur(double sigmaX, double sigmaY) native 'ImageFilter_initBlur'; + void _initBlur(double sigmaX, double sigmaY, int tileMode) native 'ImageFilter_initBlur'; /// Creates an image filter that applies a matrix transformation. /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. - _ImageFilter.matrix(this.creator) - : assert(creator != null), // ignore: unnecessary_null_comparison - assert(creator._type == ImageFilter._kTypeMatrix) { - if (creator._data.length != 16) + _ImageFilter.matrix(_MatrixImageFilter filter) + : assert(filter != null), + creator = filter { // ignore: prefer_initializing_formals + if (filter.data.length != 16) throw ArgumentError('"matrix4" must have 16 entries.'); _constructor(); - _initMatrix(creator._data, creator._filterQuality!.index); + _initMatrix(filter.data, filter.filterQuality.index); } void _initMatrix(Float64List matrix4, int filterQuality) native 'ImageFilter_initMatrix'; + /// Converts a color filter to an image filter. + _ImageFilter.fromColorFilter(ColorFilter filter) + : assert(filter != null), + creator = filter { // ignore: prefer_initializing_formals + _constructor(); + final _ColorFilter? nativeFilter = filter._toNativeColorFilter(); + _initColorFilter(nativeFilter); + } + void _initColorFilter(_ColorFilter? colorFilter) native 'ImageFilter_initColorFilter'; + + /// Composes `_innerFilter` with `_outerFilter`. + _ImageFilter.composed(_ComposeImageFilter filter) + : assert(filter != null), + creator = filter { // ignore: prefer_initializing_formals + _constructor(); + final _ImageFilter nativeFilterInner = filter.innerFilter._toNativeImageFilter(); + final _ImageFilter nativeFilterOuter = filter.outerFilter._toNativeImageFilter(); + _initComposed(nativeFilterOuter, nativeFilterInner); + } + void _initComposed(_ImageFilter outerFilter, _ImageFilter innerFilter) native 'ImageFilter_initComposeFilter'; /// The original Dart object that created the native wrapper, which retains /// the values used for the filter. final ImageFilter creator; @@ -3206,21 +3407,29 @@ class _ImageFilter extends NativeFieldWrapperClass2 { /// Base class for objects such as [Gradient] and [ImageShader] which /// correspond to shaders as used by [Paint.shader]. -class Shader extends NativeFieldWrapperClass2 { +class Shader extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @pragma('vm:entry-point') Shader._(); } -/// Defines what happens at the edge of the gradient. +/// Defines what happens at the edge of a gradient or the sampling of a source image +/// in an [ImageFilter]. /// /// A gradient is defined along a finite inner area. In the case of a linear /// gradient, it's between the parallel lines that are orthogonal to the line /// drawn between two points. In the case of radial gradients, it's the disc /// that covers the circle centered on a particular point up to a given radius. /// -/// This enum is used to define how the gradient should paint the regions +/// An image filter reads source samples from a source image and performs operations +/// on those samples to produce a result image. An image defines color samples only +/// for pixels within the bounds of the image but some filter operations, such as a blur +/// filter, read samples over a wide area to compute the output for a given pixel. Such +/// a filter would need to combine samples from inside the image with hypothetical +/// color values from outside the image. +/// +/// This enum is used to define how the gradient or image filter should treat the regions /// outside that defined inner area. /// /// See also: @@ -3232,36 +3441,67 @@ class Shader extends NativeFieldWrapperClass2 { /// * [dart:ui.Gradient], the low-level class used when dealing with the /// [Paint.shader] property directly, with its [Gradient.linear] and /// [Gradient.radial] constructors. -// These enum values must be kept in sync with SkShader::TileMode. +/// * [dart:ui.ImageFilter.blur], an ImageFilter that may sometimes need to +/// read samples from outside an image to combine with the pixels near the +/// edge of the image. +// These enum values must be kept in sync with SkTileMode. enum TileMode { - /// Edge is clamped to the final color. + /// Samples beyond the edge are clamped to the nearest color in the defined inner area. + /// + /// A gradient will paint all the regions outside the inner area with the + /// color at the end of the color stop list closest to that region. /// - /// The gradient will paint the all the regions outside the inner area with - /// the color of the point closest to that region. + /// An image filter will substitute the nearest edge pixel for any samples taken from + /// outside its source image. /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png) clamp, - /// Edge is repeated from first color to last. + /// Samples beyond the edge are repeated from the far end of the defined area. /// - /// This is as if the stop points from 0.0 to 1.0 were then repeated from 1.0 - /// to 2.0, 2.0 to 3.0, and so forth (and for linear gradients, similarly from - /// -1.0 to 0.0, -2.0 to -1.0, etc). + /// For a gradient, this technique is as if the stop points from 0.0 to 1.0 were then + /// repeated from 1.0 to 2.0, 2.0 to 3.0, and so forth (and for linear gradients, similarly + /// from -1.0 to 0.0, -2.0 to -1.0, etc). + /// + /// An image filter will treat its source image as if it were tiled across the enlarged + /// sample space from which it reads, each tile in the same orientation as the base image. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_sweep.png) repeated, - /// Edge is mirrored from last color to first. + /// Samples beyond the edge are mirrored back and forth across the defined area. /// - /// This is as if the stop points from 0.0 to 1.0 were then repeated backwards - /// from 2.0 to 1.0, then forwards from 2.0 to 3.0, then backwards again from - /// 4.0 to 3.0, and so forth (and for linear gradients, similarly from in the + /// For a gradient, this technique is as if the stop points from 0.0 to 1.0 were then + /// repeated backwards from 2.0 to 1.0, then forwards from 2.0 to 3.0, then backwards + /// again from 4.0 to 3.0, and so forth (and for linear gradients, similarly in the /// negative direction). /// + /// An image filter will treat its source image as tiled in an alternating forwards and + /// backwards or upwards and downwards direction across the sample space from which + /// it is reading. + /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_radial.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_sweep.png) mirror, + + /// Samples beyond the edge are treated as transparent black. + /// + /// A gradient will render transparency over any region that is outside the circle of a + /// radial gradient or outside the parallel lines that define the inner area of a linear + /// gradient. + /// + /// An image filter will substitute transparent black for any sample it must read from + /// outside its source image. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_linear.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_radial.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_sweep.png) + decal, } Int32List _encodeColorList(List colors) { @@ -3273,7 +3513,7 @@ Int32List _encodeColorList(List colors) { } Float32List _encodePointList(List points) { - assert(points != null); // ignore: unnecessary_null_comparison + assert(points != null); final int pointCount = points.length; final Float32List result = Float32List(pointCount * 2); for (int i = 0; i < pointCount; ++i) { @@ -3322,6 +3562,7 @@ class Gradient extends Shader { /// argument. For details, see the [TileMode] enum. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_linear.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_linear.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png) /// @@ -3341,9 +3582,9 @@ class Gradient extends Shader { Float64List? matrix4, ]) : assert(_offsetIsValid(from)), assert(_offsetIsValid(to)), - assert(colors != null), // ignore: unnecessary_null_comparison - assert(tileMode != null), // ignore: unnecessary_null_comparison - assert(matrix4 == null || _matrix4IsValid(matrix4)), // ignore: unnecessary_null_comparison + assert(colors != null), + assert(tileMode != null), + assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { _validateColorStops(colors, colorStops); final Float32List endPointsBuffer = _encodeTwoPoints(from, to); @@ -3366,6 +3607,7 @@ class Gradient extends Shader { /// argument. For details, see the [TileMode] enum. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_radial.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png) /// @@ -3393,8 +3635,8 @@ class Gradient extends Shader { Offset? focal, double focalRadius = 0.0 ]) : assert(_offsetIsValid(center)), - assert(colors != null), // ignore: unnecessary_null_comparison - assert(tileMode != null), // ignore: unnecessary_null_comparison + assert(colors != null), + assert(tileMode != null), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { _validateColorStops(colors, colorStops); @@ -3431,6 +3673,7 @@ class Gradient extends Shader { /// `tileMode` argument. For details, see the [TileMode] enum. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_sweep.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_sweep.png) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_sweep.png) /// @@ -3450,10 +3693,10 @@ class Gradient extends Shader { double endAngle = math.pi * 2, Float64List? matrix4, ]) : assert(_offsetIsValid(center)), - assert(colors != null), // ignore: unnecessary_null_comparison - assert(tileMode != null), // ignore: unnecessary_null_comparison - assert(startAngle != null), // ignore: unnecessary_null_comparison - assert(endAngle != null), // ignore: unnecessary_null_comparison + assert(colors != null), + assert(tileMode != null), + assert(startAngle != null), + assert(endAngle != null), assert(startAngle < endAngle), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { @@ -3482,22 +3725,25 @@ class ImageShader extends Shader { /// tile. The second and third arguments specify the [TileMode] for the x /// direction and y direction respectively. The fourth argument gives the /// matrix to apply to the effect. All the arguments are required and must not - /// be null. + /// be null, except for [filterQuality]. If [filterQuality] is not specified + /// at construction time it will be deduced from the environment where it is used, + /// such as from [Paint.filterQuality]. @pragma('vm:entry-point') - ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) : - // ignore: unnecessary_null_comparison + ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4, { + FilterQuality? filterQuality, + }) : assert(image != null), // image is checked on the engine side - assert(tmx != null), // ignore: unnecessary_null_comparison - assert(tmy != null), // ignore: unnecessary_null_comparison - assert(matrix4 != null), // ignore: unnecessary_null_comparison + assert(tmx != null), + assert(tmy != null), + assert(matrix4 != null), super._() { if (matrix4.length != 16) throw ArgumentError('"matrix4" must have 16 entries.'); _constructor(); - _initWithImage(image._image, tmx.index, tmy.index, matrix4); + _initWithImage(image._image, tmx.index, tmy.index, filterQuality?.index ?? -1, matrix4); } void _constructor() native 'ImageShader_constructor'; - void _initWithImage(_Image image, int tmx, int tmy, Float64List matrix4) native 'ImageShader_initWithImage'; + void _initWithImage(_Image image, int tmx, int tmy, int filterQualityIndex, Float64List matrix4) native 'ImageShader_initWithImage'; } /// Defines how a list of points is interpreted when drawing a set of triangles. @@ -3516,7 +3762,7 @@ enum VertexMode { } /// A set of vertex data used by [Canvas.drawVertices]. -class Vertices extends NativeFieldWrapperClass2 { +class Vertices extends NativeFieldWrapperClass1 { /// Creates a set of vertex data for use with [Canvas.drawVertices]. /// /// The [mode] and [positions] parameters must not be null. @@ -3532,8 +3778,8 @@ class Vertices extends NativeFieldWrapperClass2 { List? textureCoordinates, List? colors, List? indices, - }) : assert(mode != null), // ignore: unnecessary_null_comparison - assert(positions != null) { // ignore: unnecessary_null_comparison + }) : assert(mode != null), + assert(positions != null) { if (textureCoordinates != null && textureCoordinates.length != positions.length) throw ArgumentError('"positions" and "textureCoordinates" lengths must match.'); if (colors != null && colors.length != positions.length) @@ -3580,8 +3826,8 @@ class Vertices extends NativeFieldWrapperClass2 { Float32List? textureCoordinates, Int32List? colors, Uint16List? indices, - }) : assert(mode != null), // ignore: unnecessary_null_comparison - assert(positions != null) { // ignore: unnecessary_null_comparison + }) : assert(mode != null), + assert(positions != null) { if (textureCoordinates != null && textureCoordinates.length != positions.length) throw ArgumentError('"positions" and "textureCoordinates" lengths must match.'); if (colors != null && colors.length * 2 != positions.length) @@ -3603,7 +3849,6 @@ class Vertices extends NativeFieldWrapperClass2 { /// Defines how a list of points is interpreted when drawing a set of points. /// -// ignore: deprecated_member_use /// Used by [Canvas.drawPoints]. // These enum values must be kept in sync with SkCanvas::PointMode. enum PointMode { @@ -3662,7 +3907,7 @@ enum ClipOp { /// /// The current transform and clip can be saved and restored using the stack /// managed by the [save], [saveLayer], and [restore] methods. -class Canvas extends NativeFieldWrapperClass2 { +class Canvas extends NativeFieldWrapperClass1 { /// Creates a canvas for recording graphical operations into the /// given picture recorder. /// @@ -3676,7 +3921,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// To end the recording, call [PictureRecorder.endRecording] on the /// given recorder. @pragma('vm:entry-point') - Canvas(PictureRecorder recorder, [ Rect? cullRect ]) : assert(recorder != null) { // ignore: unnecessary_null_comparison + Canvas(PictureRecorder recorder, [ Rect? cullRect ]) : assert(recorder != null) { if (recorder.isRecording) throw ArgumentError('"recorder" must not already be associated with another Canvas.'); _recorder = recorder; @@ -3815,7 +4060,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// * [BlendMode], which discusses the use of [Paint.blendMode] with /// [saveLayer]. void saveLayer(Rect? bounds, Paint paint) { - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); if (bounds == null) { _saveLayerWithoutBounds(paint._objects, paint._data); } else { @@ -3876,7 +4121,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// Multiply the current transform by the specified 4⨉4 transformation matrix /// specified as a list of values in column-major order. void transform(Float64List matrix4) { - assert(matrix4 != null); // ignore: unnecessary_null_comparison + assert(matrix4 != null); if (matrix4.length != 16) throw ArgumentError('"matrix4" must have 16 entries.'); _transform(matrix4); @@ -3896,8 +4141,8 @@ class Canvas extends NativeFieldWrapperClass2 { /// current clip. void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }) { assert(_rectIsValid(rect)); - assert(clipOp != null); // ignore: unnecessary_null_comparison - assert(doAntiAlias != null); // ignore: unnecessary_null_comparison + assert(clipOp != null); + assert(doAntiAlias != null); _clipRect(rect.left, rect.top, rect.right, rect.bottom, clipOp.index, doAntiAlias); } void _clipRect(double left, @@ -3917,7 +4162,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// discussion of how to address that and some examples of using [clipRRect]. void clipRRect(RRect rrect, {bool doAntiAlias = true}) { assert(_rrectIsValid(rrect)); - assert(doAntiAlias != null); // ignore: unnecessary_null_comparison + assert(doAntiAlias != null); _clipRRect(rrect._value32, doAntiAlias); } void _clipRRect(Float32List rrect, bool doAntiAlias) native 'Canvas_clipRRect'; @@ -3932,9 +4177,8 @@ class Canvas extends NativeFieldWrapperClass2 { /// in incorrect blending at the clip boundary. See [saveLayer] for a /// discussion of how to address that. void clipPath(Path path, {bool doAntiAlias = true}) { - // ignore: unnecessary_null_comparison assert(path != null); // path is checked on the engine side - assert(doAntiAlias != null); // ignore: unnecessary_null_comparison + assert(doAntiAlias != null); _clipPath(path, doAntiAlias); } void _clipPath(Path path, bool doAntiAlias) native 'Canvas_clipPath'; @@ -3943,8 +4187,8 @@ class Canvas extends NativeFieldWrapperClass2 { /// [BlendMode], with the given color being the source and the background /// being the destination. void drawColor(Color color, BlendMode blendMode) { - assert(color != null); // ignore: unnecessary_null_comparison - assert(blendMode != null); // ignore: unnecessary_null_comparison + assert(color != null); + assert(blendMode != null); _drawColor(color.value, blendMode.index); } void _drawColor(int color, int blendMode) native 'Canvas_drawColor'; @@ -3956,7 +4200,7 @@ class Canvas extends NativeFieldWrapperClass2 { void drawLine(Offset p1, Offset p2, Paint paint) { assert(_offsetIsValid(p1)); assert(_offsetIsValid(p2)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data); } void _drawLine(double x1, @@ -3971,7 +4215,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// To fill the canvas with a solid color and blend mode, consider /// [drawColor] instead. void drawPaint(Paint paint) { - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawPaint(paint._objects, paint._data); } void _drawPaint(List? paintObjects, ByteData paintData) native 'Canvas_drawPaint'; @@ -3980,7 +4224,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// or stroked (or both) is controlled by [Paint.style]. void drawRect(Rect rect, Paint paint) { assert(_rectIsValid(rect)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawRect(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); } @@ -3995,7 +4239,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// filled or stroked (or both) is controlled by [Paint.style]. void drawRRect(RRect rrect, Paint paint) { assert(_rrectIsValid(rrect)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawRRect(rrect._value32, paint._objects, paint._data); } void _drawRRect(Float32List rrect, @@ -4010,7 +4254,7 @@ class Canvas extends NativeFieldWrapperClass2 { void drawDRRect(RRect outer, RRect inner, Paint paint) { assert(_rrectIsValid(outer)); assert(_rrectIsValid(inner)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawDRRect(outer._value32, inner._value32, paint._objects, paint._data); } void _drawDRRect(Float32List outer, @@ -4023,7 +4267,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// controlled by [Paint.style]. void drawOval(Rect rect, Paint paint) { assert(_rectIsValid(rect)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawOval(rect.left, rect.top, rect.right, rect.bottom, paint._objects, paint._data); } @@ -4040,7 +4284,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// controlled by [Paint.style]. void drawCircle(Offset c, double radius, Paint paint) { assert(_offsetIsValid(c)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawCircle(c.dx, c.dy, radius, paint._objects, paint._data); } void _drawCircle(double x, @@ -4062,7 +4306,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// This method is optimized for drawing arcs and should be faster than [Path.arcTo]. void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) { assert(_rectIsValid(rect)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawArc(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, useCenter, paint._objects, paint._data); } @@ -4082,9 +4326,8 @@ class Canvas extends NativeFieldWrapperClass2 { /// [Paint.style]. If the path is filled, then sub-paths within it are /// implicitly closed (see [Path.close]). void drawPath(Path path, Paint paint) { - // ignore: unnecessary_null_comparison assert(path != null); // path is checked on the engine side - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawPath(path, paint._objects, paint._data); } void _drawPath(Path path, @@ -4094,17 +4337,17 @@ class Canvas extends NativeFieldWrapperClass2 { /// Draws the given [Image] into the canvas with its top-left corner at the /// given [Offset]. The image is composited into the canvas using the given [Paint]. void drawImage(Image image, Offset offset, Paint paint) { - // ignore: unnecessary_null_comparison assert(image != null); // image is checked on the engine side assert(_offsetIsValid(offset)); - assert(paint != null); // ignore: unnecessary_null_comparison - _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data); + assert(paint != null); + _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data, paint.filterQuality.index); } void _drawImage(_Image image, double x, double y, List? paintObjects, - ByteData paintData) native 'Canvas_drawImage'; + ByteData paintData, + int filterQualityIndex) native 'Canvas_drawImage'; /// Draws the subset of the given image described by the `src` argument into /// the canvas in the axis-aligned rectangle given by the `dst` argument. @@ -4116,11 +4359,10 @@ class Canvas extends NativeFieldWrapperClass2 { /// image) can be batched into a single call to [drawAtlas] to improve /// performance. void drawImageRect(Image image, Rect src, Rect dst, Paint paint) { - // ignore: unnecessary_null_comparison assert(image != null); // image is checked on the engine side assert(_rectIsValid(src)); assert(_rectIsValid(dst)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawImageRect(image._image, src.left, src.top, @@ -4131,7 +4373,8 @@ class Canvas extends NativeFieldWrapperClass2 { dst.right, dst.bottom, paint._objects, - paint._data); + paint._data, + paint.filterQuality.index); } void _drawImageRect(_Image image, double srcLeft, @@ -4143,7 +4386,8 @@ class Canvas extends NativeFieldWrapperClass2 { double dstRight, double dstBottom, List? paintObjects, - ByteData paintData) native 'Canvas_drawImageRect'; + ByteData paintData, + int filterQualityIndex) native 'Canvas_drawImageRect'; /// Draws the given [Image] into the canvas using the given [Paint]. /// @@ -4159,11 +4403,10 @@ class Canvas extends NativeFieldWrapperClass2 { /// cover the destination rectangle while maintaining their relative /// positions. void drawImageNine(Image image, Rect center, Rect dst, Paint paint) { - // ignore: unnecessary_null_comparison assert(image != null); // image is checked on the engine side assert(_rectIsValid(center)); assert(_rectIsValid(dst)); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); _drawImageNine(image._image, center.left, center.top, @@ -4174,7 +4417,8 @@ class Canvas extends NativeFieldWrapperClass2 { dst.right, dst.bottom, paint._objects, - paint._data); + paint._data, + paint.filterQuality.index); } void _drawImageNine(_Image image, double centerLeft, @@ -4186,12 +4430,12 @@ class Canvas extends NativeFieldWrapperClass2 { double dstRight, double dstBottom, List? paintObjects, - ByteData paintData) native 'Canvas_drawImageNine'; + ByteData paintData, + int filterQualityIndex) native 'Canvas_drawImageNine'; /// Draw the given picture onto the canvas. To create a picture, see /// [PictureRecorder]. void drawPicture(Picture picture) { - // ignore: unnecessary_null_comparison assert(picture != null); // picture is checked on the engine side _drawPicture(picture); } @@ -4218,7 +4462,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// described by adding half of the [ParagraphConstraints.width] given to /// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate. void drawParagraph(Paragraph paragraph, Offset offset) { - assert(paragraph != null); // ignore: unnecessary_null_comparison + assert(paragraph != null); assert(_offsetIsValid(offset)); paragraph._paint(this, offset.dx, offset.dy); } @@ -4232,9 +4476,9 @@ class Canvas extends NativeFieldWrapperClass2 { /// * [drawRawPoints], which takes `points` as a [Float32List] rather than a /// [List]. void drawPoints(PointMode pointMode, List points, Paint paint) { - assert(pointMode != null); // ignore: unnecessary_null_comparison - assert(points != null); // ignore: unnecessary_null_comparison - assert(paint != null); // ignore: unnecessary_null_comparison + assert(pointMode != null); + assert(points != null); + assert(paint != null); _drawPoints(paint._objects, paint._data, pointMode.index, _encodePointList(points)); } @@ -4248,9 +4492,9 @@ class Canvas extends NativeFieldWrapperClass2 { /// * [drawPoints], which takes `points` as a [List] rather than a /// [List]. void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) { - assert(pointMode != null); // ignore: unnecessary_null_comparison - assert(points != null); // ignore: unnecessary_null_comparison - assert(paint != null); // ignore: unnecessary_null_comparison + assert(pointMode != null); + assert(points != null); + assert(paint != null); if (points.length % 2 != 0) throw ArgumentError('"points" must have an even number of values.'); _drawPoints(paint._objects, paint._data, pointMode.index, points); @@ -4270,10 +4514,10 @@ class Canvas extends NativeFieldWrapperClass2 { /// * [Vertices.raw], which creates the vertices using typed data lists /// rather than unencoded lists. void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { - // ignore: unnecessary_null_comparison + assert(vertices != null); // vertices is checked on the engine side - assert(paint != null); // ignore: unnecessary_null_comparison - assert(blendMode != null); // ignore: unnecessary_null_comparison + assert(paint != null); + assert(blendMode != null); _drawVertices(vertices, blendMode.index, paint._objects, paint._data); } void _drawVertices(Vertices vertices, @@ -4392,7 +4636,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// Rect.fromLTWH(sprite.index * 10.0, 0.0, 10.0, 10.0), /// ], [ /// for (Sprite sprite in allSprites) - /// Color.white.withAlpha(sprite.alpha), + /// Colors.white.withAlpha(sprite.alpha), /// ], BlendMode.srcIn, null, paint); /// } /// @@ -4415,12 +4659,11 @@ class Canvas extends NativeFieldWrapperClass2 { BlendMode? blendMode, Rect? cullRect, Paint paint) { - // ignore: unnecessary_null_comparison assert(atlas != null); // atlas is checked on the engine side - assert(transforms != null); // ignore: unnecessary_null_comparison - assert(rects != null); // ignore: unnecessary_null_comparison + assert(transforms != null); + assert(rects != null); assert(colors == null || colors.isEmpty || blendMode != null); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); final int rectCount = rects.length; if (transforms.length != rectCount) @@ -4451,9 +4694,10 @@ class Canvas extends NativeFieldWrapperClass2 { final Int32List? colorBuffer = (colors == null || colors.isEmpty) ? null : _encodeColorList(colors); final Float32List? cullRectBuffer = cullRect?._value32; + final int qualityIndex = paint.filterQuality.index; _drawAtlas( - paint._objects, paint._data, atlas._image, rstTransformBuffer, rectBuffer, + paint._objects, paint._data, qualityIndex, atlas._image, rstTransformBuffer, rectBuffer, colorBuffer, (blendMode ?? BlendMode.src).index, cullRectBuffer ); } @@ -4519,7 +4763,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// transformList[i * 4 + 0] = 1.0; /// transformList[i * 4 + 1] = 0.0; /// transformList[i * 4 + 2] = sprite.centerX - 5.0; - /// transformList[i * 4 + 2] = sprite.centerY - 5.0; + /// transformList[i * 4 + 3] = sprite.centerY - 5.0; /// } /// Paint paint = Paint(); /// canvas.drawAtlas(spriteAtlas, transformList, rectList, null, null, null, paint); @@ -4579,7 +4823,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// transformList[i * 4 + 0] = transform.scos; /// transformList[i * 4 + 1] = transform.ssin; /// transformList[i * 4 + 2] = transform.tx; - /// transformList[i * 4 + 2] = transform.ty; + /// transformList[i * 4 + 3] = transform.ty; /// /// // This example computes the color value directly, but one could also compute /// // an actual Color object and use its Color.value getter for the same result. @@ -4606,12 +4850,11 @@ class Canvas extends NativeFieldWrapperClass2 { BlendMode? blendMode, Rect? cullRect, Paint paint) { - // ignore: unnecessary_null_comparison assert(atlas != null); // atlas is checked on the engine side - assert(rstTransforms != null); // ignore: unnecessary_null_comparison - assert(rects != null); // ignore: unnecessary_null_comparison + assert(rstTransforms != null); + assert(rects != null); assert(colors == null || blendMode != null); - assert(paint != null); // ignore: unnecessary_null_comparison + assert(paint != null); final int rectCount = rects.length; if (rstTransforms.length != rectCount) @@ -4620,15 +4863,17 @@ class Canvas extends NativeFieldWrapperClass2 { throw ArgumentError('"rstTransforms" and "rects" lengths must be a multiple of four.'); if (colors != null && colors.length * 4 != rectCount) throw ArgumentError('If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".'); + final int qualityIndex = paint.filterQuality.index; _drawAtlas( - paint._objects, paint._data, atlas._image, rstTransforms, rects, + paint._objects, paint._data, qualityIndex, atlas._image, rstTransforms, rects, colors, (blendMode ?? BlendMode.src).index, cullRect?._value32 ); } void _drawAtlas(List? paintObjects, ByteData paintData, + int filterQualityIndex, _Image atlas, Float32List rstTransforms, Float32List rects, @@ -4643,10 +4888,9 @@ class Canvas extends NativeFieldWrapperClass2 { /// /// The arguments must not be null. void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { - // ignore: unnecessary_null_comparison assert(path != null); // path is checked on the engine side - assert(color != null); // ignore: unnecessary_null_comparison - assert(transparentOccluder != null); // ignore: unnecessary_null_comparison + assert(color != null); + assert(transparentOccluder != null); _drawShadow(path, color.value, elevation, transparentOccluder); } void _drawShadow(Path path, @@ -4663,7 +4907,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// the [SceneBuilder.addPicture] method. A [Picture] can also be /// drawn into a [Canvas], using the [Canvas.drawPicture] method. @pragma('vm:entry-point') -class Picture extends NativeFieldWrapperClass2 { +class Picture extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// @@ -4683,13 +4927,17 @@ class Picture extends NativeFieldWrapperClass2 { if (width <= 0 || height <= 0) throw Exception('Invalid image dimensions.'); return _futurize( - (_Callback callback) => _toImage(width, height, (_Image image) { - callback(Image._(image)); + (_Callback callback) => _toImage(width, height, (_Image? image) { + if (image == null) { + callback(null); + } else { + callback(Image._(image)); + } }), ); } - String _toImage(int width, int height, _Callback<_Image> callback) native 'Picture_toImage'; + String? _toImage(int width, int height, _Callback<_Image?> callback) native 'Picture_toImage'; /// Release the resources used by this object. The object is no longer usable /// after this method is called. @@ -4706,7 +4954,7 @@ class Picture extends NativeFieldWrapperClass2 { /// /// To begin recording, construct a [Canvas] to record the commands. /// To end recording, use the [PictureRecorder.endRecording] method. -class PictureRecorder extends NativeFieldWrapperClass2 { +class PictureRecorder extends NativeFieldWrapperClass1 { /// Creates a new idle PictureRecorder. To associate it with a /// [Canvas] and begin recording, pass this [PictureRecorder] to the /// [Canvas] constructor. @@ -4761,8 +5009,8 @@ class Shadow { this.color = const Color(_kColorDefault), this.offset = Offset.zero, this.blurRadius = 0.0, - }) : assert(color != null, 'Text shadow color was null.'), // ignore: unnecessary_null_comparison - assert(offset != null, 'Text shadow offset was null.'), // ignore: unnecessary_null_comparison + }) : assert(color != null, 'Text shadow color was null.'), + assert(offset != null, 'Text shadow offset was null.'), assert(blurRadius >= 0.0, 'Text shadow blur radius should be non-negative.'); static const int _kColorDefault = 0xFF000000; @@ -4796,7 +5044,7 @@ class Shadow { // See SkBlurMask::ConvertRadiusToSigma(). // static double convertRadiusToSigma(double radius) { - return radius * 0.57735 + 0.5; + return radius > 0 ? radius * 0.57735 + 0.5 : 0; } /// The [blurRadius] in sigmas instead of logical pixels. @@ -4850,7 +5098,7 @@ class Shadow { /// an [AnimationController]. /// {@endtemplate} static Shadow? lerp(Shadow? a, Shadow? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (b == null) { if (a == null) { return null; @@ -4876,7 +5124,7 @@ class Shadow { /// /// {@macro dart.ui.shadow.lerp} static List? lerpList(List? a, List? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (a == null && b == null) return null; a ??= []; @@ -4921,7 +5169,6 @@ class Shadow { // TODO(yjbanov): remove the null check when the framework is migrated. While the list // of shadows contains non-nullable elements, unmigrated code can still // pass nulls. - // ignore: unnecessary_null_comparison if (shadow != null) { shadowOffset = shadowIndex * _kBytesPerShadow; @@ -4934,8 +5181,9 @@ class Shadow { shadowsData.setFloat32(_kYOffset + shadowOffset, shadow.offset.dy, _kFakeHostEndian); + final double blurSigma = Shadow.convertRadiusToSigma(shadow.blurRadius); shadowsData.setFloat32(_kBlurOffset + shadowOffset, - shadow.blurRadius, _kFakeHostEndian); + blurSigma, _kFakeHostEndian); } } @@ -4947,7 +5195,10 @@ class Shadow { } /// A handle to a read-only byte buffer that is managed by the engine. -class ImmutableBuffer extends NativeFieldWrapperClass2 { +/// +/// The creator of this object is responsible for calling [dispose] when it is +/// no longer needed. +class ImmutableBuffer extends NativeFieldWrapperClass1 { ImmutableBuffer._(this.length); /// Creates a copy of the data from a [Uint8List] suitable for internal use @@ -4963,16 +5214,46 @@ class ImmutableBuffer extends NativeFieldWrapperClass2 { /// The length, in bytes, of the underlying data. final int length; + bool _debugDisposed = false; + + /// Whether [dispose] has been called. + /// + /// This must only be used when asserts are enabled. Otherwise, it will throw. + bool get debugDisposed { + late bool disposed; + assert(() { + disposed = _debugDisposed; + return true; + }()); + return disposed; + } + /// Release the resources used by this object. The object is no longer usable /// after this method is called. - void dispose() native 'ImmutableBuffer_dispose'; + /// + /// The underlying memory allocated by this object will be retained beyond + /// this call if it is still needed by another object that has not been + /// disposed. For example, an [ImageDescriptor] that has not been disposed + /// may still retain a reference to the memory from this buffer even if it + /// has been disposed. Freeing that memory requires disposing all resources + /// that may still hold it. + void dispose() { + assert(() { + assert(!_debugDisposed); + _debugDisposed = true; + return true; + }()); + _dispose(); + } + + void _dispose() native 'ImmutableBuffer_dispose'; } /// A descriptor of data that can be turned into an [Image] via a [Codec]. /// /// Use this class to determine the height, width, and byte size of image data /// before decoding it. -class ImageDescriptor extends NativeFieldWrapperClass2 { +class ImageDescriptor extends NativeFieldWrapperClass1 { ImageDescriptor._(); /// Creates an image descriptor from encoded data in a supported format. @@ -5077,7 +5358,7 @@ typedef _Callback = void Function(T result); /// /// Return value should be null on success, and a string error message on /// failure. -typedef _Callbacker = String? Function(_Callback callback); +typedef _Callbacker = String? Function(_Callback callback); /// Converts a method that receives a value-returning callback to a method that /// returns a Future. @@ -5102,7 +5383,7 @@ typedef _Callbacker = String? Function(_Callback callback); /// ``` Future _futurize(_Callbacker callbacker) { final Completer completer = Completer.sync(); - final String? error = callbacker((T t) { + final String? error = callbacker((T? t) { if (t == null) { completer.completeError(Exception('operation failed')); } else { diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 68ae4898c176a..cd5492f0b6f14 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/lib/ui/painting/canvas.h" +#include "flutter/lib/ui/painting/image_filter.h" #include @@ -84,6 +85,7 @@ fml::RefPtr Canvas::Create(PictureRecorder* recorder, fml::RefPtr canvas = fml::MakeRefCounted( recorder->BeginRecording(SkRect::MakeLTRB(left, top, right, bottom))); recorder->set_canvas(canvas); + canvas->display_list_recorder_ = recorder->display_list_recorder(); return canvas; } @@ -315,7 +317,8 @@ void Canvas::drawImage(const CanvasImage* image, double x, double y, const Paint& paint, - const PaintData& paint_data) { + const PaintData& paint_data, + int filterQualityIndex) { if (!canvas_) { return; } @@ -324,7 +327,8 @@ void Canvas::drawImage(const CanvasImage* image, ToDart("Canvas.drawImage called with non-genuine Image.")); return; } - canvas_->drawImage(image->image(), x, y, paint.paint()); + auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); + canvas_->drawImage(image->image(), x, y, sampling, paint.paint()); } void Canvas::drawImageRect(const CanvasImage* image, @@ -337,7 +341,8 @@ void Canvas::drawImageRect(const CanvasImage* image, double dst_right, double dst_bottom, const Paint& paint, - const PaintData& paint_data) { + const PaintData& paint_data, + int filterQualityIndex) { if (!canvas_) { return; } @@ -348,7 +353,8 @@ void Canvas::drawImageRect(const CanvasImage* image, } SkRect src = SkRect::MakeLTRB(src_left, src_top, src_right, src_bottom); SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); - canvas_->drawImageRect(image->image(), src, dst, paint.paint(), + auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); + canvas_->drawImageRect(image->image(), src, dst, sampling, paint.paint(), SkCanvas::kFast_SrcRectConstraint); } @@ -362,7 +368,8 @@ void Canvas::drawImageNine(const CanvasImage* image, double dst_right, double dst_bottom, const Paint& paint, - const PaintData& paint_data) { + const PaintData& paint_data, + int bitmapSamplingIndex) { if (!canvas_) { return; } @@ -376,7 +383,20 @@ void Canvas::drawImageNine(const CanvasImage* image, SkIRect icenter; center.round(&icenter); SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); - canvas_->drawImageNine(image->image(), icenter, dst, paint.paint()); + auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex); + if (display_list_recorder_) { + // SkCanvas turns a simple 2-rect DrawImageNine operation into a + // drawImageLattice operation which has arrays to allocate and + // pass along. For simplicity, we will bypass the canvas and ask + // the recorder to record our paint attributes and record a much + // simpler DrawImageNineOp record directly. + display_list_recorder_->RecordPaintAttributes( + paint.paint(), DisplayListCanvasRecorder::DrawType::kImageOpType); + builder()->drawImageNine(image->image(), icenter, dst, filter); + } else { + canvas_->drawImageNine(image->image().get(), icenter, dst, filter, + paint.paint()); + } } void Canvas::drawPicture(Picture* picture) { @@ -388,7 +408,17 @@ void Canvas::drawPicture(Picture* picture) { ToDart("Canvas.drawPicture called with non-genuine Picture.")); return; } - canvas_->drawPicture(picture->picture().get()); + if (picture->picture()) { + canvas_->drawPicture(picture->picture().get()); + } else if (picture->display_list()) { + if (display_list_recorder_) { + builder()->drawDisplayList(picture->display_list()); + } else { + picture->display_list()->RenderTo(canvas_); + } + } else { + FML_DCHECK(false); + } } void Canvas::drawPoints(const Paint& paint, @@ -425,6 +455,7 @@ void Canvas::drawVertices(const Vertices* vertices, void Canvas::drawAtlas(const Paint& paint, const PaintData& paint_data, + int filterQualityIndex, CanvasImage* atlas, const tonic::Float32List& transforms, const tonic::Float32List& rects, @@ -448,12 +479,14 @@ void Canvas::drawAtlas(const Paint& paint, static_assert(sizeof(SkRect) == sizeof(float) * 4, "SkRect doesn't use floats."); + auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); + canvas_->drawAtlas( skImage.get(), reinterpret_cast(transforms.data()), reinterpret_cast(rects.data()), reinterpret_cast(colors.data()), rects.num_elements() / 4, // SkRect have four floats. - blend_mode, reinterpret_cast(cull_rect.data()), + blend_mode, sampling, reinterpret_cast(cull_rect.data()), paint.paint()); } @@ -468,11 +501,23 @@ void Canvas::drawShadow(const CanvasPath* path, } SkScalar dpr = UIDartState::Current() ->platform_configuration() - ->window() + ->get_window(0) ->viewport_metrics() .device_pixel_ratio; - flutter::PhysicalShapeLayer::DrawShadow(canvas_, path->path(), color, - elevation, transparentOccluder, dpr); + if (display_list_recorder_) { + // The DrawShadow mechanism results in non-public operations to be + // performed on the canvas involving an SkDrawShadowRec. Since we + // cannot include the header that defines that structure, we cannot + // record an operation that it injects into an SkCanvas. To prevent + // that situation we bypass the canvas interface and inject the + // shadow parameters directly into the underlying DisplayList. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + builder()->drawShadow(path->path(), color, elevation, transparentOccluder, + dpr); + } else { + flutter::PhysicalShapeLayer::DrawShadow( + canvas_, path->path(), color, elevation, transparentOccluder, dpr); + } } void Canvas::Invalidate() { diff --git a/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index 3c58bad4e7895..ca328d310dbb6 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -110,7 +110,8 @@ class Canvas : public RefCountedDartWrappable { double x, double y, const Paint& paint, - const PaintData& paint_data); + const PaintData& paint_data, + int filterQualityIndex); void drawImageRect(const CanvasImage* image, double src_left, double src_top, @@ -121,7 +122,8 @@ class Canvas : public RefCountedDartWrappable { double dst_right, double dst_bottom, const Paint& paint, - const PaintData& paint_data); + const PaintData& paint_data, + int filterQualityIndex); void drawImageNine(const CanvasImage* image, double center_left, double center_top, @@ -132,7 +134,8 @@ class Canvas : public RefCountedDartWrappable { double dst_right, double dst_bottom, const Paint& paint, - const PaintData& paint_data); + const PaintData& paint_data, + int bitmapSamplingIndex); void drawPicture(Picture* picture); // The paint argument is first for the following functions because Paint @@ -152,6 +155,7 @@ class Canvas : public RefCountedDartWrappable { void drawAtlas(const Paint& paint, const PaintData& paint_data, + int filterQualityIndex, CanvasImage* atlas, const tonic::Float32List& transforms, const tonic::Float32List& rects, @@ -176,6 +180,17 @@ class Canvas : public RefCountedDartWrappable { // which does not transfer ownership. For this reason, we hold a raw // pointer and manually set to null in Clear. SkCanvas* canvas_; + + // A copy of the recorder used by the SkCanvas->DisplayList adapter for cases + // where we cannot record the SkCanvas method call through the various OnOp() + // virtual methods or where we can be more efficient by talking directly in + // the DisplayList operation lexicon. The recorder has a method for recording + // paint attributes from an SkPaint and an operation type as well as access + // to the raw DisplayListBuilder for emitting custom rendering operations. + sk_sp display_list_recorder_; + sk_sp builder() { + return display_list_recorder_->builder(); + } }; } // namespace flutter diff --git a/lib/ui/painting/engine_layer.cc b/lib/ui/painting/engine_layer.cc index 4504ac278e618..0f3e0b73787d1 100644 --- a/lib/ui/painting/engine_layer.cc +++ b/lib/ui/painting/engine_layer.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/engine_layer.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" @@ -13,22 +14,20 @@ using tonic::ToDart; namespace flutter { -EngineLayer::EngineLayer(std::shared_ptr layer) - : layer_(layer) {} +IMPLEMENT_WRAPPERTYPEINFO(ui, EngineLayer); -EngineLayer::~EngineLayer() = default; +#define FOR_EACH_BINDING(V) V(EngineLayer, dispose) -size_t EngineLayer::GetAllocationSize() const { - // Provide an approximation of the total memory impact of this object to the - // Dart GC. The ContainerLayer may hold references to a tree of other layers, - // which in turn may contain Skia objects. - return 3000; -}; +DART_BIND_ALL(EngineLayer, FOR_EACH_BINDING) -IMPLEMENT_WRAPPERTYPEINFO(ui, EngineLayer); +EngineLayer::EngineLayer(std::shared_ptr layer) + : layer_(layer) {} -#define FOR_EACH_BINDING(V) // nothing to bind +EngineLayer::~EngineLayer() = default; -DART_BIND_ALL(EngineLayer, FOR_EACH_BINDING) +void EngineLayer::dispose() { + layer_.reset(); + ClearDartWrapper(); +} } // namespace flutter diff --git a/lib/ui/painting/engine_layer.h b/lib/ui/painting/engine_layer.h index 3b40a3e1e64e1..87ae5ab173bba 100644 --- a/lib/ui/painting/engine_layer.h +++ b/lib/ui/painting/engine_layer.h @@ -22,13 +22,6 @@ class EngineLayer : public RefCountedDartWrappable { public: ~EngineLayer() override; - size_t GetAllocationSize() const override; - - static fml::RefPtr MakeRetained( - std::shared_ptr layer) { - return fml::MakeRefCounted(layer); - } - static void MakeRetained(Dart_Handle dart_handle, std::shared_ptr layer) { auto engine_layer = fml::MakeRefCounted(layer); @@ -37,6 +30,8 @@ class EngineLayer : public RefCountedDartWrappable { static void RegisterNatives(tonic::DartLibraryNatives* natives); + void dispose(); + std::shared_ptr Layer() const { return layer_; } private: diff --git a/lib/ui/painting/gradient.cc b/lib/ui/painting/gradient.cc index d71e75a495f94..0d2d2fc3ddb96 100644 --- a/lib/ui/painting/gradient.cc +++ b/lib/ui/painting/gradient.cc @@ -58,10 +58,10 @@ void CanvasGradient::initLinear(const tonic::Float32List& end_points, sk_matrix = ToSkMatrix(matrix4); } - set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeLinear( + sk_shader_ = UIDartState::CreateGPUObject(SkGradientShader::MakeLinear( reinterpret_cast(end_points.data()), reinterpret_cast(colors.data()), color_stops.data(), - colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr))); + colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)); } void CanvasGradient::initRadial(double center_x, @@ -83,10 +83,10 @@ void CanvasGradient::initRadial(double center_x, sk_matrix = ToSkMatrix(matrix4); } - set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeRadial( + sk_shader_ = UIDartState::CreateGPUObject(SkGradientShader::MakeRadial( SkPoint::Make(center_x, center_y), radius, reinterpret_cast(colors.data()), color_stops.data(), - colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr))); + colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)); } void CanvasGradient::initSweep(double center_x, @@ -109,11 +109,11 @@ void CanvasGradient::initSweep(double center_x, sk_matrix = ToSkMatrix(matrix4); } - set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeSweep( + sk_shader_ = UIDartState::CreateGPUObject(SkGradientShader::MakeSweep( center_x, center_y, reinterpret_cast(colors.data()), color_stops.data(), colors.num_elements(), tile_mode, start_angle * 180.0 / M_PI, end_angle * 180.0 / M_PI, 0, - has_matrix ? &sk_matrix : nullptr))); + has_matrix ? &sk_matrix : nullptr)); } void CanvasGradient::initTwoPointConical(double start_x, @@ -138,11 +138,13 @@ void CanvasGradient::initTwoPointConical(double start_x, sk_matrix = ToSkMatrix(matrix4); } - set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeTwoPointConical( - SkPoint::Make(start_x, start_y), start_radius, - SkPoint::Make(end_x, end_y), end_radius, - reinterpret_cast(colors.data()), color_stops.data(), - colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr))); + sk_shader_ = + UIDartState::CreateGPUObject(SkGradientShader::MakeTwoPointConical( + SkPoint::Make(start_x, start_y), start_radius, + SkPoint::Make(end_x, end_y), end_radius, + reinterpret_cast(colors.data()), color_stops.data(), + colors.num_elements(), tile_mode, 0, + has_matrix ? &sk_matrix : nullptr)); } CanvasGradient::CanvasGradient() = default; diff --git a/lib/ui/painting/gradient.h b/lib/ui/painting/gradient.h index a0d142a5adab4..cc0a0cf27d590 100644 --- a/lib/ui/painting/gradient.h +++ b/lib/ui/painting/gradient.h @@ -62,10 +62,15 @@ class CanvasGradient : public Shader { SkTileMode tile_mode, const tonic::Float64List& matrix4); + sk_sp shader(SkSamplingOptions) override { + return sk_shader_.skia_object(); + } + static void RegisterNatives(tonic::DartLibraryNatives* natives); private: CanvasGradient(); + flutter::SkiaGPUObject sk_shader_; }; } // namespace flutter diff --git a/lib/ui/painting/image.cc b/lib/ui/painting/image.cc index 1f813096b8e73..673674a5efa7b 100644 --- a/lib/ui/painting/image.cc +++ b/lib/ui/painting/image.cc @@ -44,16 +44,12 @@ Dart_Handle CanvasImage::toByteData(int format, Dart_Handle callback) { } void CanvasImage::dispose() { - auto hint_freed_delegate = UIDartState::Current()->GetHintFreedDelegate(); - if (hint_freed_delegate) { - hint_freed_delegate->HintFreed(GetAllocationSize()); - } image_.reset(); ClearDartWrapper(); } size_t CanvasImage::GetAllocationSize() const { - if (auto image = image_.get()) { + if (auto image = image_.skia_object()) { const auto& info = image->imageInfo(); const auto kMipmapOverhead = 4.0 / 3.0; const size_t image_byte_size = info.computeMinByteSize() * kMipmapOverhead; diff --git a/lib/ui/painting/image.h b/lib/ui/painting/image.h index b8bae633e6e25..a80dcd28c605e 100644 --- a/lib/ui/painting/image.h +++ b/lib/ui/painting/image.h @@ -26,15 +26,15 @@ class CanvasImage final : public RefCountedDartWrappable { return fml::MakeRefCounted(); } - int width() { return image_.get()->width(); } + int width() { return image_.skia_object()->width(); } - int height() { return image_.get()->height(); } + int height() { return image_.skia_object()->height(); } Dart_Handle toByteData(int format, Dart_Handle callback); void dispose(); - sk_sp image() const { return image_.get(); } + sk_sp image() const { return image_.skia_object(); } void set_image(flutter::SkiaGPUObject image) { image_ = std::move(image); } diff --git a/lib/ui/painting/image_decoder.cc b/lib/ui/painting/image_decoder.cc index a2de0185bb285..141a0ae70e4d2 100644 --- a/lib/ui/painting/image_decoder.cc +++ b/lib/ui/painting/image_decoder.cc @@ -53,8 +53,10 @@ static sk_sp ResizeRasterImage(sk_sp image, return nullptr; } - if (!image->scalePixels(scaled_bitmap.pixmap(), kLow_SkFilterQuality, - SkImage::kDisallow_CachingHint)) { + if (!image->scalePixels( + scaled_bitmap.pixmap(), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), + SkImage::kDisallow_CachingHint)) { FML_LOG(ERROR) << "Could not scale pixels"; return nullptr; } @@ -73,7 +75,7 @@ static sk_sp ResizeRasterImage(sk_sp image, } static sk_sp ImageFromDecompressedData( - fml::RefPtr descriptor, + ImageDescriptor* descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow& flow) { @@ -96,7 +98,7 @@ static sk_sp ImageFromDecompressedData( SkISize::Make(target_width, target_height), flow); } -sk_sp ImageFromCompressedData(fml::RefPtr descriptor, +sk_sp ImageFromCompressedData(ImageDescriptor* descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow& flow) { @@ -105,7 +107,8 @@ sk_sp ImageFromCompressedData(fml::RefPtr descriptor, if (!descriptor->should_resize(target_width, target_height)) { // No resizing requested. Just decode & rasterize the image. - return descriptor->image()->makeRasterImage(); + sk_sp image = descriptor->image(); + return image ? image->makeRasterImage() : nullptr; } const SkISize source_dimensions = descriptor->image_info().dimensions(); @@ -213,37 +216,55 @@ static SkiaGPUObject UploadRasterImage( return result; } -void ImageDecoder::Decode(fml::RefPtr descriptor, +void ImageDecoder::Decode(fml::RefPtr descriptor_ref_ptr, uint32_t target_width, uint32_t target_height, const ImageResult& callback) { TRACE_EVENT0("flutter", __FUNCTION__); fml::tracing::TraceFlow flow(__FUNCTION__); + // ImageDescriptors have Dart peers that must be collected on the UI thread. + // However, closures in MakeCopyable below capture the descriptor. The + // captures of copyable closures may be collected on any of the thread + // participating in task execution. + // + // To avoid this issue, we resort to manually reference counting the + // descriptor. Since all task flows invoke the `result` callback, the raw + // descriptor is retained in the beginning and released in the `result` + // callback. + // + // `ImageDecoder::Decode` itself is invoked on the UI thread, so the + // collection of the smart pointer from which we obtained the raw descriptor + // is fine in this scope. + auto raw_descriptor = descriptor_ref_ptr.get(); + raw_descriptor->AddRef(); + FML_DCHECK(callback); FML_DCHECK(runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); - // Always service the callback on the UI thread. - auto result = [callback, ui_runner = runners_.GetUITaskRunner()]( - SkiaGPUObject image, - fml::tracing::TraceFlow flow) { - ui_runner->PostTask(fml::MakeCopyable( - [callback, image = std::move(image), flow = std::move(flow)]() mutable { - // We are going to terminate the trace flow here. Flows cannot - // terminate without a base trace. Add one explicitly. - TRACE_EVENT0("flutter", "ImageDecodeCallback"); - flow.End(); - callback(std::move(image)); - })); - }; - - if (!descriptor->data() || descriptor->data()->size() == 0) { + // Always service the callback (and cleanup the descriptor) on the UI thread. + auto result = + [callback, raw_descriptor, ui_runner = runners_.GetUITaskRunner()]( + SkiaGPUObject image, fml::tracing::TraceFlow flow) { + ui_runner->PostTask(fml::MakeCopyable( + [callback, raw_descriptor, image = std::move(image), + flow = std::move(flow)]() mutable { + // We are going to terminate the trace flow here. Flows cannot + // terminate without a base trace. Add one explicitly. + TRACE_EVENT0("flutter", "ImageDecodeCallback"); + flow.End(); + callback(std::move(image)); + raw_descriptor->Release(); + })); + }; + + if (!raw_descriptor->data() || raw_descriptor->data()->size() == 0) { result({}, std::move(flow)); return; } concurrent_task_runner_->PostTask( - fml::MakeCopyable([descriptor, // + fml::MakeCopyable([raw_descriptor, // io_manager = io_manager_, // io_runner = runners_.GetIOTaskRunner(), // result, // @@ -254,19 +275,18 @@ void ImageDecoder::Decode(fml::RefPtr descriptor, // Step 1: Decompress the image. // On Worker. - auto decompressed = - descriptor->is_compressed() - ? ImageFromCompressedData(std::move(descriptor), // - target_width, // - target_height, // - flow) - : ImageFromDecompressedData(std::move(descriptor), // - target_width, // - target_height, // - flow); + auto decompressed = raw_descriptor->is_compressed() + ? ImageFromCompressedData(raw_descriptor, // + target_width, // + target_height, // + flow) + : ImageFromDecompressedData(raw_descriptor, // + target_width, // + target_height, // + flow); if (!decompressed) { - FML_LOG(ERROR) << "Could not decompress image."; + FML_DLOG(ERROR) << "Could not decompress image."; result({}, std::move(flow)); return; } @@ -278,8 +298,9 @@ void ImageDecoder::Decode(fml::RefPtr descriptor, flow = std::move(flow)]() mutable { if (!io_manager) { - FML_LOG(ERROR) << "Could not acquire IO manager."; - return result({}, std::move(flow)); + FML_DLOG(ERROR) << "Could not acquire IO manager."; + result({}, std::move(flow)); + return; } // If the IO manager does not have a resource context, the caller @@ -294,8 +315,8 @@ void ImageDecoder::Decode(fml::RefPtr descriptor, auto uploaded = UploadRasterImage(std::move(decompressed), io_manager, flow); - if (!uploaded.get()) { - FML_LOG(ERROR) << "Could not upload image to the GPU."; + if (!uploaded.skia_object()) { + FML_DLOG(ERROR) << "Could not upload image to the GPU."; result({}, std::move(flow)); return; } diff --git a/lib/ui/painting/image_decoder.h b/lib/ui/painting/image_decoder.h index dbcf4701d04ad..e5f2fba6df1ad 100644 --- a/lib/ui/painting/image_decoder.h +++ b/lib/ui/painting/image_decoder.h @@ -61,7 +61,7 @@ class ImageDecoder { FML_DISALLOW_COPY_AND_ASSIGN(ImageDecoder); }; -sk_sp ImageFromCompressedData(fml::RefPtr descriptor, +sk_sp ImageFromCompressedData(ImageDescriptor* descriptor, uint32_t target_width, uint32_t target_height, const fml::tracing::TraceFlow& flow); diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index 0a6dfbaed4dea..7cb1b6fa902d6 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc @@ -16,7 +16,6 @@ #include "flutter/testing/test_dart_native_resolver.h" #include "flutter/testing/test_gl_surface.h" #include "flutter/testing/testing.h" -#include "third_party/skia/include/codec/SkCodec.h" namespace flutter { namespace testing { @@ -40,7 +39,7 @@ class TestIOManager final : public IOManager { weak_factory_(this) { FML_CHECK(task_runner->RunsTasksOnCurrentThread()) << "The IO manager must be initialized its primary task runner. The " - "test harness may not be setup correctly/safely."; + "test harness may not be set up correctly/safely."; weak_prototype_ = weak_factory_.GetWeakPtr(); } @@ -71,7 +70,7 @@ class TestIOManager final : public IOManager { } // |IOManager| - std::shared_ptr GetIsGpuDisabledSyncSwitch() override { + std::shared_ptr GetIsGpuDisabledSyncSwitch() override { did_access_is_gpu_disabled_sync_switch_ = true; return is_gpu_disabled_sync_switch_; } @@ -145,6 +144,37 @@ TEST_F(ImageDecoderFixtureTest, CanCreateImageDecoder) { latch.Wait(); } +/// An Image generator that pretends it can't recognize the data it was given. +class UnknownImageGenerator : public ImageGenerator { + public: + UnknownImageGenerator() : info_(SkImageInfo::MakeUnknown()){}; + ~UnknownImageGenerator() = default; + const SkImageInfo& GetInfo() { return info_; } + + unsigned int GetFrameCount() const { return 1; } + + unsigned int GetPlayCount() const { return 1; } + + const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) const { + return {std::nullopt, 0, SkCodecAnimation::DisposalMethod::kKeep}; + } + + SkISize GetScaledDimensions(float scale) { + return SkISize::Make(info_.width(), info_.height()); + } + + bool GetPixels(const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index, + std::optional prior_frame) { + return false; + }; + + private: + SkImageInfo info_; +}; + TEST_F(ImageDecoderFixtureTest, InvalidImageResultsError) { auto loop = fml::ConcurrentMessageLoop::Create(); auto thread_task_runner = CreateNewThread(); @@ -165,12 +195,12 @@ TEST_F(ImageDecoderFixtureTest, InvalidImageResultsError) { ASSERT_FALSE(data); fml::RefPtr image_descriptor = - fml::MakeRefCounted(std::move(data), - std::unique_ptr(nullptr)); + fml::MakeRefCounted( + std::move(data), std::make_unique()); ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); - ASSERT_FALSE(image.get()); + ASSERT_FALSE(image.skia_object()); latch.Signal(); }; decoder.Decode(image_descriptor, 0, 0, callback); @@ -205,15 +235,17 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) { ASSERT_TRUE(data); ASSERT_GE(data->size(), 0u); - std::unique_ptr codec = SkCodec::MakeFromData(data); - ASSERT_TRUE(codec); + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); - auto descriptor = - fml::MakeRefCounted(std::move(data), std::move(codec)); + auto descriptor = fml::MakeRefCounted( + std::move(data), std::move(generator)); ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); - ASSERT_TRUE(image.get()); + ASSERT_TRUE(image.skia_object()); EXPECT_TRUE(io_manager->did_access_is_gpu_disabled_sync_switch_); runners.GetIOTaskRunner()->PostTask(release_io_manager); }; @@ -260,16 +292,18 @@ TEST_F(ImageDecoderFixtureTest, ExifDataIsRespectedOnDecode) { ASSERT_TRUE(data); ASSERT_GE(data->size(), 0u); - std::unique_ptr codec = SkCodec::MakeFromData(data); - ASSERT_TRUE(codec); + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); - auto descriptor = - fml::MakeRefCounted(std::move(data), std::move(codec)); + auto descriptor = fml::MakeRefCounted( + std::move(data), std::move(generator)); ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); - ASSERT_TRUE(image.get()); - decoded_size = image.get()->dimensions(); + ASSERT_TRUE(image.skia_object()); + decoded_size = image.skia_object()->dimensions(); runners.GetIOTaskRunner()->PostTask(release_io_manager); }; image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(), @@ -317,15 +351,17 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithoutAGPUContext) { ASSERT_TRUE(data); ASSERT_GE(data->size(), 0u); - std::unique_ptr codec = SkCodec::MakeFromData(data); - ASSERT_TRUE(codec); + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); - auto descriptor = - fml::MakeRefCounted(std::move(data), std::move(codec)); + auto descriptor = fml::MakeRefCounted( + std::move(data), std::move(generator)); ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); - ASSERT_TRUE(image.get()); + ASSERT_TRUE(image.skia_object()); runners.GetIOTaskRunner()->PostTask(release_io_manager); }; image_decoder->Decode(descriptor, descriptor->width(), descriptor->height(), @@ -390,16 +426,18 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithResizes) { ASSERT_TRUE(data); ASSERT_GE(data->size(), 0u); - std::unique_ptr codec = SkCodec::MakeFromData(data); - ASSERT_TRUE(codec); + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); - auto descriptor = fml::MakeRefCounted(std::move(data), - std::move(codec)); + auto descriptor = fml::MakeRefCounted( + std::move(data), std::move(generator)); ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); - ASSERT_TRUE(image.get()); - final_size = image.get()->dimensions(); + ASSERT_TRUE(image.skia_object()); + final_size = image.skia_object()->dimensions(); latch.Signal(); }; image_decoder->Decode(descriptor, target_width, target_height, callback); @@ -427,7 +465,9 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithResizes) { latch.Wait(); } -TEST_F(ImageDecoderFixtureTest, CanResizeWithoutDecode) { +// TODO(https://github.com/flutter/flutter/issues/81232) - disabled due to +// flakiness +TEST_F(ImageDecoderFixtureTest, DISABLED_CanResizeWithoutDecode) { SkImageInfo info = {}; size_t row_bytes; sk_sp decompressed_data; @@ -494,8 +534,8 @@ TEST_F(ImageDecoderFixtureTest, CanResizeWithoutDecode) { ImageDecoder::ImageResult callback = [&](SkiaGPUObject image) { ASSERT_TRUE(runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); - ASSERT_TRUE(image.get()); - final_size = image.get()->dimensions(); + ASSERT_TRUE(image.skia_object()); + final_size = image.skia_object()->dimensions(); latch.Signal(); }; image_decoder->Decode(descriptor, target_width, target_height, callback); @@ -533,16 +573,17 @@ TEST(ImageDecoderTest, ASSERT_TRUE(gif_mapping); ASSERT_TRUE(webp_mapping); - auto gif_codec = SkCodec::MakeFromData(gif_mapping); - auto webp_codec = SkCodec::MakeFromData(webp_mapping); + ImageGeneratorRegistry registry; - ASSERT_TRUE(gif_codec); - ASSERT_TRUE(webp_codec); + auto gif_generator = registry.CreateCompatibleGenerator(gif_mapping); + auto webp_generator = registry.CreateCompatibleGenerator(webp_mapping); - // Both fixtures have a loop count of 2 which should lead to the repeat count - // of 1 - ASSERT_EQ(gif_codec->getRepetitionCount(), 1); - ASSERT_EQ(webp_codec->getRepetitionCount(), 1); + ASSERT_TRUE(gif_generator); + ASSERT_TRUE(webp_generator); + + // Both fixtures have a loop count of 2. + ASSERT_EQ(gif_generator->GetPlayCount(), static_cast(2)); + ASSERT_EQ(webp_generator->GetPlayCount(), static_cast(2)); } TEST(ImageDecoderTest, VerifySimpleDecoding) { @@ -551,31 +592,37 @@ TEST(ImageDecoderTest, VerifySimpleDecoding) { ASSERT_TRUE(image != nullptr); ASSERT_EQ(SkISize::Make(600, 200), image->dimensions()); - auto codec = SkCodec::MakeFromData(data); - ASSERT_TRUE(codec); - auto descriptor = - fml::MakeRefCounted(std::move(data), std::move(codec)); + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); - ASSERT_EQ( - ImageFromCompressedData(descriptor, 6, 2, fml::tracing::TraceFlow("")) - ->dimensions(), - SkISize::Make(6, 2)); + auto descriptor = fml::MakeRefCounted(std::move(data), + std::move(generator)); + + ASSERT_EQ(ImageFromCompressedData(descriptor.get(), 6, 2, + fml::tracing::TraceFlow("")) + ->dimensions(), + SkISize::Make(6, 2)); } TEST(ImageDecoderTest, VerifySubpixelDecodingPreservesExifOrientation) { auto data = OpenFixtureAsSkData("Horizontal.jpg"); - auto codec = SkCodec::MakeFromData(data); - ASSERT_TRUE(codec); + + ImageGeneratorRegistry registry; + std::shared_ptr generator = + registry.CreateCompatibleGenerator(data); + ASSERT_TRUE(generator); auto descriptor = - fml::MakeRefCounted(data, std::move(codec)); + fml::MakeRefCounted(data, std::move(generator)); auto image = SkImage::MakeFromEncoded(data); ASSERT_TRUE(image != nullptr); ASSERT_EQ(SkISize::Make(600, 200), image->dimensions()); auto decode = [descriptor](uint32_t target_width, uint32_t target_height) { - return ImageFromCompressedData(descriptor, target_width, target_height, - fml::tracing::TraceFlow("")); + return ImageFromCompressedData(descriptor.get(), target_width, + target_height, fml::tracing::TraceFlow("")); }; auto expected_data = OpenFixtureAsSkData("Horizontal.png"); @@ -614,10 +661,10 @@ TEST_F(ImageDecoderFixtureTest, ASSERT_TRUE(gif_mapping); - auto gif_codec = std::shared_ptr( - static_cast( - SkCodecImageGenerator::MakeFromEncodedCodec(gif_mapping).release())); - ASSERT_TRUE(gif_codec); + ImageGeneratorRegistry registry; + std::shared_ptr gif_generator = + registry.CreateCompatibleGenerator(gif_mapping); + ASSERT_TRUE(gif_generator); TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform @@ -637,9 +684,9 @@ TEST_F(ImageDecoderFixtureTest, }); latch.Wait(); - auto isolate = - RunDartCodeInIsolate(vm_ref, settings, runners, "main", {}, - GetFixturesPath(), io_manager->GetWeakIOManager()); + auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "main", {}, + GetDefaultKernelFilePath(), + io_manager->GetWeakIOManager()); // Latch the IO task runner. runners.GetIOTaskRunner()->PostTask([&]() { io_latch.Wait(); }); @@ -660,7 +707,7 @@ TEST_F(ImageDecoderFixtureTest, return false; } - codec = fml::MakeRefCounted(std::move(gif_codec)); + codec = fml::MakeRefCounted(std::move(gif_generator)); codec->getNextFrame(closure); codec = nullptr; isolate_latch.Signal(); diff --git a/lib/ui/painting/image_descriptor.cc b/lib/ui/painting/image_descriptor.cc index 76cd577c13c0c..75f0ec1a93e57 100644 --- a/lib/ui/painting/image_descriptor.cc +++ b/lib/ui/painting/image_descriptor.cc @@ -7,27 +7,12 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" -#include "flutter/lib/ui/painting/codec.h" -#include "flutter/lib/ui/painting/image_decoder.h" #include "flutter/lib/ui/painting/multi_frame_codec.h" #include "flutter/lib/ui/painting/single_frame_codec.h" #include "flutter/lib/ui/ui_dart_state.h" #include "third_party/tonic/dart_binding_macros.h" #include "third_party/tonic/logging/dart_invoke.h" -#ifdef OS_MACOSX -#include "third_party/skia/include/ports/SkImageGeneratorCG.h" -#define PLATFORM_IMAGE_GENERATOR(data) \ - SkImageGeneratorCG::MakeFromEncodedCG(data) -#elif OS_WIN -#include "third_party/skia/include/ports/SkImageGeneratorWIC.h" -#define PLATFORM_IMAGE_GENERATOR(data) \ - SkImageGeneratorWIC::MakeFromEncodedWIC(data) -#else -#define PLATFORM_IMAGE_GENERATOR(data) \ - std::unique_ptr(nullptr) -#endif - namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, ImageDescriptor); @@ -37,7 +22,8 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, ImageDescriptor); V(ImageDescriptor, instantiateCodec) \ V(ImageDescriptor, width) \ V(ImageDescriptor, height) \ - V(ImageDescriptor, bytesPerPixel) + V(ImageDescriptor, bytesPerPixel) \ + V(ImageDescriptor, dispose) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) @@ -48,13 +34,8 @@ void ImageDescriptor::RegisterNatives(tonic::DartLibraryNatives* natives) { } const SkImageInfo ImageDescriptor::CreateImageInfo() const { - if (generator_) { - return generator_->getInfo(); - } - if (platform_image_generator_) { - return platform_image_generator_->getInfo(); - } - return SkImageInfo::MakeUnknown(); + FML_DCHECK(generator_); + return generator_->GetInfo(); } ImageDescriptor::ImageDescriptor(sk_sp buffer, @@ -62,26 +43,13 @@ ImageDescriptor::ImageDescriptor(sk_sp buffer, std::optional row_bytes) : buffer_(std::move(buffer)), generator_(nullptr), - platform_image_generator_(nullptr), image_info_(std::move(image_info)), row_bytes_(row_bytes) {} ImageDescriptor::ImageDescriptor(sk_sp buffer, - std::unique_ptr codec) + std::shared_ptr generator) : buffer_(std::move(buffer)), - generator_(std::shared_ptr( - static_cast( - SkCodecImageGenerator::MakeFromCodec(std::move(codec)) - .release()))), - platform_image_generator_(nullptr), - image_info_(CreateImageInfo()), - row_bytes_(std::nullopt) {} - -ImageDescriptor::ImageDescriptor(sk_sp buffer, - std::unique_ptr generator) - : buffer_(std::move(buffer)), - generator_(nullptr), - platform_image_generator_(std::move(generator)), + generator_(std::move(generator)), image_info_(CreateImageInfo()), row_bytes_(std::nullopt) {} @@ -103,27 +71,30 @@ void ImageDescriptor::initEncoded(Dart_NativeArguments args) { return; } - // This call will succeed if Skia has a built-in codec for this. - // If it fails, we will check if the platform knows how to decode this image. - std::unique_ptr codec = - SkCodec::MakeFromData(immutable_buffer->data()); - fml::RefPtr descriptor; - if (!codec) { - std::unique_ptr generator = - PLATFORM_IMAGE_GENERATOR(immutable_buffer->data()); - if (!generator) { - // We don't have a Skia codec for this image, and the platform doesn't - // know how to decode it. - Dart_SetReturnValue(args, tonic::ToDart("Invalid image data")); - return; - } - descriptor = fml::MakeRefCounted(immutable_buffer->data(), - std::move(generator)); - } else { - descriptor = fml::MakeRefCounted(immutable_buffer->data(), - std::move(codec)); + // This has to be valid because this method is called from Dart. + auto dart_state = UIDartState::Current(); + auto registry = dart_state->GetImageGeneratorRegistry(); + + if (!registry) { + Dart_SetReturnValue( + args, tonic::ToDart("Failed to access the internal image decoder " + "registry on this isolate. Please file a bug on " + "https://github.com/flutter/flutter/issues.")); + return; + } + + auto generator = + registry->CreateCompatibleGenerator(immutable_buffer->data()); + + if (!generator) { + // No compatible image decoder was found. + Dart_SetReturnValue(args, tonic::ToDart("Invalid image data")); + return; } + auto descriptor = fml::MakeRefCounted( + immutable_buffer->data(), std::move(generator)); + FML_DCHECK(descriptor); descriptor->AssociateWithDartWrapper(descriptor_handle); @@ -158,7 +129,7 @@ void ImageDescriptor::instantiateCodec(Dart_Handle codec_handle, int target_width, int target_height) { fml::RefPtr ui_codec; - if (!generator_ || generator_->getFrameCount() == 1) { + if (!generator_ || generator_->GetFrameCount() == 1) { ui_codec = fml::MakeRefCounted( static_cast>(this), target_width, target_height); @@ -169,29 +140,13 @@ void ImageDescriptor::instantiateCodec(Dart_Handle codec_handle, } sk_sp ImageDescriptor::image() const { - SkBitmap bitmap; - if (!bitmap.tryAllocPixels(image_info_)) { - FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size " - << image_info_.computeMinByteSize() << "B"; - return nullptr; - } - - const auto& pixmap = bitmap.pixmap(); - if (!get_pixels(pixmap)) { - FML_LOG(ERROR) << "Failed to get pixels for image."; - return nullptr; - } - bitmap.setImmutable(); - return SkImage::MakeFromBitmap(bitmap); + return generator_->GetImage(); } bool ImageDescriptor::get_pixels(const SkPixmap& pixmap) const { - if (generator_) { - return generator_->getPixels(pixmap.info(), pixmap.writable_addr(), - pixmap.rowBytes()); - } - FML_DCHECK(platform_image_generator_); - return platform_image_generator_->getPixels(pixmap); + FML_DCHECK(generator_); + return generator_->GetPixels(pixmap.info(), pixmap.writable_addr(), + pixmap.rowBytes()); } } // namespace flutter diff --git a/lib/ui/painting/image_descriptor.h b/lib/ui/painting/image_descriptor.h index f644f29c48c53..10e6c150137f9 100644 --- a/lib/ui/painting/image_descriptor.h +++ b/lib/ui/painting/image_descriptor.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/image_generator_registry.h" #include "flutter/lib/ui/painting/immutable_buffer.h" #include "third_party/skia/include/codec/SkCodec.h" #include "third_party/skia/include/core/SkImageGenerator.h" @@ -20,13 +21,13 @@ namespace flutter { -/// Creates an image descriptor for encoded or decoded image data, describing -/// the width, height, and bytes per pixel for that image. -/// -/// This class will hold a reference on the underlying image data, and in the -/// case of compressed data, an SkCodec and SkImageGenerator for the data. -/// The Codec initialization actually happens in initEncoded, making -/// initstantiateCodec a lightweight operation. +/// @brief Creates an image descriptor for encoded or decoded image data, +/// describing the width, height, and bytes per pixel for that image. +/// This class will hold a reference on the underlying image data, and +/// in the case of compressed data, an `ImageGenerator` for the data. +/// The Codec initialization actually happens in initEncoded, making +/// `initstantiateCodec` a lightweight operation. +/// @see `ImageGenerator` class ImageDescriptor : public RefCountedDartWrappable { public: ~ImageDescriptor() override = default; @@ -37,15 +38,16 @@ class ImageDescriptor : public RefCountedDartWrappable { kBGRA8888, }; - /// Asynchronously initlializes an ImageDescriptor for an encoded image, as - /// long as the format is supported by Skia. - /// - /// Calling this method will result in creating an SkCodec and - /// SkImageGenerator to read EXIF corrected dimensions from the image data. + /// @brief Asynchronously initlializes an ImageDescriptor for an encoded + /// image, as long as the format is recognized by an encoder installed + /// in the `ImageGeneratorRegistry`. Calling this method will create + /// an `ImageGenerator` and read EXIF corrected dimensions from the + /// image data. + /// @see `ImageGeneratorRegistry` static void initEncoded(Dart_NativeArguments args); - /// Synchronously initializes an ImageDescriptor for decompressed image data - /// as specified by the PixelFormat. + /// @brief Synchronously initializes an `ImageDescriptor` for decompressed + /// image data as specified by the `PixelFormat`. static void initRaw(Dart_Handle descriptor_handle, fml::RefPtr data, int width, @@ -53,60 +55,61 @@ class ImageDescriptor : public RefCountedDartWrappable { int row_bytes, PixelFormat pixel_format); - /// Associates a flutter::Codec object with the dart.ui Codec handle. + /// @brief Associates a flutter::Codec object with the dart.ui Codec handle. void instantiateCodec(Dart_Handle codec, int target_width, int target_height); - /// The width of this image, EXIF oriented if applicable. + /// @brief The width of this image, EXIF oriented if applicable. int width() const { return image_info_.width(); } - /// The height of this image. EXIF oriented if applicable. + /// @brief The height of this image. EXIF oriented if applicable. int height() const { return image_info_.height(); } - /// The bytes per pixel of the image. + /// @brief The bytes per pixel of the image. int bytesPerPixel() const { return image_info_.bytesPerPixel(); } - /// The byte length of the first row of the image. - /// - /// Defaults to width() * 4. + /// @brief The byte length of the first row of the image. + /// Defaults to width() * 4. int row_bytes() const { return row_bytes_.value_or( static_cast(image_info_.width() * image_info_.bytesPerPixel())); } - /// Whether the given target_width or target_height differ from width() and - /// height() respectively. + /// @brief Whether the given `target_width` or `target_height` differ from + /// `width()` and `height()` respectively. bool should_resize(int target_width, int target_height) const { return target_width != width() || target_height != height(); } - /// The underlying buffer for this image. + /// @brief The underlying buffer for this image. sk_sp data() const { return buffer_; } sk_sp image() const; - /// Whether this descriptor represents compressed (encoded) data or not. - bool is_compressed() const { return generator_ || platform_image_generator_; } + /// @brief Whether this descriptor represents compressed (encoded) data or + /// not. + bool is_compressed() const { return !!generator_; } - /// The orientation corrected image info for this image. + /// @brief The orientation corrected image info for this image. const SkImageInfo& image_info() const { return image_info_; } - /// Gets the scaled dimensions of this image, if backed by a codec that can - /// perform efficient subpixel scaling. + /// @brief Gets the scaled dimensions of this image, if backed by an + /// `ImageGenerator` that can perform efficient subpixel scaling. + /// @see `ImageGenerator::GetScaledDimensions` SkISize get_scaled_dimensions(float scale) { if (generator_) { - return generator_->getScaledDimensions(scale); + return generator_->GetScaledDimensions(scale); } return image_info_.dimensions(); } - /// Gets pixels for this image transformed based on the EXIF orientation tag, - /// if applicable. + /// @brief Gets pixels for this image transformed based on the EXIF + /// orientation tag, if applicable. bool get_pixels(const SkPixmap& pixmap) const; void dispose() { - ClearDartWrapper(); + buffer_.reset(); generator_.reset(); - platform_image_generator_.reset(); + ClearDartWrapper(); } size_t GetAllocationSize() const override { @@ -119,13 +122,11 @@ class ImageDescriptor : public RefCountedDartWrappable { ImageDescriptor(sk_sp buffer, const SkImageInfo& image_info, std::optional row_bytes); - ImageDescriptor(sk_sp buffer, std::unique_ptr codec); ImageDescriptor(sk_sp buffer, - std::unique_ptr generator); + std::shared_ptr generator); sk_sp buffer_; - std::shared_ptr generator_; - std::unique_ptr platform_image_generator_; + std::shared_ptr generator_; const SkImageInfo image_info_; std::optional row_bytes_; diff --git a/lib/ui/painting/image_dispose_unittests.cc b/lib/ui/painting/image_dispose_unittests.cc index 63737cc3d0fec..3a673328932ea 100644 --- a/lib/ui/painting/image_dispose_unittests.cc +++ b/lib/ui/painting/image_dispose_unittests.cc @@ -6,8 +6,10 @@ #include "flutter/common/task_runners.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/picture.h" +#include "flutter/lib/ui/painting/picture_recorder.h" #include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/shell_test.h" #include "flutter/shell/common/thread_host.h" @@ -30,17 +32,12 @@ class ImageDisposeTest : public ShellTest { // Used to wait on Dart callbacks or Shell task runner flushing fml::AutoResetWaitableEvent message_latch_; - fml::AutoResetWaitableEvent picture_finalizer_latch_; - static void picture_finalizer(void* isolate_callback_data, void* peer) { - auto latch = reinterpret_cast(peer); - latch->Signal(); - } - sk_sp current_picture_; + sk_sp current_display_list_; sk_sp current_image_; }; -TEST_F(ImageDisposeTest, ImageReleasedAfterFrame) { +TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { auto native_capture_image_and_picture = [&](Dart_NativeArguments args) { auto image_handle = Dart_GetNativeArgument(args, 0); auto native_image_handle = @@ -51,15 +48,17 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrame) { CanvasImage* image = GetNativePeer(native_image_handle); Picture* picture = GetNativePeer(Dart_GetNativeArgument(args, 1)); ASSERT_FALSE(image->image()->unique()); - ASSERT_FALSE(picture->picture()->unique()); + if (picture->display_list()) { + ASSERT_FALSE(picture->display_list()->unique()); + current_display_list_ = picture->display_list(); + } else { + ASSERT_FALSE(picture->picture()->unique()); + current_picture_ = picture->picture(); + } current_image_ = image->image(); - current_picture_ = picture->picture(); - - Dart_NewFinalizableHandle(Dart_GetNativeArgument(args, 1), - &picture_finalizer_latch_, 0, &picture_finalizer); }; - auto native_on_begin_frame_done = [&](Dart_NativeArguments args) { + auto native_finish = [&](Dart_NativeArguments args) { message_latch_.Signal(); }; @@ -74,8 +73,7 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrame) { AddNativeCallback("CaptureImageAndPicture", CREATE_NATIVE_ENTRY(native_capture_image_and_picture)); - AddNativeCallback("OnBeginFrameDone", - CREATE_NATIVE_ENTRY(native_on_begin_frame_done)); + AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(native_finish)); std::unique_ptr shell = CreateShell(std::move(settings), task_runners); @@ -94,18 +92,11 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrame) { message_latch_.Wait(); - ASSERT_TRUE(current_picture_); + ASSERT_TRUE(current_display_list_ || current_picture_); ASSERT_TRUE(current_image_); - // Simulate a large notify idle, as the animator would do - // when it has no frames left. - // On slower machines, this is especially important - we capture that - // this happens normally in devicelab bnechmarks like large_image_changer. - NotifyIdle(shell.get(), Dart_TimelineGetMicros() + 100000); - - picture_finalizer_latch_.Wait(); - - // Force a drain the SkiaUnrefQueue. + // Force a drain the SkiaUnrefQueue. The engine does this normally as frames + // pump, but we force it here to make the test more deterministic. message_latch_.Reset(); task_runner->PostTask([&, io_manager = shell->GetIOManager()]() { io_manager->GetSkiaUnrefQueue()->Drain(); @@ -113,8 +104,13 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrame) { }); message_latch_.Wait(); - EXPECT_TRUE(current_picture_->unique()); - current_picture_.reset(); + if (current_display_list_) { + EXPECT_TRUE(current_display_list_->unique()); + current_display_list_.reset(); + } else { + EXPECT_TRUE(current_picture_->unique()); + current_picture_.reset(); + } EXPECT_TRUE(current_image_->unique()); current_image_.reset(); diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index c17a784c54caf..90dbbb6f34bc0 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -35,9 +35,7 @@ enum ImageByteFormat { kPNG, }; -void FinalizeSkData(void* isolate_callback_data, - Dart_WeakPersistentHandle handle, - void* peer) { +void FinalizeSkData(void* isolate_callback_data, void* peer) { SkData* buffer = reinterpret_cast(peer); buffer->unref(); } @@ -174,7 +172,7 @@ sk_sp CopyImageByteData(sk_sp raster_image, color_type, kPremul_SkAlphaType, nullptr)); if (!surface) { - FML_LOG(ERROR) << "Could not setup the surface for swizzle."; + FML_LOG(ERROR) << "Could not set up the surface for swizzle."; return nullptr; } diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index 75b883cb48c04..2b20b22ac5f6e 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -5,9 +5,7 @@ #include "flutter/lib/ui/painting/image_filter.h" #include "flutter/lib/ui/painting/matrix.h" -#include "third_party/skia/include/effects/SkBlurImageFilter.h" -#include "third_party/skia/include/effects/SkImageSource.h" -#include "third_party/skia/include/effects/SkPictureImageFilter.h" +#include "third_party/skia/include/effects/SkImageFilters.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" @@ -22,11 +20,13 @@ static void ImageFilter_constructor(Dart_NativeArguments args) { IMPLEMENT_WRAPPERTYPEINFO(ui, ImageFilter); -#define FOR_EACH_BINDING(V) \ - V(ImageFilter, initImage) \ - V(ImageFilter, initPicture) \ - V(ImageFilter, initBlur) \ - V(ImageFilter, initMatrix) +#define FOR_EACH_BINDING(V) \ + V(ImageFilter, initImage) \ + V(ImageFilter, initPicture) \ + V(ImageFilter, initBlur) \ + V(ImageFilter, initMatrix) \ + V(ImageFilter, initColorFilter) \ + V(ImageFilter, initComposeFilter) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) @@ -40,28 +40,63 @@ fml::RefPtr ImageFilter::Create() { return fml::MakeRefCounted(); } +static const std::array filter_qualities = { + SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), + SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}), +}; + +SkSamplingOptions ImageFilter::SamplingFromIndex(int filterQualityIndex) { + if (filterQualityIndex < 0) { + return filter_qualities.front(); + } else if (((size_t)filterQualityIndex) >= filter_qualities.size()) { + return filter_qualities.back(); + } else { + return filter_qualities[filterQualityIndex]; + } +} + +SkFilterMode ImageFilter::FilterModeFromIndex(int filterQualityIndex) { + if (filterQualityIndex <= 0) { + return SkFilterMode::kNearest; + } + return SkFilterMode::kLinear; +} + ImageFilter::ImageFilter() {} ImageFilter::~ImageFilter() {} void ImageFilter::initImage(CanvasImage* image) { - filter_ = SkImageSource::Make(image->image()); + filter_ = SkImageFilters::Image(image->image()); } void ImageFilter::initPicture(Picture* picture) { - filter_ = SkPictureImageFilter::Make(picture->picture()); + filter_ = SkImageFilters::Picture(picture->picture()); } -void ImageFilter::initBlur(double sigma_x, double sigma_y) { - filter_ = SkBlurImageFilter::Make(sigma_x, sigma_y, nullptr, nullptr, - SkBlurImageFilter::kClamp_TileMode); +void ImageFilter::initBlur(double sigma_x, + double sigma_y, + SkTileMode tile_mode) { + filter_ = SkImageFilters::Blur(sigma_x, sigma_y, tile_mode, nullptr, nullptr); } void ImageFilter::initMatrix(const tonic::Float64List& matrix4, - int filterQuality) { - filter_ = SkImageFilter::MakeMatrixFilter( - ToSkMatrix(matrix4), static_cast(filterQuality), - nullptr); + int filterQualityIndex) { + auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex); + filter_ = + SkImageFilters::MatrixTransform(ToSkMatrix(matrix4), sampling, nullptr); +} + +void ImageFilter::initColorFilter(ColorFilter* colorFilter) { + filter_ = SkImageFilters::ColorFilter( + colorFilter ? colorFilter->filter() : nullptr, nullptr); +} + +void ImageFilter::initComposeFilter(ImageFilter* outer, ImageFilter* inner) { + filter_ = SkImageFilters::Compose(outer ? outer->filter() : nullptr, + inner ? inner->filter() : nullptr); } } // namespace flutter diff --git a/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index c44243016b9cc..32a8c1f229802 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -6,6 +6,7 @@ #define FLUTTER_LIB_UI_PAINTING_IMAGE_FILTER_H_ #include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/color_filter.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/picture.h" #include "third_party/skia/include/core/SkImageFilter.h" @@ -21,10 +22,15 @@ class ImageFilter : public RefCountedDartWrappable { ~ImageFilter() override; static fml::RefPtr Create(); + static SkSamplingOptions SamplingFromIndex(int filterQualityIndex); + static SkFilterMode FilterModeFromIndex(int index); + void initImage(CanvasImage* image); void initPicture(Picture*); - void initBlur(double sigma_x, double sigma_y); - void initMatrix(const tonic::Float64List& matrix4, int filter_quality); + void initBlur(double sigma_x, double sigma_y, SkTileMode tile_mode); + void initMatrix(const tonic::Float64List& matrix4, int filter_quality_index); + void initColorFilter(ColorFilter* colorFilter); + void initComposeFilter(ImageFilter* outer, ImageFilter* inner); const sk_sp& filter() const { return filter_; } diff --git a/lib/ui/painting/image_generator.cc b/lib/ui/painting/image_generator.cc new file mode 100644 index 0000000000000..015713dcd453d --- /dev/null +++ b/lib/ui/painting/image_generator.cc @@ -0,0 +1,143 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/painting/image_generator.h" + +#include "flutter/fml/logging.h" + +namespace flutter { + +ImageGenerator::~ImageGenerator() = default; + +sk_sp ImageGenerator::GetImage() { + SkImageInfo info = GetInfo(); + + SkBitmap bitmap; + if (!bitmap.tryAllocPixels(info)) { + FML_DLOG(ERROR) << "Failed to allocate memory for bitmap of size " + << info.computeMinByteSize() << "B"; + return nullptr; + } + + const auto& pixmap = bitmap.pixmap(); + if (!GetPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes())) { + FML_DLOG(ERROR) << "Failed to get pixels for image."; + return nullptr; + } + bitmap.setImmutable(); + return SkImage::MakeFromBitmap(bitmap); +} + +BuiltinSkiaImageGenerator::~BuiltinSkiaImageGenerator() = default; + +BuiltinSkiaImageGenerator::BuiltinSkiaImageGenerator( + std::unique_ptr generator) + : generator_(std::move(generator)) {} + +const SkImageInfo& BuiltinSkiaImageGenerator::GetInfo() { + return generator_->getInfo(); +} + +unsigned int BuiltinSkiaImageGenerator::GetFrameCount() const { + return 1; +} + +unsigned int BuiltinSkiaImageGenerator::GetPlayCount() const { + return 1; +} + +const ImageGenerator::FrameInfo BuiltinSkiaImageGenerator::GetFrameInfo( + unsigned int frame_index) const { + return {.required_frame = std::nullopt, + .duration = 0, + .disposal_method = SkCodecAnimation::DisposalMethod::kKeep}; +} + +SkISize BuiltinSkiaImageGenerator::GetScaledDimensions(float desired_scale) { + return generator_->getInfo().dimensions(); +} + +bool BuiltinSkiaImageGenerator::GetPixels( + const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index, + std::optional prior_frame) { + return generator_->getPixels(info, pixels, row_bytes); +} + +std::unique_ptr BuiltinSkiaImageGenerator::MakeFromGenerator( + std::unique_ptr generator) { + if (!generator) { + return nullptr; + } + return std::make_unique(std::move(generator)); +} + +BuiltinSkiaCodecImageGenerator::~BuiltinSkiaCodecImageGenerator() = default; + +BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator( + std::unique_ptr codec) + : codec_generator_(static_cast( + SkCodecImageGenerator::MakeFromCodec(std::move(codec)).release())) {} + +BuiltinSkiaCodecImageGenerator::BuiltinSkiaCodecImageGenerator( + sk_sp buffer) + : codec_generator_(static_cast( + SkCodecImageGenerator::MakeFromEncodedCodec(buffer).release())) {} + +const SkImageInfo& BuiltinSkiaCodecImageGenerator::GetInfo() { + return codec_generator_->getInfo(); +} + +unsigned int BuiltinSkiaCodecImageGenerator::GetFrameCount() const { + return codec_generator_->getFrameCount(); +} + +unsigned int BuiltinSkiaCodecImageGenerator::GetPlayCount() const { + auto repetition_count = codec_generator_->getRepetitionCount(); + return repetition_count < 0 ? kInfinitePlayCount : repetition_count + 1; +} + +const ImageGenerator::FrameInfo BuiltinSkiaCodecImageGenerator::GetFrameInfo( + unsigned int frame_index) const { + SkCodec::FrameInfo info; + codec_generator_->getFrameInfo(frame_index, &info); + return { + .required_frame = info.fRequiredFrame == SkCodec::kNoFrame + ? std::nullopt + : std::optional(info.fRequiredFrame), + .duration = static_cast(info.fDuration), + .disposal_method = info.fDisposalMethod}; +} + +SkISize BuiltinSkiaCodecImageGenerator::GetScaledDimensions( + float desired_scale) { + return codec_generator_->getScaledDimensions(desired_scale); +} + +bool BuiltinSkiaCodecImageGenerator::GetPixels( + const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index, + std::optional prior_frame) { + SkCodec::Options options; + options.fFrameIndex = frame_index; + if (prior_frame.has_value()) { + options.fPriorFrame = prior_frame.value(); + } + return codec_generator_->getPixels(info, pixels, row_bytes, &options); +} + +std::unique_ptr BuiltinSkiaCodecImageGenerator::MakeFromData( + sk_sp data) { + auto codec = SkCodec::MakeFromData(data); + if (!codec) { + return nullptr; + } + return std::make_unique(std::move(codec)); +} + +} // namespace flutter diff --git a/lib/ui/painting/image_generator.h b/lib/ui/painting/image_generator.h new file mode 100644 index 0000000000000..ae5816374427e --- /dev/null +++ b/lib/ui/painting/image_generator.h @@ -0,0 +1,211 @@ +// 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_LIB_UI_PAINTING_IMAGE_GENERATOR_H_ +#define FLUTTER_LIB_UI_PAINTING_IMAGE_GENERATOR_H_ + +#include +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/src/codec/SkCodecImageGenerator.h" + +namespace flutter { + +/// @brief The minimal interface necessary for defining a decoder that can be +/// used for both single and multi-frame image decoding. Image +/// generators can also optionally support decoding into a subscaled +/// buffer. Implementers of `ImageGenerator` regularly keep internal +/// state which is not thread safe, and so aliasing and parallel access +/// should never be done with `ImageGenerator`s. +/// @see `ImageGenerator::GetScaledDimensions` +class ImageGenerator { + public: + /// Frame count value to denote infinite looping. + const static unsigned int kInfinitePlayCount = + std::numeric_limits::max(); + + /// @brief Info about a single frame in the context of a multi-frame image, + /// useful for animation and blending. + struct FrameInfo { + /// The frame index of the frame that, if any, this frame needs to be + /// blended with. + std::optional required_frame; + + /// Number of milliseconds to show this frame. + unsigned int duration; + + /// How this frame should be modified before decoding the next one. + SkCodecAnimation::DisposalMethod disposal_method; + }; + + virtual ~ImageGenerator(); + + /// @brief Returns basic information about the contents of the encoded + /// image. This information can almost always be collected by just + /// interpreting the header of a decoded image. + /// @return Size and color information describing the image. + /// @note This method is executed on the UI thread and used for layout + /// purposes by the framework, and so this method should not perform + /// long synchronous tasks. + virtual const SkImageInfo& GetInfo() = 0; + + /// @brief Get the number of frames that the encoded image stores. This + /// method is always expected to be called before `GetFrameInfo`, as + /// the underlying image decoder may interpret frame information that + /// is then used when calling `GetFrameInfo`. + /// @return The number of frames that the encoded image stores. This will + /// always be 1 for single-frame images. + virtual unsigned int GetFrameCount() const = 0; + + /// @brief The number of times an animated image should play through before + /// playback stops. + /// @return If this image is animated, the number of times the animation + /// should play through is returned, otherwise it'll just return 1. + /// If the animation should loop forever, `kInfinitePlayCount` is + /// returned. + virtual unsigned int GetPlayCount() const = 0; + + /// @brief Get information about a single frame in the context of a + /// multi-frame image, useful for animation and frame blending. + /// This method should only ever be called after `GetFrameCount` + /// has been called. This information is nonsensical for + /// single-frame images. + /// @param[in] frame_index The index of the frame to get information about. + /// @return Information about the given frame. If the image is + /// single-frame, a default result is returned. + /// @see `GetFrameCount` + virtual const FrameInfo GetFrameInfo(unsigned int frame_index) const = 0; + + /// @brief Given a scale value, find the closest image size that can be + /// used for efficiently decoding the image. If subpixel image + /// decoding is not supported by the decoder, this method should + /// just return the original image size. + /// @param[in] scale The desired scale factor of the image for decoding. + /// @return The closest image size that can be used for efficiently + /// decoding the image. + /// @note This method is called prior to `GetPixels` in order to query + /// for supported sizes. + /// @see `GetPixels` + virtual SkISize GetScaledDimensions(float scale) = 0; + + /// @brief Decode the image into a given buffer. This method is currently + /// always used for sub-pixel image decoding. For full-sized still + /// images, `GetImage` is always attempted first. + /// @param[in] info The desired size and color info of the decoded + /// image to be returned. The implementation of + /// `GetScaledDimensions` determines which sizes are + /// supported by the image decoder. + /// @param[in] pixels The location where the raw decoded image data + /// should be written. + /// @param[in] row_bytes The total number of bytes that should make up a + /// single row of decoded image data + /// (i.e. width * bytes_per_pixel). + /// @param[in] frame_index Which frame to decode. This is only useful for + /// multi-frame images. + /// @param[in] prior_frame Optional frame index parameter for multi-frame + /// images which specifies the previous frame that + /// should be use for blending. This hints to the + /// decoder that it should use a previously cached + /// frame instead of decoding dependency frame(s). + /// If an empty value is supplied, the decoder should + /// decode any necessary frames first. + /// @return True if the image was successfully decoded. + /// @note This method performs potentially long synchronous work, and so + /// it should never be executed on the UI thread. Image decoders + /// do not require GPU acceleration, and so threads without a GPU + /// context may also be used. + /// @see `GetScaledDimensions` + virtual bool GetPixels( + const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index = 0, + std::optional prior_frame = std::nullopt) = 0; + + /// @brief Creates an `SkImage` based on the current `ImageInfo` of this + /// `ImageGenerator`. + /// @return A new `SkImage` containing the decoded image data. + sk_sp GetImage(); +}; + +class BuiltinSkiaImageGenerator : public ImageGenerator { + public: + ~BuiltinSkiaImageGenerator(); + + BuiltinSkiaImageGenerator(std::unique_ptr generator); + + // |ImageGenerator| + const SkImageInfo& GetInfo() override; + + // |ImageGenerator| + unsigned int GetFrameCount() const override; + + // |ImageGenerator| + unsigned int GetPlayCount() const override; + + // |ImageGenerator| + const ImageGenerator::FrameInfo GetFrameInfo( + unsigned int frame_index) const override; + + // |ImageGenerator| + SkISize GetScaledDimensions(float desired_scale) override; + + // |ImageGenerator| + bool GetPixels( + const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index = 0, + std::optional prior_frame = std::nullopt) override; + + static std::unique_ptr MakeFromGenerator( + std::unique_ptr generator); + + private: + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BuiltinSkiaImageGenerator); + std::unique_ptr generator_; +}; + +class BuiltinSkiaCodecImageGenerator : public ImageGenerator { + public: + ~BuiltinSkiaCodecImageGenerator(); + + BuiltinSkiaCodecImageGenerator(std::unique_ptr codec); + + BuiltinSkiaCodecImageGenerator(sk_sp buffer); + + // |ImageGenerator| + const SkImageInfo& GetInfo() override; + + // |ImageGenerator| + unsigned int GetFrameCount() const override; + + // |ImageGenerator| + unsigned int GetPlayCount() const override; + + // |ImageGenerator| + const ImageGenerator::FrameInfo GetFrameInfo( + unsigned int frame_index) const override; + + // |ImageGenerator| + SkISize GetScaledDimensions(float desired_scale) override; + + // |ImageGenerator| + bool GetPixels( + const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index = 0, + std::optional prior_frame = std::nullopt) override; + + static std::unique_ptr MakeFromData(sk_sp data); + + private: + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BuiltinSkiaCodecImageGenerator); + std::unique_ptr codec_generator_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_PAINTING_IMAGE_GENERATOR_H_ diff --git a/lib/ui/painting/image_generator_registry.cc b/lib/ui/painting/image_generator_registry.cc new file mode 100644 index 0000000000000..f48cfd5f8bb50 --- /dev/null +++ b/lib/ui/painting/image_generator_registry.cc @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/fml/trace_event.h" +#include "flutter/lib/ui/painting/image_generator_registry.h" +#include "third_party/skia/include/codec/SkCodec.h" +#include "third_party/skia/include/core/SkImageGenerator.h" +#include "third_party/skia/src/codec/SkCodecImageGenerator.h" +#ifdef OS_MACOSX +#include "third_party/skia/include/ports/SkImageGeneratorCG.h" +#elif OS_WIN +#include "third_party/skia/include/ports/SkImageGeneratorWIC.h" +#endif + +namespace flutter { + +ImageGeneratorRegistry::ImageGeneratorRegistry() : weak_factory_(this) { + AddFactory( + [](sk_sp buffer) { + return BuiltinSkiaCodecImageGenerator::MakeFromData(buffer); + }, + 0); + + // todo(bdero): https://github.com/flutter/flutter/issues/82603 +#ifdef OS_MACOSX + AddFactory( + [](sk_sp buffer) { + auto generator = SkImageGeneratorCG::MakeFromEncodedCG(buffer); + return BuiltinSkiaImageGenerator::MakeFromGenerator( + std::move(generator)); + }, + 0); +#elif OS_WIN + AddFactory( + [](sk_sp buffer) { + auto generator = SkImageGeneratorWIC::MakeFromEncodedWIC(buffer); + return BuiltinSkiaImageGenerator::MakeFromGenerator( + std::move(generator)); + }, + 0); +#endif +} + +ImageGeneratorRegistry::~ImageGeneratorRegistry() = default; + +void ImageGeneratorRegistry::AddFactory(ImageGeneratorFactory factory, + int32_t priority) { + image_generator_factories_.insert( + {factory, priority, fml::tracing::TraceNonce()}); +} + +std::shared_ptr +ImageGeneratorRegistry::CreateCompatibleGenerator(sk_sp buffer) { + if (!image_generator_factories_.size()) { + FML_LOG(WARNING) + << "There are currently no image decoders installed. If you're writing " + "your own platform embedding, you can register new image decoders " + "via `ImageGeneratorRegistry::AddFactory` on the " + "`ImageGeneratorRegistry` provided by the engine. Otherwise, please " + "file a bug on https://github.com/flutter/flutter/issues."; + } + + for (auto& factory : image_generator_factories_) { + std::shared_ptr result = factory.callback(buffer); + if (result) { + return result; + } + } + return nullptr; +} + +fml::WeakPtr ImageGeneratorRegistry::GetWeakPtr() + const { + return weak_factory_.GetWeakPtr(); +} + +} // namespace flutter diff --git a/lib/ui/painting/image_generator_registry.h b/lib/ui/painting/image_generator_registry.h new file mode 100644 index 0000000000000..8c0d7d0f136b5 --- /dev/null +++ b/lib/ui/painting/image_generator_registry.h @@ -0,0 +1,92 @@ +// 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_LIB_UI_PAINTING_IMAGE_GENERATOR_REGISTRY_H_ +#define FLUTTER_LIB_UI_PAINTING_IMAGE_GENERATOR_REGISTRY_H_ + +#include +#include + +#include "flutter/fml/mapping.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/lib/ui/painting/image_generator.h" + +namespace flutter { + +/// @brief `ImageGeneratorFactory` is the top level primitive for specifying an +/// image decoder in Flutter. When called, it should return an +/// `ImageGenerator` that typically compatible with the given input +/// data. +using ImageGeneratorFactory = + std::function(sk_sp buffer)>; + +/// @brief Keeps a priority-ordered registry of image generator builders to be +/// used when decoding images. This object must be created, accessed, and +/// collected on the UI thread (typically the engine or its runtime +/// controller). +class ImageGeneratorRegistry { + public: + ImageGeneratorRegistry(); + + ~ImageGeneratorRegistry(); + + /// @brief Install a new factory for image generators + /// @param[in] factory Callback that produces `ImageGenerator`s for + /// compatible input data. + /// @param[in] priority The priority used to determine the order in which + /// factories are tried. Higher values mean higher + /// priority. The built-in Skia decoders are installed + /// at priority 0, and so a priority > 0 takes precedent + /// over the builtin decoders. When multiple decoders + /// are added with the same priority, those which are + /// added earlier take precedent. + /// @see `CreateCompatibleGenerator` + void AddFactory(ImageGeneratorFactory factory, int32_t priority); + + /// @brief Walks the list of image generator builders in descending + /// priority order until a compatible `ImageGenerator` is able to + /// be built. This method is safe to perform on the UI thread, as + /// checking for `ImageGenerator` compatibility is expected to be + /// a lightweight operation. The returned `ImageGenerator` can + /// then be used to fully decode the image on e.g. the IO thread. + /// @param[in] buffer The raw encoded image data. + /// @return An `ImageGenerator` that is compatible with the input buffer. + /// If no compatible `ImageGenerator` type was found, then + /// `std::shared_ptr(nullptr)` is returned. + /// @see `ImageGenerator` + std::shared_ptr CreateCompatibleGenerator( + sk_sp buffer); + + fml::WeakPtr GetWeakPtr() const; + + private: + struct PrioritizedFactory { + ImageGeneratorFactory callback; + + int32_t priority = 0; + // Used as a fallback priority comparison when equal. + size_t ascending_nonce = 0; + }; + + struct Compare { + constexpr bool operator()(const PrioritizedFactory& lhs, + const PrioritizedFactory& rhs) const { + // When priorities are equal, factories registered earlier take + // precedent. + if (lhs.priority == rhs.priority) { + return lhs.ascending_nonce < rhs.ascending_nonce; + } + // Order by descending priority. + return lhs.priority > rhs.priority; + } + }; + + using FactorySet = std::set; + FactorySet image_generator_factories_; + fml::WeakPtrFactory weak_factory_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_PAINTING_IMAGE_DECODER_H_ diff --git a/lib/ui/painting/image_generator_registry_unittests.cc b/lib/ui/painting/image_generator_registry_unittests.cc new file mode 100644 index 0000000000000..925eb919ba231 --- /dev/null +++ b/lib/ui/painting/image_generator_registry_unittests.cc @@ -0,0 +1,115 @@ +// 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/lib/ui/painting/image_generator_registry.h" + +#include "flutter/fml/mapping.h" +#include "flutter/shell/common/shell_test.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +static sk_sp LoadValidImageFixture() { + auto fixture_mapping = OpenFixtureAsMapping("DashInNooglerHat.jpg"); + + // Remap to sk_sp. + SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void { + delete reinterpret_cast(context); + }; + auto data = SkData::MakeWithProc(fixture_mapping->GetMapping(), + fixture_mapping->GetSize(), on_release, + fixture_mapping.get()); + + if (data) { + fixture_mapping.release(); + } + + return data; +} + +TEST_F(ShellTest, CreateCompatibleReturnsBuiltinImageGeneratorForValidImage) { + auto data = LoadValidImageFixture(); + + // Fetch the generator and query for basic info + ImageGeneratorRegistry registry; + auto result = registry.CreateCompatibleGenerator(data); + auto info = result->GetInfo(); + ASSERT_EQ(info.width(), 3024); + ASSERT_EQ(info.height(), 4032); +} + +TEST_F(ShellTest, CreateCompatibleReturnsNullptrForInvalidImage) { + ImageGeneratorRegistry registry; + auto result = registry.CreateCompatibleGenerator(SkData::MakeEmpty()); + ASSERT_EQ(result, nullptr); +} + +class FakeImageGenerator : public ImageGenerator { + public: + FakeImageGenerator(int identifiableFakeWidth) + : info_(SkImageInfo::Make(identifiableFakeWidth, + identifiableFakeWidth, + SkColorType::kRGBA_8888_SkColorType, + SkAlphaType::kOpaque_SkAlphaType)){}; + ~FakeImageGenerator() = default; + const SkImageInfo& GetInfo() { return info_; } + + unsigned int GetFrameCount() const { return 1; } + + unsigned int GetPlayCount() const { return 1; } + + const ImageGenerator::FrameInfo GetFrameInfo(unsigned int frame_index) const { + return {std::nullopt, 0, SkCodecAnimation::DisposalMethod::kKeep}; + } + + SkISize GetScaledDimensions(float scale) { + return SkISize::Make(info_.width(), info_.height()); + } + + bool GetPixels(const SkImageInfo& info, + void* pixels, + size_t row_bytes, + unsigned int frame_index, + std::optional prior_frame) { + return false; + }; + + private: + SkImageInfo info_; +}; + +TEST_F(ShellTest, PositivePriorityTakesPrecedentOverDefaultGenerators) { + ImageGeneratorRegistry registry; + + const int fake_width = 1337; + registry.AddFactory( + [fake_width](sk_sp buffer) { + return std::make_unique(fake_width); + }, + 1); + + // Fetch the generator and query for basic info. + auto result = registry.CreateCompatibleGenerator(LoadValidImageFixture()); + ASSERT_EQ(result->GetInfo().width(), fake_width); +} + +TEST_F(ShellTest, DefaultGeneratorsTakePrecedentOverNegativePriority) { + ImageGeneratorRegistry registry; + + registry.AddFactory( + [](sk_sp buffer) { + return std::make_unique(1337); + }, + -1); + + // Fetch the generator and query for basic info. + auto result = registry.CreateCompatibleGenerator(LoadValidImageFixture()); + // If the real width of the image pops out, then the default generator was + // returned rather than the fake one. + ASSERT_EQ(result->GetInfo().width(), 3024); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/painting/image_shader.cc b/lib/ui/painting/image_shader.cc index 95c27a6007cbb..d8895280d08e0 100644 --- a/lib/ui/painting/image_shader.cc +++ b/lib/ui/painting/image_shader.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/lib/ui/painting/image_shader.h" +#include "flutter/lib/ui/painting/image_filter.h" #include "flutter/lib/ui/ui_dart_state.h" #include "third_party/tonic/converter/dart_converter.h" @@ -37,15 +38,35 @@ fml::RefPtr ImageShader::Create() { void ImageShader::initWithImage(CanvasImage* image, SkTileMode tmx, SkTileMode tmy, + int filter_quality_index, const tonic::Float64List& matrix4) { if (!image) { Dart_ThrowException( ToDart("ImageShader constructor called with non-genuine Image.")); return; } - SkMatrix sk_matrix = ToSkMatrix(matrix4); - set_shader(UIDartState::CreateGPUObject( - image->image()->makeShader(tmx, tmy, &sk_matrix))); + sk_image_ = image->image(); + tmx_ = tmx; + tmy_ = tmy; + local_matrix_ = ToSkMatrix(matrix4); + if (filter_quality_index >= 0) { + cached_sampling_ = ImageFilter::SamplingFromIndex(filter_quality_index); + sampling_is_locked_ = true; + } else { + sampling_is_locked_ = false; + } +} + +sk_sp ImageShader::shader(SkSamplingOptions sampling) { + if (sampling_is_locked_) { + sampling = cached_sampling_; + } + if (!cached_shader_.skia_object() || cached_sampling_ != sampling) { + cached_sampling_ = sampling; + cached_shader_ = UIDartState::CreateGPUObject( + sk_image_->makeShader(tmx_, tmy_, sampling, &local_matrix_)); + } + return cached_shader_.skia_object(); } ImageShader::ImageShader() = default; diff --git a/lib/ui/painting/image_shader.h b/lib/ui/painting/image_shader.h index e57a7a5985c5c..b547b9f3ae303 100644 --- a/lib/ui/painting/image_shader.h +++ b/lib/ui/painting/image_shader.h @@ -31,12 +31,24 @@ class ImageShader : public Shader { void initWithImage(CanvasImage* image, SkTileMode tmx, SkTileMode tmy, + int filter_quality_index, const tonic::Float64List& matrix4); + sk_sp shader(SkSamplingOptions) override; + static void RegisterNatives(tonic::DartLibraryNatives* natives); private: ImageShader(); + + sk_sp sk_image_; + SkTileMode tmx_; + SkTileMode tmy_; + SkMatrix local_matrix_; + bool sampling_is_locked_; + + SkSamplingOptions cached_sampling_; + flutter::SkiaGPUObject cached_shader_; }; } // namespace flutter diff --git a/lib/ui/painting/immutable_buffer.h b/lib/ui/painting/immutable_buffer.h index ff7299881d643..a3a382e63bdfb 100644 --- a/lib/ui/painting/immutable_buffer.h +++ b/lib/ui/painting/immutable_buffer.h @@ -54,8 +54,8 @@ class ImmutableBuffer : public RefCountedDartWrappable { /// The byte buffer will continue to live if other objects hold a reference to /// it. void dispose() { - ClearDartWrapper(); data_.reset(); + ClearDartWrapper(); } size_t GetAllocationSize() const override; diff --git a/lib/ui/painting/multi_frame_codec.cc b/lib/ui/painting/multi_frame_codec.cc index 2ab5ce6d108c4..ba34eabaabeb5 100644 --- a/lib/ui/painting/multi_frame_codec.cc +++ b/lib/ui/painting/multi_frame_codec.cc @@ -12,16 +12,18 @@ namespace flutter { -MultiFrameCodec::MultiFrameCodec( - std::shared_ptr generator) +MultiFrameCodec::MultiFrameCodec(std::shared_ptr generator) : state_(new State(std::move(generator))) {} MultiFrameCodec::~MultiFrameCodec() = default; -MultiFrameCodec::State::State(std::shared_ptr generator) +MultiFrameCodec::State::State(std::shared_ptr generator) : generator_(std::move(generator)), - frameCount_(generator_->getFrameCount()), - repetitionCount_(generator_->getRepetitionCount()), + frameCount_(generator_->GetFrameCount()), + repetitionCount_(generator_->GetPlayCount() == + ImageGenerator::kInfinitePlayCount + ? -1 + : generator_->GetPlayCount() - 1), nextFrameIndex_(0) {} static void InvokeNextFrameCallback( @@ -76,18 +78,20 @@ static bool CopyToBitmap(SkBitmap* dst, sk_sp MultiFrameCodec::State::GetNextFrameImage( fml::WeakPtr resourceContext) { SkBitmap bitmap = SkBitmap(); - SkImageInfo info = generator_->getInfo().makeColorType(kN32_SkColorType); + SkImageInfo info = generator_->GetInfo().makeColorType(kN32_SkColorType); if (info.alphaType() == kUnpremul_SkAlphaType) { SkImageInfo updated = info.makeAlphaType(kPremul_SkAlphaType); info = updated; } bitmap.allocPixels(info); - SkCodec::Options options; - options.fFrameIndex = nextFrameIndex_; - SkCodec::FrameInfo frameInfo{0}; - generator_->getFrameInfo(nextFrameIndex_, &frameInfo); - const int requiredFrameIndex = frameInfo.fRequiredFrame; + ImageGenerator::FrameInfo frameInfo = + generator_->GetFrameInfo(nextFrameIndex_); + + const int requiredFrameIndex = + frameInfo.required_frame.value_or(SkCodec::kNoFrame); + std::optional prior_frame_index = std::nullopt; + if (requiredFrameIndex != SkCodec::kNoFrame) { if (lastRequiredFrame_ == nullptr) { FML_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame " @@ -103,18 +107,18 @@ sk_sp MultiFrameCodec::State::GetNextFrameImage( if (lastRequiredFrame_->getPixels() && CopyToBitmap(&bitmap, lastRequiredFrame_->colorType(), *lastRequiredFrame_)) { - options.fPriorFrame = requiredFrameIndex; + prior_frame_index = requiredFrameIndex; } } - if (!generator_->getPixels(info, bitmap.getPixels(), bitmap.rowBytes(), - &options)) { + if (!generator_->GetPixels(info, bitmap.getPixels(), bitmap.rowBytes(), + nextFrameIndex_, requiredFrameIndex)) { FML_LOG(ERROR) << "Could not getPixels for frame " << nextFrameIndex_; return nullptr; } // Hold onto this if we need it to decode future frames. - if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kKeep) { + if (frameInfo.disposal_method == SkCodecAnimation::DisposalMethod::kKeep) { lastRequiredFrame_ = std::make_unique(bitmap); lastRequiredFrameIndex_ = nextFrameIndex_; } @@ -144,9 +148,9 @@ void MultiFrameCodec::State::GetNextFrameAndInvokeCallback( if (skImage) { image = CanvasImage::Create(); image->set_image({skImage, std::move(unref_queue)}); - SkCodec::FrameInfo skFrameInfo{0}; - generator_->getFrameInfo(nextFrameIndex_, &skFrameInfo); - duration = skFrameInfo.fDuration; + ImageGenerator::FrameInfo frameInfo = + generator_->GetFrameInfo(nextFrameIndex_); + duration = frameInfo.duration; } nextFrameIndex_ = (nextFrameIndex_ + 1) % frameCount_; @@ -170,6 +174,17 @@ Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) { const auto& task_runners = dart_state->GetTaskRunners(); + if (state_->frameCount_ == 0) { + FML_LOG(ERROR) << "Could not provide any frame."; + task_runners.GetUITaskRunner()->PostTask(fml::MakeCopyable( + [trace_id, + callback = std::make_unique( + tonic::DartState::Current(), callback_handle)]() mutable { + InvokeNextFrameCallback(nullptr, 0, std::move(callback), trace_id); + })); + return Dart_Null(); + } + task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable( [callback = std::make_unique( tonic::DartState::Current(), callback_handle), diff --git a/lib/ui/painting/multi_frame_codec.h b/lib/ui/painting/multi_frame_codec.h index 428d67c0bfeda..92902fe344c50 100644 --- a/lib/ui/painting/multi_frame_codec.h +++ b/lib/ui/painting/multi_frame_codec.h @@ -7,13 +7,13 @@ #include "flutter/fml/macros.h" #include "flutter/lib/ui/painting/codec.h" -#include "third_party/skia/src/codec/SkCodecImageGenerator.h" +#include "flutter/lib/ui/painting/image_generator.h" namespace flutter { class MultiFrameCodec : public Codec { public: - MultiFrameCodec(std::shared_ptr generator); + MultiFrameCodec(std::shared_ptr generator); ~MultiFrameCodec() override; @@ -37,9 +37,9 @@ class MultiFrameCodec : public Codec { // shares it with the IO task runner's decoding work, and sets the live_ // member to false when it is destructed. struct State { - State(std::shared_ptr generator); + State(std::shared_ptr generator); - const std::shared_ptr generator_; + const std::shared_ptr generator_; const int frameCount_; const int repetitionCount_; diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index d3e49ad767460..ee3c322418729 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -71,6 +71,12 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { return; } + tonic::DartByteData byte_data(paint_data); + FML_CHECK(byte_data.length_in_bytes() == kDataByteCount); + + const uint32_t* uint_data = static_cast(byte_data.data()); + const float* float_data = static_cast(byte_data.data()); + Dart_Handle values[kObjectCount]; if (!Dart_IsNull(paint_objects)) { FML_DCHECK(Dart_IsList(paint_objects)); @@ -86,7 +92,9 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { Dart_Handle shader = values[kShaderIndex]; if (!Dart_IsNull(shader)) { Shader* decoded = tonic::DartConverter::FromDart(shader); - paint_.setShader(decoded->shader()); + auto sampling = + ImageFilter::SamplingFromIndex(uint_data[kFilterQualityIndex]); + paint_.setShader(decoded->shader(sampling)); } Dart_Handle color_filter = values[kColorFilterIndex]; @@ -104,12 +112,6 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { } } - tonic::DartByteData byte_data(paint_data); - FML_CHECK(byte_data.length_in_bytes() == kDataByteCount); - - const uint32_t* uint_data = static_cast(byte_data.data()); - const float* float_data = static_cast(byte_data.data()); - paint_.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0); uint32_t encoded_color = uint_data[kColorIndex]; @@ -149,11 +151,6 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { paint_.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault); } - uint32_t filter_quality = uint_data[kFilterQualityIndex]; - if (filter_quality) { - paint_.setFilterQuality(static_cast(filter_quality)); - } - if (uint_data[kInvertColorIndex]) { sk_sp invert_filter = ColorFilter::MakeColorMatrixFilter255(invert_colors); diff --git a/lib/ui/painting/path.cc b/lib/ui/painting/path.cc index d0898a4ca283c..650aa231f8add 100644 --- a/lib/ui/painting/path.cc +++ b/lib/ui/painting/path.cc @@ -67,43 +67,70 @@ void CanvasPath::RegisterNatives(tonic::DartLibraryNatives* natives) { FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); } -CanvasPath::CanvasPath() {} +CanvasPath::CanvasPath() + : path_tracker_(UIDartState::Current()->GetVolatilePathTracker()), + tracked_path_(std::make_shared()) { + FML_DCHECK(path_tracker_); + resetVolatility(); +} + +CanvasPath::~CanvasPath() = default; + +void CanvasPath::resetVolatility() { + if (!tracked_path_->tracking_volatility) { + mutable_path().setIsVolatile(true); + tracked_path_->frame_count = 0; + tracked_path_->tracking_volatility = true; + path_tracker_->Insert(tracked_path_); + } +} -CanvasPath::~CanvasPath() {} +void CanvasPath::ReleaseDartWrappableReference() const { + FML_DCHECK(path_tracker_); + path_tracker_->Erase(tracked_path_); + RefCountedDartWrappable::ReleaseDartWrappableReference(); +} int CanvasPath::getFillType() { - return static_cast(path_.getFillType()); + return static_cast(path().getFillType()); } void CanvasPath::setFillType(int fill_type) { - path_.setFillType(static_cast(fill_type)); + mutable_path().setFillType(static_cast(fill_type)); + resetVolatility(); } void CanvasPath::moveTo(float x, float y) { - path_.moveTo(x, y); + mutable_path().moveTo(x, y); + resetVolatility(); } void CanvasPath::relativeMoveTo(float x, float y) { - path_.rMoveTo(x, y); + mutable_path().rMoveTo(x, y); + resetVolatility(); } void CanvasPath::lineTo(float x, float y) { - path_.lineTo(x, y); + mutable_path().lineTo(x, y); + resetVolatility(); } void CanvasPath::relativeLineTo(float x, float y) { - path_.rLineTo(x, y); + mutable_path().rLineTo(x, y); + resetVolatility(); } void CanvasPath::quadraticBezierTo(float x1, float y1, float x2, float y2) { - path_.quadTo(x1, y1, x2, y2); + mutable_path().quadTo(x1, y1, x2, y2); + resetVolatility(); } void CanvasPath::relativeQuadraticBezierTo(float x1, float y1, float x2, float y2) { - path_.rQuadTo(x1, y1, x2, y2); + mutable_path().rQuadTo(x1, y1, x2, y2); + resetVolatility(); } void CanvasPath::cubicTo(float x1, @@ -112,7 +139,8 @@ void CanvasPath::cubicTo(float x1, float y2, float x3, float y3) { - path_.cubicTo(x1, y1, x2, y2, x3, y3); + mutable_path().cubicTo(x1, y1, x2, y2, x3, y3); + resetVolatility(); } void CanvasPath::relativeCubicTo(float x1, @@ -121,11 +149,13 @@ void CanvasPath::relativeCubicTo(float x1, float y2, float x3, float y3) { - path_.rCubicTo(x1, y1, x2, y2, x3, y3); + mutable_path().rCubicTo(x1, y1, x2, y2, x3, y3); + resetVolatility(); } void CanvasPath::conicTo(float x1, float y1, float x2, float y2, float w) { - path_.conicTo(x1, y1, x2, y2, w); + mutable_path().conicTo(x1, y1, x2, y2, w); + resetVolatility(); } void CanvasPath::relativeConicTo(float x1, @@ -133,7 +163,8 @@ void CanvasPath::relativeConicTo(float x1, float x2, float y2, float w) { - path_.rConicTo(x1, y1, x2, y2, w); + mutable_path().rConicTo(x1, y1, x2, y2, w); + resetVolatility(); } void CanvasPath::arcTo(float left, @@ -143,9 +174,10 @@ void CanvasPath::arcTo(float left, float startAngle, float sweepAngle, bool forceMoveTo) { - path_.arcTo(SkRect::MakeLTRB(left, top, right, bottom), - startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, - forceMoveTo); + mutable_path().arcTo(SkRect::MakeLTRB(left, top, right, bottom), + startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, + forceMoveTo); + resetVolatility(); } void CanvasPath::arcToPoint(float arcEndX, @@ -160,8 +192,9 @@ void CanvasPath::arcToPoint(float arcEndX, const auto direction = isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW; - path_.arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, arcEndX, - arcEndY); + mutable_path().arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, + arcEndX, arcEndY); + resetVolatility(); } void CanvasPath::relativeArcToPoint(float arcEndDeltaX, @@ -175,16 +208,19 @@ void CanvasPath::relativeArcToPoint(float arcEndDeltaX, : SkPath::ArcSize::kSmall_ArcSize; const auto direction = isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW; - path_.rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, - arcEndDeltaX, arcEndDeltaY); + mutable_path().rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, + arcEndDeltaX, arcEndDeltaY); + resetVolatility(); } void CanvasPath::addRect(float left, float top, float right, float bottom) { - path_.addRect(SkRect::MakeLTRB(left, top, right, bottom)); + mutable_path().addRect(SkRect::MakeLTRB(left, top, right, bottom)); + resetVolatility(); } void CanvasPath::addOval(float left, float top, float right, float bottom) { - path_.addOval(SkRect::MakeLTRB(left, top, right, bottom)); + mutable_path().addOval(SkRect::MakeLTRB(left, top, right, bottom)); + resetVolatility(); } void CanvasPath::addArc(float left, @@ -193,17 +229,20 @@ void CanvasPath::addArc(float left, float bottom, float startAngle, float sweepAngle) { - path_.addArc(SkRect::MakeLTRB(left, top, right, bottom), - startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI); + mutable_path().addArc(SkRect::MakeLTRB(left, top, right, bottom), + startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI); + resetVolatility(); } void CanvasPath::addPolygon(const tonic::Float32List& points, bool close) { - path_.addPoly(reinterpret_cast(points.data()), - points.num_elements() / 2, close); + mutable_path().addPoly(reinterpret_cast(points.data()), + points.num_elements() / 2, close); + resetVolatility(); } void CanvasPath::addRRect(const RRect& rrect) { - path_.addRRect(rrect.sk_rrect); + mutable_path().addRRect(rrect.sk_rrect); + resetVolatility(); } void CanvasPath::addPath(CanvasPath* path, double dx, double dy) { @@ -211,7 +250,8 @@ void CanvasPath::addPath(CanvasPath* path, double dx, double dy) { Dart_ThrowException(ToDart("Path.addPath called with non-genuine Path.")); return; } - path_.addPath(path->path(), dx, dy, SkPath::kAppend_AddPathMode); + mutable_path().addPath(path->path(), dx, dy, SkPath::kAppend_AddPathMode); + resetVolatility(); } void CanvasPath::addPathWithMatrix(CanvasPath* path, @@ -227,8 +267,9 @@ void CanvasPath::addPathWithMatrix(CanvasPath* path, SkMatrix matrix = ToSkMatrix(matrix4); matrix.setTranslateX(matrix.getTranslateX() + dx); matrix.setTranslateY(matrix.getTranslateY() + dy); - path_.addPath(path->path(), matrix, SkPath::kAppend_AddPathMode); + mutable_path().addPath(path->path(), matrix, SkPath::kAppend_AddPathMode); matrix4.Release(); + resetVolatility(); } void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) { @@ -237,7 +278,8 @@ void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) { ToDart("Path.extendWithPath called with non-genuine Path.")); return; } - path_.addPath(path->path(), dx, dy, SkPath::kExtend_AddPathMode); + mutable_path().addPath(path->path(), dx, dy, SkPath::kExtend_AddPathMode); + resetVolatility(); } void CanvasPath::extendWithPathAndMatrix(CanvasPath* path, @@ -253,37 +295,43 @@ void CanvasPath::extendWithPathAndMatrix(CanvasPath* path, SkMatrix matrix = ToSkMatrix(matrix4); matrix.setTranslateX(matrix.getTranslateX() + dx); matrix.setTranslateY(matrix.getTranslateY() + dy); - path_.addPath(path->path(), matrix, SkPath::kExtend_AddPathMode); + mutable_path().addPath(path->path(), matrix, SkPath::kExtend_AddPathMode); matrix4.Release(); + resetVolatility(); } void CanvasPath::close() { - path_.close(); + mutable_path().close(); + resetVolatility(); } void CanvasPath::reset() { - path_.reset(); + mutable_path().reset(); + resetVolatility(); } bool CanvasPath::contains(double x, double y) { - return path_.contains(x, y); + return path().contains(x, y); } void CanvasPath::shift(Dart_Handle path_handle, double dx, double dy) { fml::RefPtr path = CanvasPath::Create(path_handle); - path_.offset(dx, dy, &path->path_); + auto& other_mutable_path = path->mutable_path(); + mutable_path().offset(dx, dy, &other_mutable_path); + resetVolatility(); } void CanvasPath::transform(Dart_Handle path_handle, tonic::Float64List& matrix4) { fml::RefPtr path = CanvasPath::Create(path_handle); - path_.transform(ToSkMatrix(matrix4), &path->path_); + auto& other_mutable_path = path->mutable_path(); + mutable_path().transform(ToSkMatrix(matrix4), &other_mutable_path); matrix4.Release(); } tonic::Float32List CanvasPath::getBounds() { tonic::Float32List rect(Dart_NewTypedData(Dart_TypedData_kFloat32, 4)); - const SkRect& bounds = path_.getBounds(); + const SkRect& bounds = path().getBounds(); rect[0] = bounds.left(); rect[1] = bounds.top(); rect[2] = bounds.right(); @@ -293,21 +341,22 @@ tonic::Float32List CanvasPath::getBounds() { bool CanvasPath::op(CanvasPath* path1, CanvasPath* path2, int operation) { return Op(path1->path(), path2->path(), static_cast(operation), - &path_); + &tracked_path_->path); + resetVolatility(); } void CanvasPath::clone(Dart_Handle path_handle) { fml::RefPtr path = CanvasPath::Create(path_handle); // per Skia docs, this will create a fast copy // data is shared until the source path or dest path are mutated - path->path_ = path_; + path->mutable_path() = this->path(); } // This is doomed to be called too early, since Paths are mutable. // However, it can help for some of the clone/shift/transform type methods // where the resultant path will initially have a meaningful size. size_t CanvasPath::GetAllocationSize() const { - return sizeof(CanvasPath) + path_.approximateBytesUsed(); + return sizeof(CanvasPath) + path().approximateBytesUsed(); } } // namespace flutter diff --git a/lib/ui/painting/path.h b/lib/ui/painting/path.h index dbd6d7a0d1cc2..3c05cdd140837 100644 --- a/lib/ui/painting/path.h +++ b/lib/ui/painting/path.h @@ -7,6 +7,7 @@ #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/rrect.h" +#include "flutter/lib/ui/volatile_path_tracker.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/pathops/SkPathOps.h" #include "third_party/tonic/typed_data/typed_list.h" @@ -36,7 +37,7 @@ class CanvasPath : public RefCountedDartWrappable { static fml::RefPtr CreateFrom(Dart_Handle path_handle, const SkPath& src) { fml::RefPtr path = CanvasPath::Create(path_handle); - path->path_ = src; + path->tracked_path_->path = src; return path; } @@ -108,16 +109,24 @@ class CanvasPath : public RefCountedDartWrappable { bool op(CanvasPath* path1, CanvasPath* path2, int operation); void clone(Dart_Handle path_handle); - const SkPath& path() const { return path_; } + const SkPath& path() const { return tracked_path_->path; } size_t GetAllocationSize() const override; static void RegisterNatives(tonic::DartLibraryNatives* natives); + virtual void ReleaseDartWrappableReference() const override; + private: CanvasPath(); - SkPath path_; + std::shared_ptr path_tracker_; + std::shared_ptr tracked_path_; + + // Must be called whenever the path is created or mutated. + void resetVolatility(); + + SkPath& mutable_path() { return tracked_path_->path; } }; } // namespace flutter diff --git a/lib/ui/painting/path_unittests.cc b/lib/ui/painting/path_unittests.cc new file mode 100644 index 0000000000000..f8b2eabc9f30d --- /dev/null +++ b/lib/ui/painting/path_unittests.cc @@ -0,0 +1,188 @@ +// 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/lib/ui/painting/path.h" + +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +TEST_F(ShellTest, PathVolatilityOldPathsBecomeNonVolatile) { + auto message_latch = std::make_shared(); + + auto native_validate_path = [message_latch](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + EXPECT_FALSE(Dart_IsError(result)); + CanvasPath* path = reinterpret_cast(peer); + EXPECT_TRUE(path); + EXPECT_TRUE(path->path().isVolatile()); + std::shared_ptr tracker = + UIDartState::Current()->GetVolatilePathTracker(); + EXPECT_TRUE(tracker); + + for (int i = 0; i < VolatilePathTracker::kFramesOfVolatility; i++) { + EXPECT_TRUE(path->path().isVolatile()); + tracker->OnFrame(); + } + EXPECT_FALSE(path->path().isVolatile()); + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("ValidatePath", CREATE_NATIVE_ENTRY(native_validate_path)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("createPath"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + + DestroyShell(std::move(shell), std::move(task_runners)); +} + +TEST_F(ShellTest, PathVolatilityGCRemovesPathFromTracker) { + auto message_latch = std::make_shared(); + + auto native_validate_path = [message_latch](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + EXPECT_FALSE(Dart_IsError(result)); + CanvasPath* path = reinterpret_cast(peer); + EXPECT_TRUE(path); + path->AddRef(); + EXPECT_TRUE(path->path().isVolatile()); + std::shared_ptr tracker = + UIDartState::Current()->GetVolatilePathTracker(); + EXPECT_TRUE(tracker); + + static_assert(VolatilePathTracker::kFramesOfVolatility > 1); + EXPECT_TRUE(path->path().isVolatile()); + tracker->OnFrame(); + EXPECT_TRUE(path->path().isVolatile()); + + // simulate GC + path->ReleaseDartWrappableReference(); + + tracker->OnFrame(); + // Because the path got GC'd, it was removed from the cache and we're the + // only one holding it. + EXPECT_TRUE(path->path().isVolatile()); + + path->Release(); + + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("ValidatePath", CREATE_NATIVE_ENTRY(native_validate_path)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("createPath"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + + DestroyShell(std::move(shell), std::move(task_runners)); +} + +// Screen diffing tests use deterministic rendering. Allowing a path to be +// volatile or not for an individual frame can result in minor pixel differences +// that cause the test to fail. +// If deterministic rendering is enabled, the tracker should be disabled and +// paths should always be non-volatile. +TEST_F(ShellTest, DeterministicRenderingDisablesPathVolatility) { + auto message_latch = std::make_shared(); + + auto native_validate_path = [message_latch](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + EXPECT_FALSE(Dart_IsError(result)); + CanvasPath* path = reinterpret_cast(peer); + EXPECT_TRUE(path); + EXPECT_FALSE(path->path().isVolatile()); + std::shared_ptr tracker = + UIDartState::Current()->GetVolatilePathTracker(); + EXPECT_TRUE(tracker); + EXPECT_FALSE(tracker->enabled()); + + for (int i = 0; i < VolatilePathTracker::kFramesOfVolatility; i++) { + tracker->OnFrame(); + EXPECT_FALSE(path->path().isVolatile()); + } + EXPECT_FALSE(path->path().isVolatile()); + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + settings.skia_deterministic_rendering_on_cpu = true; + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("ValidatePath", CREATE_NATIVE_ENTRY(native_validate_path)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("createPath"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + + DestroyShell(std::move(shell), std::move(task_runners)); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 5225d993ba4fb..ca360610f3bf6 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -37,29 +37,51 @@ fml::RefPtr Picture::Create( return canvas_picture; } +fml::RefPtr Picture::Create(Dart_Handle dart_handle, + sk_sp display_list) { + auto canvas_picture = fml::MakeRefCounted(std::move(display_list)); + + canvas_picture->AssociateWithDartWrapper(dart_handle); + return canvas_picture; +} + Picture::Picture(flutter::SkiaGPUObject picture) : picture_(std::move(picture)) {} +Picture::Picture(sk_sp display_list) + : display_list_(std::move(display_list)) {} + Picture::~Picture() = default; Dart_Handle Picture::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - if (!picture_.get()) { - return tonic::ToDart("Picture is null"); + if (display_list_) { + return RasterizeToImage( + [display_list = display_list_](SkCanvas* canvas) { + display_list->RenderTo(canvas); + }, + width, height, raw_image_callback); + } else { + if (!picture_.skia_object()) { + return tonic::ToDart("Picture is null"); + } + return RasterizeToImage(picture_.skia_object(), width, height, + raw_image_callback); } - - return RasterizeToImage(picture_.get(), width, height, raw_image_callback); } void Picture::dispose() { picture_.reset(); + display_list_.reset(); ClearDartWrapper(); } size_t Picture::GetAllocationSize() const { - if (auto picture = picture_.get()) { + if (auto picture = picture_.skia_object()) { return picture->approximateBytesUsed() + sizeof(Picture); + } else if (display_list_) { + return display_list_->bytes() + sizeof(Picture); } else { return sizeof(Picture); } @@ -69,6 +91,16 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { + return RasterizeToImage( + [picture](SkCanvas* canvas) { canvas->drawPicture(picture); }, width, + height, raw_image_callback); +} + +Dart_Handle Picture::RasterizeToImage( + std::function draw_callback, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback) { if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { return tonic::ToDart("Image callback was invalid"); } @@ -121,10 +153,10 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, // Kick things off on the raster rask runner. fml::TaskRunner::RunNowOrPostTask( - raster_task_runner, - [ui_task_runner, snapshot_delegate, picture, picture_bounds, ui_task] { - sk_sp raster_image = - snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds); + raster_task_runner, [ui_task_runner, snapshot_delegate, draw_callback, + picture_bounds, ui_task] { + sk_sp raster_image = snapshot_delegate->MakeRasterSnapshot( + draw_callback, picture_bounds); fml::TaskRunner::RunNowOrPostTask( ui_task_runner, diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index e0158d400ff5e..6116b52b9d800 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_H_ +#include "flutter/flow/display_list.h" #include "flutter/flow/skia_gpu_object.h" #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/image.h" @@ -26,8 +27,11 @@ class Picture : public RefCountedDartWrappable { ~Picture() override; static fml::RefPtr Create(Dart_Handle dart_handle, flutter::SkiaGPUObject picture); + static fml::RefPtr Create(Dart_Handle dart_handle, + sk_sp display_list); - sk_sp picture() const { return picture_.get(); } + sk_sp picture() const { return picture_.skia_object(); } + sk_sp display_list() const { return display_list_; } Dart_Handle toImage(uint32_t width, uint32_t height, @@ -44,10 +48,18 @@ class Picture : public RefCountedDartWrappable { uint32_t height, Dart_Handle raw_image_callback); + static Dart_Handle RasterizeToImage( + std::function draw_callback, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); + private: Picture(flutter::SkiaGPUObject picture); + Picture(sk_sp display_list); flutter::SkiaGPUObject picture_; + sk_sp display_list_; }; } // namespace flutter diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index 5084f30d7812f..ed6f88cfeda78 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -39,7 +39,13 @@ PictureRecorder::PictureRecorder() {} PictureRecorder::~PictureRecorder() {} SkCanvas* PictureRecorder::BeginRecording(SkRect bounds) { - return picture_recorder_.beginRecording(bounds, &rtree_factory_); + bool enable_display_list = UIDartState::Current()->enable_display_list(); + if (enable_display_list) { + display_list_recorder_ = sk_make_sp(bounds); + return display_list_recorder_.get(); + } else { + return picture_recorder_.beginRecording(bounds, &rtree_factory_); + } } fml::RefPtr PictureRecorder::endRecording(Dart_Handle dart_picture) { @@ -47,9 +53,16 @@ fml::RefPtr PictureRecorder::endRecording(Dart_Handle dart_picture) { return nullptr; } - fml::RefPtr picture = Picture::Create( - dart_picture, UIDartState::CreateGPUObject( - picture_recorder_.finishRecordingAsPicture())); + fml::RefPtr picture; + + if (display_list_recorder_) { + picture = Picture::Create(dart_picture, display_list_recorder_->Build()); + display_list_recorder_ = nullptr; + } else { + picture = Picture::Create( + dart_picture, UIDartState::CreateGPUObject( + picture_recorder_.finishRecordingAsPicture())); + } canvas_->Invalidate(); canvas_ = nullptr; diff --git a/lib/ui/painting/picture_recorder.h b/lib/ui/painting/picture_recorder.h index 59d0d782c57ae..7d2303284d32b 100644 --- a/lib/ui/painting/picture_recorder.h +++ b/lib/ui/painting/picture_recorder.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ +#include "flutter/flow/display_list_canvas.h" #include "flutter/lib/ui/dart_wrapper.h" #include "third_party/skia/include/core/SkPictureRecorder.h" @@ -28,6 +29,10 @@ class PictureRecorder : public RefCountedDartWrappable { SkCanvas* BeginRecording(SkRect bounds); fml::RefPtr endRecording(Dart_Handle dart_picture); + sk_sp display_list_recorder() { + return display_list_recorder_; + } + void set_canvas(fml::RefPtr canvas) { canvas_ = std::move(canvas); } static void RegisterNatives(tonic::DartLibraryNatives* natives); @@ -37,6 +42,9 @@ class PictureRecorder : public RefCountedDartWrappable { SkRTreeFactory rtree_factory_; SkPictureRecorder picture_recorder_; + + sk_sp display_list_recorder_; + fml::RefPtr canvas_; }; diff --git a/lib/ui/painting/shader.cc b/lib/ui/painting/shader.cc index c6f0e6b5617d5..e350033cf5ae0 100644 --- a/lib/ui/painting/shader.cc +++ b/lib/ui/painting/shader.cc @@ -10,9 +10,6 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, Shader); -Shader::Shader(flutter::SkiaGPUObject shader) - : shader_(std::move(shader)) {} - Shader::~Shader() = default; } // namespace flutter diff --git a/lib/ui/painting/shader.h b/lib/ui/painting/shader.h index cb9043ae0b4da..25c1d7aed550a 100644 --- a/lib/ui/painting/shader.h +++ b/lib/ui/painting/shader.h @@ -19,17 +19,13 @@ class Shader : public RefCountedDartWrappable { public: ~Shader() override; - sk_sp shader() { return shader_.get(); } - - void set_shader(flutter::SkiaGPUObject shader) { - shader_ = std::move(shader); - } + virtual sk_sp shader(SkSamplingOptions) = 0; protected: - Shader(flutter::SkiaGPUObject shader = {}); + Shader() {} private: - flutter::SkiaGPUObject shader_; + // flutter::SkiaGPUObject shader_; }; } // namespace flutter diff --git a/lib/ui/painting/single_frame_codec.cc b/lib/ui/painting/single_frame_codec.cc index 3d9d51f02a2dd..f277bc1245165 100644 --- a/lib/ui/painting/single_frame_codec.cc +++ b/lib/ui/painting/single_frame_codec.cc @@ -52,7 +52,10 @@ Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) { auto decoder = dart_state->GetImageDecoder(); if (!decoder) { - return tonic::ToDart("Image decoder not available."); + return tonic::ToDart( + "Failed to access the internal image decoder " + "registry on this isolate. Please file a bug on " + "https://github.com/flutter/flutter/issues."); } // The SingleFrameCodec must be deleted on the UI thread. Allocate a RefPtr @@ -78,7 +81,7 @@ Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) { tonic::DartState::Scope scope(state.get()); - if (image.get()) { + if (image.skia_object()) { auto canvas_image = fml::MakeRefCounted(); canvas_image->set_image(std::move(image)); @@ -108,10 +111,7 @@ Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) { } size_t SingleFrameCodec::GetAllocationSize() const { - const auto& data_size = descriptor_->GetAllocationSize(); - const auto frame_byte_size = - cached_image_ ? cached_image_->GetAllocationSize() : 0; - return data_size + frame_byte_size + sizeof(this); + return sizeof(*this); } } // namespace flutter diff --git a/lib/ui/painting/single_frame_codec_unittests.cc b/lib/ui/painting/single_frame_codec_unittests.cc new file mode 100644 index 0000000000000..7e07033d2ebd7 --- /dev/null +++ b/lib/ui/painting/single_frame_codec_unittests.cc @@ -0,0 +1,62 @@ +// 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/lib/ui/painting/single_frame_codec.h" + +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +TEST_F(ShellTest, SingleFrameCodecAccuratelyReportsSize) { + auto message_latch = std::make_shared(); + + auto validate_codec = [](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + ASSERT_FALSE(Dart_IsError(result)); + SingleFrameCodec* codec = reinterpret_cast(peer); + ASSERT_EQ(codec->GetAllocationSize(), sizeof(SingleFrameCodec)); + }; + auto finish = [message_latch](Dart_NativeArguments args) { + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("ValidateCodec", CREATE_NATIVE_ENTRY(validate_codec)); + AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(finish)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("createSingleFrameCodec"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart new file mode 100644 index 0000000000000..99a3bd3d77f57 --- /dev/null +++ b/lib/ui/platform_dispatcher.dart @@ -0,0 +1,1675 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.12 +part of dart.ui; + +/// Signature of callbacks that have no arguments and return no data. +typedef VoidCallback = void Function(); + +/// Signature for [PlatformDispatcher.onBeginFrame]. +typedef FrameCallback = void Function(Duration duration); + +/// Signature for [PlatformDispatcher.onReportTimings]. +/// +/// {@template dart.ui.TimingsCallback.list} +/// The callback takes a list of [FrameTiming] because it may not be +/// immediately triggered after each frame. Instead, Flutter tries to batch +/// frames together and send all their timings at once to decrease the +/// overhead (as this is available in the release mode). The list is sorted in +/// ascending order of time (earliest frame first). The timing of any frame +/// will be sent within about 1 second (100ms if in the profile/debug mode) +/// even if there are no later frames to batch. The timing of the first frame +/// will be sent immediately without batching. +/// {@endtemplate} +typedef TimingsCallback = void Function(List timings); + +/// Signature for [PlatformDispatcher.onPointerDataPacket]. +typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); + +// Signature for the response to KeyDataCallback. +typedef _KeyDataResponseCallback = void Function(int responseId, bool handled); + +/// Signature for [PlatformDispatcher.onKeyData]. +/// +/// The callback should return true if the key event has been handled by the +/// framework and should not be propagated further. +typedef KeyDataCallback = bool Function(KeyData data); + +/// Signature for [PlatformDispatcher.onSemanticsAction]. +typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); + +/// Signature for responses to platform messages. +/// +/// Used as a parameter to [PlatformDispatcher.sendPlatformMessage] and +/// [PlatformDispatcher.onPlatformMessage]. +typedef PlatformMessageResponseCallback = void Function(ByteData? data); + +/// Signature for [PlatformDispatcher.onPlatformMessage]. +// TODO(ianh): deprecate once framework uses [ChannelBuffers.setListener]. +typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); + +// Signature for _setNeedsReportTimings. +typedef _SetNeedsReportTimingsFunc = void Function(bool value); + +/// Signature for [PlatformDispatcher.onConfigurationChanged]. +typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); + +// A gesture setting value that indicates it has not been set by the engine. +const double _kUnsetGestureSetting = -1.0; + +/// Platform event dispatcher singleton. +/// +/// The most basic interface to the host operating system's interface. +/// +/// This is the central entry point for platform messages and configuration +/// events from the platform. +/// +/// It exposes the core scheduler API, the input event callback, the graphics +/// drawing API, and other such core services. +/// +/// It manages the list of the application's [views] and the [screens] attached +/// to the device, as well as the [configuration] of various platform +/// attributes. +/// +/// Consider avoiding static references to this singleton through +/// [PlatformDispatcher.instance] and instead prefer using a binding for +/// dependency resolution such as `WidgetsBinding.instance.platformDispatcher`. +/// See [PlatformDispatcher.instance] for more information about why this is +/// preferred. +class PlatformDispatcher { + /// Private constructor, since only dart:ui is supposed to create one of + /// these. Use [instance] to access the singleton. + PlatformDispatcher._() { + _setNeedsReportTimings = _nativeSetNeedsReportTimings; + } + + /// The [PlatformDispatcher] singleton. + /// + /// Consider avoiding static references to this singleton though + /// [PlatformDispatcher.instance] and instead prefer using a binding for + /// dependency resolution such as `WidgetsBinding.instance.platformDispatcher`. + /// + /// Static access of this object means that Flutter has few, if any options to + /// fake or mock the given object in tests. Even in cases where Dart offers + /// special language constructs to forcefully shadow such properties, those + /// mechanisms would only be reasonable for tests and they would not be + /// reasonable for a future of Flutter where we legitimately want to select an + /// appropriate implementation at runtime. + /// + /// The only place that `WidgetsBinding.instance.platformDispatcher` is + /// inappropriate is if access to these APIs is required before the binding is + /// initialized by invoking `runApp()` or + /// `WidgetsFlutterBinding.instance.ensureInitialized()`. In that case, it is + /// necessary (though unfortunate) to use the [PlatformDispatcher.instance] + /// object statically. + static PlatformDispatcher get instance => _instance; + static final PlatformDispatcher _instance = PlatformDispatcher._(); + + /// The current platform configuration. + /// + /// If values in this configuration change, [onPlatformConfigurationChanged] + /// will be called. + PlatformConfiguration get configuration => _configuration; + PlatformConfiguration _configuration = const PlatformConfiguration(); + + /// Called when the platform configuration changes. + /// + /// The engine invokes this callback in the same zone in which the callback + /// was set. + VoidCallback? get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; + VoidCallback? _onPlatformConfigurationChanged; + Zone _onPlatformConfigurationChangedZone = Zone.root; + set onPlatformConfigurationChanged(VoidCallback? callback) { + _onPlatformConfigurationChanged = callback; + _onPlatformConfigurationChangedZone = Zone.current; + } + + /// The current list of views, including top level platform windows used by + /// the application. + /// + /// If any of their configurations change, [onMetricsChanged] will be called. + Iterable get views => _views.values; + Map _views = {}; + + // A map of opaque platform view identifiers to view configurations. + Map _viewConfigurations = {}; + + /// A callback that is invoked whenever the [ViewConfiguration] of any of the + /// [views] changes. + /// + /// For example when the device is rotated or when the application is resized + /// (e.g. when showing applications side-by-side on Android), + /// `onMetricsChanged` is called. + /// + /// The engine invokes this callback in the same zone in which the callback + /// was set. + /// + /// The framework registers with this callback and updates the layout + /// appropriately. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// register for notifications when this is called. + /// * [MediaQuery.of], a simpler mechanism for the same. + VoidCallback? get onMetricsChanged => _onMetricsChanged; + VoidCallback? _onMetricsChanged; + Zone _onMetricsChangedZone = Zone.root; + set onMetricsChanged(VoidCallback? callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + // Called from the engine, via hooks.dart + // + // Updates the metrics of the window with the given id. + void _updateWindowMetrics( + Object id, + double devicePixelRatio, + double width, + double height, + double viewPaddingTop, + double viewPaddingRight, + double viewPaddingBottom, + double viewPaddingLeft, + double viewInsetTop, + double viewInsetRight, + double viewInsetBottom, + double viewInsetLeft, + double systemGestureInsetTop, + double systemGestureInsetRight, + double systemGestureInsetBottom, + double systemGestureInsetLeft, + double physicalTouchSlop, + ) { + final ViewConfiguration previousConfiguration = + _viewConfigurations[id] ?? const ViewConfiguration(); + if (!_views.containsKey(id)) { + _views[id] = FlutterWindow._(id, this); + } + _viewConfigurations[id] = previousConfiguration.copyWith( + window: _views[id], + devicePixelRatio: devicePixelRatio, + geometry: Rect.fromLTWH(0.0, 0.0, width, height), + viewPadding: WindowPadding._( + top: viewPaddingTop, + right: viewPaddingRight, + bottom: viewPaddingBottom, + left: viewPaddingLeft, + ), + viewInsets: WindowPadding._( + top: viewInsetTop, + right: viewInsetRight, + bottom: viewInsetBottom, + left: viewInsetLeft, + ), + padding: WindowPadding._( + top: math.max(0.0, viewPaddingTop - viewInsetTop), + right: math.max(0.0, viewPaddingRight - viewInsetRight), + bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), + left: math.max(0.0, viewPaddingLeft - viewInsetLeft), + ), + systemGestureInsets: WindowPadding._( + top: math.max(0.0, systemGestureInsetTop), + right: math.max(0.0, systemGestureInsetRight), + bottom: math.max(0.0, systemGestureInsetBottom), + left: math.max(0.0, systemGestureInsetLeft), + ), + // -1 is used as a sentinel for an undefined touch slop + gestureSettings: GestureSettings( + physicalTouchSlop: physicalTouchSlop == _kUnsetGestureSetting ? null : physicalTouchSlop, + ), + ); + _invoke(onMetricsChanged, _onMetricsChangedZone); + } + + /// A callback invoked when any view begins a frame. + /// + /// A callback that is invoked to notify the application that it is an + /// appropriate time to provide a scene using the [SceneBuilder] API and the + /// [FlutterView.render] method. + /// + /// When possible, this is driven by the hardware VSync signal of the attached + /// screen with the highest VSync rate. This is only called if + /// [PlatformDispatcher.scheduleFrame] has been called since the last time + /// this callback was invoked. + FrameCallback? get onBeginFrame => _onBeginFrame; + FrameCallback? _onBeginFrame; + Zone _onBeginFrameZone = Zone.root; + set onBeginFrame(FrameCallback? callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _beginFrame(int microseconds) { + _invoke1( + onBeginFrame, + _onBeginFrameZone, + Duration(microseconds: microseconds), + ); + } + + /// A callback that is invoked for each frame after [onBeginFrame] has + /// completed and after the microtask queue has been drained. + /// + /// This can be used to implement a second phase of frame rendering that + /// happens after any deferred work queued by the [onBeginFrame] phase. + VoidCallback? get onDrawFrame => _onDrawFrame; + VoidCallback? _onDrawFrame; + Zone _onDrawFrameZone = Zone.root; + set onDrawFrame(VoidCallback? callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _drawFrame() { + _invoke(onDrawFrame, _onDrawFrameZone); + } + + /// A callback that is invoked when pointer data is available. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; + PointerDataPacketCallback? _onPointerDataPacket; + Zone _onPointerDataPacketZone = Zone.root; + set onPointerDataPacket(PointerDataPacketCallback? callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _dispatchPointerDataPacket(ByteData packet) { + if (onPointerDataPacket != null) { + _invoke1( + onPointerDataPacket, + _onPointerDataPacketZone, + _unpackPointerDataPacket(packet), + ); + } + } + + // If this value changes, update the encoding code in the following files: + // + // * pointer_data.cc + // * pointer.dart + // * AndroidTouchProcessor.java + static const int _kPointerDataFieldCount = 29; + + static PointerDataPacket _unpackPointerDataPacket(ByteData packet) { + const int kStride = Int64List.bytesPerElement; + const int kBytesPerPointerData = _kPointerDataFieldCount * kStride; + final int length = packet.lengthInBytes ~/ kBytesPerPointerData; + assert(length * kBytesPerPointerData == packet.lengthInBytes); + final List data = []; + for (int i = 0; i < length; ++i) { + int offset = i * _kPointerDataFieldCount; + data.add(PointerData( + embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian), + timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)), + change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + device: packet.getInt64(kStride * offset++, _kFakeHostEndian), + pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian), + physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), + obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, + synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, + pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + size: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), + scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + )); + assert(offset == (i + 1) * _kPointerDataFieldCount); + } + return PointerDataPacket(data: data); + } + + /// Called by [_dispatchKeyData]. + void _respondToKeyData(int responseId, bool handled) + native 'PlatformConfiguration_respondToKeyData'; + + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// The callback should return true if the key event has been handled by the + /// framework and should not be propagated further. + KeyDataCallback? get onKeyData => _onKeyData; + KeyDataCallback? _onKeyData; + Zone _onKeyDataZone = Zone.root; + set onKeyData(KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _dispatchKeyData(ByteData packet, int responseId) { + _invoke2( + (KeyData data, _KeyDataResponseCallback callback) { + callback(responseId, onKeyData != null && onKeyData!(data)); + }, + _onKeyDataZone, + _unpackKeyData(packet), + _respondToKeyData, + ); + } + + // If this value changes, update the encoding code in the following files: + // + // * key_data.h + // * key.dart (ui) + // * key.dart (web_ui) + // * HardwareKeyboard.java + static const int _kKeyDataFieldCount = 5; + + // The packet structure is described in `key_data_packet.h`. + static KeyData _unpackKeyData(ByteData packet) { + const int kStride = Int64List.bytesPerElement; + + int offset = 0; + final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian); + final String? character = charDataSize == 0 ? null : utf8.decoder.convert( + packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize)); + + final KeyData keyData = KeyData( + timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), + type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + physical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + logical: packet.getUint64(kStride * offset++, _kFakeHostEndian), + character: character, + synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0, + ); + + return keyData; + } + + /// A callback that is invoked to report the [FrameTiming] of recently + /// rasterized frames. + /// + /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use + /// [onReportTimings] directly because [SchedulerBinding.addTimingsCallback] + /// allows multiple callbacks. + /// + /// This can be used to see if the application has missed frames (through + /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high + /// latencies (through [FrameTiming.totalSpan]). + /// + /// Unlike [Timeline], the timing information here is available in the release + /// mode (additional to the profile and the debug mode). Hence this can be + /// used to monitor the application's performance in the wild. + /// + /// {@macro dart.ui.TimingsCallback.list} + /// + /// If this is null, no additional work will be done. If this is not null, + /// Flutter spends less than 0.1ms every 1 second to report the timings + /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for + /// 60fps), or 0.01% CPU usage per second. + TimingsCallback? get onReportTimings => _onReportTimings; + TimingsCallback? _onReportTimings; + Zone _onReportTimingsZone = Zone.root; + set onReportTimings(TimingsCallback? callback) { + if ((callback == null) != (_onReportTimings == null)) { + _setNeedsReportTimings(callback != null); + } + _onReportTimings = callback; + _onReportTimingsZone = Zone.current; + } + + late _SetNeedsReportTimingsFunc _setNeedsReportTimings; + void _nativeSetNeedsReportTimings(bool value) + native 'PlatformConfiguration_setNeedsReportTimings'; + + // Called from the engine, via hooks.dart + void _reportTimings(List timings) { + assert(timings.length % (FramePhase.values.length + 1) == 0); + final List frameTimings = []; + for (int i = 0; i < timings.length; i += FramePhase.values.length + 1) { + frameTimings.add(FrameTiming._(timings.sublist(i, i + FramePhase.values.length + 1))); + } + _invoke1(onReportTimings, _onReportTimingsZone, frameTimings); + } + + /// Sends a message to a platform-specific plugin. + /// + /// The `name` parameter determines which plugin receives the message. The + /// `data` parameter contains the message payload and is typically UTF-8 + /// encoded JSON but can be arbitrary data. If the plugin replies to the + /// message, `callback` will be called with the response. + /// + /// The framework invokes [callback] in the same zone in which this method was + /// called. + void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) { + final String? error = + _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data); + if (error != null) + throw Exception(error); + } + + String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) + native 'PlatformConfiguration_sendPlatformMessage'; + + /// Called whenever this platform dispatcher receives a message from a + /// platform-specific plugin. + /// + /// The `name` parameter determines which plugin sent the message. The `data` + /// parameter is the payload and is typically UTF-8 encoded JSON but can be + /// arbitrary data. + /// + /// Message handlers must call the function given in the `callback` parameter. + /// If the handler does not need to respond, the handler should pass null to + /// the callback. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + // TODO(ianh): Deprecate onPlatformMessage once the framework is moved over + // to using channel buffers exclusively. + PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; + PlatformMessageCallback? _onPlatformMessage; + Zone _onPlatformMessageZone = Zone.root; + set onPlatformMessage(PlatformMessageCallback? callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } + + /// Called by [_dispatchPlatformMessage]. + void _respondToPlatformMessage(int responseId, ByteData? data) + native 'PlatformConfiguration_respondToPlatformMessage'; + + /// Wraps the given [callback] in another callback that ensures that the + /// original callback is called in the zone it was registered in. + static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback( + PlatformMessageResponseCallback? callback, + ) { + if (callback == null) { + return null; + } + + // Store the zone in which the callback is being registered. + final Zone registrationZone = Zone.current; + + return (ByteData? data) { + registrationZone.runUnaryGuarded(callback, data); + }; + } + + /// Send a message to the framework using the [ChannelBuffers]. + /// + /// This method constructs the appropriate callback to respond + /// with the given `responseId`. It should only be called for messages + /// from the platform. + void _dispatchPlatformMessage(String name, ByteData? data, int responseId) { + if (name == ChannelBuffers.kControlChannelName) { + try { + channelBuffers.handleMessage(data!); + } finally { + _respondToPlatformMessage(responseId, null); + } + } else if (onPlatformMessage != null) { + _invoke3( + onPlatformMessage, + _onPlatformMessageZone, + name, + data, + (ByteData? responseData) { + _respondToPlatformMessage(responseId, responseData); + }, + ); + } else { + channelBuffers.push(name, data, (ByteData? responseData) { + _respondToPlatformMessage(responseId, responseData); + }); + } + } + + /// Set the debug name associated with this platform dispatcher's root + /// isolate. + /// + /// Normally debug names are automatically generated from the Dart port, entry + /// point, and source file. For example: `main.dart$main-1234`. + /// + /// This can be combined with flutter tools `--isolate-filter` flag to debug + /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. + /// Note that this does not rename any child isolates of the root. + void setIsolateDebugName(String name) native 'PlatformConfiguration_setIsolateDebugName'; + + /// The embedder can specify data that the isolate can request synchronously + /// on launch. This accessor fetches that data. + /// + /// This data is persistent for the duration of the Flutter application and is + /// available even after isolate restarts. Because of this lifecycle, the size + /// of this data must be kept to a minimum. + /// + /// For asynchronous communication between the embedder and isolate, a + /// platform channel may be used. + ByteData? getPersistentIsolateData() native 'PlatformConfiguration_getPersistentIsolateData'; + + /// Requests that, at the next appropriate opportunity, the [onBeginFrame] and + /// [onDrawFrame] callbacks be invoked. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'; + + /// Additional accessibility features that may be enabled by the platform. + AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; + + /// A callback that is invoked when the value of [accessibilityFeatures] + /// changes. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + VoidCallback? _onAccessibilityFeaturesChanged; + Zone _onAccessibilityFeaturesChangedZone = Zone.root; + set onAccessibilityFeaturesChanged(VoidCallback? callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFeaturesChangedZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _updateAccessibilityFeatures(int values) { + final AccessibilityFeatures newFeatures = AccessibilityFeatures._(values); + final PlatformConfiguration previousConfiguration = configuration; + if (newFeatures == previousConfiguration.accessibilityFeatures) { + return; + } + _configuration = previousConfiguration.copyWith( + accessibilityFeatures: newFeatures, + ); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone,); + _invoke(onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone,); + } + + /// Change the retained semantics data about this platform dispatcher. + /// + /// If [semanticsEnabled] is true, the user has requested that this function + /// be called whenever the semantic content of this platform dispatcher + /// changes. + /// + /// In either case, this function disposes the given update, which means the + /// semantics update cannot be used further. + void updateSemantics(SemanticsUpdate update) native 'PlatformConfiguration_updateSemantics'; + + /// The system-reported default locale of the device. + /// + /// This establishes the language and formatting conventions that application + /// should, if possible, use to render their user interface. + /// + /// This is the first locale selected by the user and is the user's primary + /// locale (the locale the device UI is displayed in) + /// + /// This is equivalent to `locales.first`, except that it will provide an + /// undefined (using the language tag "und") non-null locale if the [locales] + /// list has not been set or is empty. + Locale get locale => locales.isEmpty ? const Locale.fromSubtags() : locales.first; + + /// The full system-reported supported locales of the device. + /// + /// This establishes the language and formatting conventions that application + /// should, if possible, use to render their user interface. + /// + /// The list is ordered in order of priority, with lower-indexed locales being + /// preferred over higher-indexed ones. The first element is the primary + /// [locale]. + /// + /// The [onLocaleChanged] callback is called whenever this value changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + List get locales => configuration.locales; + + /// Performs the platform-native locale resolution. + /// + /// Each platform may return different results. + /// + /// If the platform fails to resolve a locale, then this will return null. + /// + /// This method returns synchronously and is a direct call to + /// platform specific APIs without invoking method channels. + Locale? computePlatformResolvedLocale(List supportedLocales) { + final List supportedLocalesData = []; + for (final Locale locale in supportedLocales) { + supportedLocalesData.add(locale.languageCode); + supportedLocalesData.add(locale.countryCode); + supportedLocalesData.add(locale.scriptCode); + } + + final List result = _computePlatformResolvedLocale(supportedLocalesData); + + if (result.isNotEmpty) { + return Locale.fromSubtags( + languageCode: result[0], + countryCode: result[1] == '' ? null : result[1], + scriptCode: result[2] == '' ? null : result[2]); + } + return null; + } + List _computePlatformResolvedLocale(List supportedLocalesData) native 'PlatformConfiguration_computePlatformResolvedLocale'; + + /// A callback that is invoked whenever [locale] changes value. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + VoidCallback? get onLocaleChanged => _onLocaleChanged; + VoidCallback? _onLocaleChanged; + Zone _onLocaleChangedZone = Zone.root; + set onLocaleChanged(VoidCallback? callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _updateLocales(List locales) { + const int stringsPerLocale = 4; + final int numLocales = locales.length ~/ stringsPerLocale; + final PlatformConfiguration previousConfiguration = configuration; + final List newLocales = []; + bool localesDiffer = numLocales != previousConfiguration.locales.length; + for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { + final String countryCode = locales[localeIndex * stringsPerLocale + 1]; + final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; + + newLocales.add(Locale.fromSubtags( + languageCode: locales[localeIndex * stringsPerLocale], + countryCode: countryCode.isEmpty ? null : countryCode, + scriptCode: scriptCode.isEmpty ? null : scriptCode, + )); + if (!localesDiffer && newLocales[localeIndex] != previousConfiguration.locales[localeIndex]) { + localesDiffer = true; + } + } + if (!localesDiffer) { + return; + } + _configuration = previousConfiguration.copyWith(locales: newLocales); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onLocaleChanged, _onLocaleChangedZone); + } + + // Called from the engine, via hooks.dart + String _localeClosure() => locale.toString(); + + /// The lifecycle state immediately after dart isolate initialization. + /// + /// This property will not be updated as the lifecycle changes. + /// + /// It is used to initialize [SchedulerBinding.lifecycleState] at startup with + /// any buffered lifecycle state events. + String get initialLifecycleState { + _initialLifecycleStateAccessed = true; + return _initialLifecycleState; + } + + late String _initialLifecycleState; + + /// Tracks if the initial state has been accessed. Once accessed, we will stop + /// updating the [initialLifecycleState], as it is not the preferred way to + /// access the state. + bool _initialLifecycleStateAccessed = false; + + // Called from the engine, via hooks.dart + void _updateLifecycleState(String state) { + // We do not update the state if the state has already been used to initialize + // the lifecycleState. + if (!_initialLifecycleStateAccessed) + _initialLifecycleState = state; + } + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + /// + /// This option is used by [showTimePicker]. + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + + /// The system-reported text scale. + /// + /// This establishes the text scaling factor to use when rendering text, + /// according to the user's platform preferences. + /// + /// The [onTextScaleFactorChanged] callback is called whenever this value + /// changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + double get textScaleFactor => configuration.textScaleFactor; + + /// A callback that is invoked whenever [textScaleFactor] changes value. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; + VoidCallback? _onTextScaleFactorChanged; + Zone _onTextScaleFactorChangedZone = Zone.root; + set onTextScaleFactorChanged(VoidCallback? callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// The setting indicating the current brightness mode of the host platform. + /// If the platform has no preference, [platformBrightness] defaults to + /// [Brightness.light]. + Brightness get platformBrightness => configuration.platformBrightness; + + /// A callback that is invoked whenever [platformBrightness] changes value. + /// + /// The framework invokes this callback in the same zone in which the callback + /// was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + VoidCallback? _onPlatformBrightnessChanged; + Zone _onPlatformBrightnessChangedZone = Zone.root; + set onPlatformBrightnessChanged(VoidCallback? callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _updateUserSettingsData(String jsonData) { + final Map data = json.decode(jsonData) as Map; + if (data.isEmpty) { + return; + } + + final double textScaleFactor = (data['textScaleFactor'] as num).toDouble(); + final bool alwaysUse24HourFormat = data['alwaysUse24HourFormat'] as bool; + final Brightness platformBrightness = + data['platformBrightness'] as String == 'dark' ? Brightness.dark : Brightness.light; + final PlatformConfiguration previousConfiguration = configuration; + final bool platformBrightnessChanged = + previousConfiguration.platformBrightness != platformBrightness; + final bool textScaleFactorChanged = previousConfiguration.textScaleFactor != textScaleFactor; + final bool alwaysUse24HourFormatChanged = + previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; + if (!platformBrightnessChanged && !textScaleFactorChanged && !alwaysUse24HourFormatChanged) { + return; + } + _configuration = previousConfiguration.copyWith( + textScaleFactor: textScaleFactor, + alwaysUse24HourFormat: alwaysUse24HourFormat, + platformBrightness: platformBrightness, + ); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + if (textScaleFactorChanged) { + _invoke(onTextScaleFactorChanged, _onTextScaleFactorChangedZone); + } + if (platformBrightnessChanged) { + _invoke(onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); + } + } + + /// Whether the user has requested that [updateSemantics] be called when the + /// semantic contents of a view changes. + /// + /// The [onSemanticsEnabledChanged] callback is called whenever this value + /// changes. + bool get semanticsEnabled => configuration.semanticsEnabled; + + /// A callback that is invoked when the value of [semanticsEnabled] changes. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + VoidCallback? _onSemanticsEnabledChanged; + Zone _onSemanticsEnabledChangedZone = Zone.root; + set onSemanticsEnabledChanged(VoidCallback? callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _updateSemanticsEnabled(bool enabled) { + final PlatformConfiguration previousConfiguration = configuration; + if (previousConfiguration.semanticsEnabled == enabled) { + return; + } + _configuration = previousConfiguration.copyWith( + semanticsEnabled: enabled, + ); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); + } + + /// A callback that is invoked whenever the user requests an action to be + /// performed. + /// + /// This callback is used when the user expresses the action they wish to + /// perform based on the semantics supplied by [updateSemantics]. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction; + SemanticsActionCallback? _onSemanticsAction; + Zone _onSemanticsActionZone = Zone.root; + set onSemanticsAction(SemanticsActionCallback? callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + // Called from the engine via hooks.dart. + void _updateFrameData(int frameNumber) { + final FrameData previous = _frameData; + if (previous.frameNumber == frameNumber) { + return; + } + _frameData = FrameData._(frameNumber: frameNumber); + _invoke(onFrameDataChanged, _onFrameDataChangedZone); + } + + /// The [FrameData] object for the current frame. + FrameData get frameData => _frameData; + FrameData _frameData = const FrameData._(); + + /// A callback that is invoked when the window updates the [FrameData]. + VoidCallback? get onFrameDataChanged => _onFrameDataChanged; + VoidCallback? _onFrameDataChanged; + Zone _onFrameDataChangedZone = Zone.root; + set onFrameDataChanged(VoidCallback? callback) { + _onFrameDataChanged = callback; + _onFrameDataChangedZone = Zone.current; + } + + // Called from the engine, via hooks.dart + void _dispatchSemanticsAction(int id, int action, ByteData? args) { + _invoke3( + onSemanticsAction, + _onSemanticsActionZone, + id, + SemanticsAction.values[action]!, + args, + ); + } + + /// The route or path that the embedder requested when the application was + /// launched. + /// + /// This will be the string "`/`" if no particular route was requested. + /// + /// ## Android + /// + /// On Android, calling + /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `createFlutterView` method in your `FlutterActivity` + /// subclass is a suitable time to set the value. The application's + /// `AndroidManifest.xml` file must also be updated to have a suitable + /// [``](https://developer.android.com/guide/topics/manifest/intent-filter-element.html). + /// + /// ## iOS + /// + /// On iOS, calling + /// [`FlutterViewController.setInitialRoute`](/objcdoc/Classes/FlutterViewController.html#/c:objc%28cs%29FlutterViewController%28im%29setInitialRoute:) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `application:didFinishLaunchingWithOptions:` method is a + /// suitable time to set this value. + /// + /// See also: + /// + /// * [Navigator], a widget that handles routing. + /// * [SystemChannels.navigation], which handles subsequent navigation + /// requests from the embedder. + String get defaultRouteName => _defaultRouteName(); + String _defaultRouteName() native 'PlatformConfiguration_defaultRouteName'; +} + +/// Configuration of the platform. +/// +/// Immutable class (but can't use @immutable in dart:ui) +class PlatformConfiguration { + /// Const constructor for [PlatformConfiguration]. + const PlatformConfiguration({ + this.accessibilityFeatures = const AccessibilityFeatures._(0), + this.alwaysUse24HourFormat = false, + this.semanticsEnabled = false, + this.platformBrightness = Brightness.light, + this.textScaleFactor = 1.0, + this.locales = const [], + this.defaultRouteName, + }); + + /// Copy a [PlatformConfiguration] with some fields replaced. + PlatformConfiguration copyWith({ + AccessibilityFeatures? accessibilityFeatures, + bool? alwaysUse24HourFormat, + bool? semanticsEnabled, + Brightness? platformBrightness, + double? textScaleFactor, + List? locales, + String? defaultRouteName, + }) { + return PlatformConfiguration( + accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, + alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, + semanticsEnabled: semanticsEnabled ?? this.semanticsEnabled, + platformBrightness: platformBrightness ?? this.platformBrightness, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + locales: locales ?? this.locales, + defaultRouteName: defaultRouteName ?? this.defaultRouteName, + ); + } + + /// Additional accessibility features that may be enabled by the platform. + final AccessibilityFeatures accessibilityFeatures; + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + final bool alwaysUse24HourFormat; + + /// Whether the user has requested that [updateSemantics] be called when the + /// semantic contents of a view changes. + final bool semanticsEnabled; + + /// The setting indicating the current brightness mode of the host platform. + /// If the platform has no preference, [platformBrightness] defaults to + /// [Brightness.light]. + final Brightness platformBrightness; + + /// The system-reported text scale. + final double textScaleFactor; + + /// The full system-reported supported locales of the device. + final List locales; + + /// The route or path that the embedder requested when the application was + /// launched. + final String? defaultRouteName; +} + +/// An immutable view configuration. +class ViewConfiguration { + /// A const constructor for an immutable [ViewConfiguration]. + const ViewConfiguration({ + this.window, + this.devicePixelRatio = 1.0, + this.geometry = Rect.zero, + this.visible = false, + this.viewInsets = WindowPadding.zero, + this.viewPadding = WindowPadding.zero, + this.systemGestureInsets = WindowPadding.zero, + this.padding = WindowPadding.zero, + this.gestureSettings = const GestureSettings(), + }); + + /// Copy this configuration with some fields replaced. + ViewConfiguration copyWith({ + FlutterView? window, + double? devicePixelRatio, + Rect? geometry, + bool? visible, + WindowPadding? viewInsets, + WindowPadding? viewPadding, + WindowPadding? systemGestureInsets, + WindowPadding? padding, + GestureSettings? gestureSettings + }) { + return ViewConfiguration( + window: window ?? this.window, + devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio, + geometry: geometry ?? this.geometry, + visible: visible ?? this.visible, + viewInsets: viewInsets ?? this.viewInsets, + viewPadding: viewPadding ?? this.viewPadding, + systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, + padding: padding ?? this.padding, + gestureSettings: gestureSettings ?? this.gestureSettings, + ); + } + + /// The top level view into which the view is placed and its geometry is + /// relative to. + /// + /// If null, then this configuration represents a top level view itself. + final FlutterView? window; + + /// The pixel density of the output surface. + final double devicePixelRatio; + + /// The geometry requested for the view on the screen or within its parent + /// window, in logical pixels. + final Rect geometry; + + /// Whether or not the view is currently visible on the screen. + final bool visible; + + /// The view insets, as it intersects with [Screen.viewInsets] for the screen + /// it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.viewInsets] area, [viewInsets] will be + /// [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this view rectangle into + /// which the application can draw, but over which the operating system will + /// likely place system UI, such as the keyboard or system menus, that fully + /// obscures any content. + final WindowPadding viewInsets; + + /// The view insets, as it intersects with [ScreenConfiguration.viewPadding] + /// for the screen it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.viewPadding] area, [viewPadding] will be + /// [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but which may be partially + /// obscured by system UI (such as the system notification area), or physical + /// intrusions in the display (e.g. overscan regions on television screens or + /// phone sensor housings). + final WindowPadding viewPadding; + + /// The view insets, as it intersects with + /// [ScreenConfiguration.systemGestureInsets] for the screen it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.systemGestureInsets] area, [systemGestureInsets] will + /// be [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but where the operating system + /// will consume input gestures for the sake of system navigation. + final WindowPadding systemGestureInsets; + + /// The view insets, as it intersects with [ScreenConfiguration.padding] for + /// the screen it is on. + /// + /// For instance, if the view doesn't overlap the + /// [ScreenConfiguration.padding] area, [padding] will be + /// [WindowPadding.zero]. + /// + /// The number of physical pixels on each side of this screen rectangle into + /// which the application can place a view, but which may be partially + /// obscured by system UI (such as the system notification area), or physical + /// intrusions in the display (e.g. overscan regions on television screens or + /// phone sensor housings). + final WindowPadding padding; + + /// Additional configuration for touch gestures performed on this view. + /// + /// For example, the touch slop defined in physical pixels may be provided + /// by the gesture settings and should be preferred over the framework + /// touch slop constant. + final GestureSettings gestureSettings; + + @override + String toString() { + return '$runtimeType[window: $window, geometry: $geometry]'; + } +} + +/// Various important time points in the lifetime of a frame. +/// +/// [FrameTiming] records a timestamp of each phase for performance analysis. +enum FramePhase { + /// The timestamp of the vsync signal given by the operating system. + /// + /// See also [FrameTiming.vsyncOverhead]. + vsyncStart, + + /// When the UI thread starts building a frame. + /// + /// See also [FrameTiming.buildDuration]. + buildStart, + + /// When the UI thread finishes building a frame. + /// + /// See also [FrameTiming.buildDuration]. + buildFinish, + + /// When the raster thread starts rasterizing a frame. + /// + /// See also [FrameTiming.rasterDuration]. + rasterStart, + + /// When the raster thread finishes rasterizing a frame. + /// + /// See also [FrameTiming.rasterDuration]. + rasterFinish, + + /// When the raster thread finished rasterizing a frame in wall-time. + /// + /// This is useful for correlating time raster finish time with the system + /// clock to integrate with other profiling tools. + rasterFinishWallTime, +} + +/// Time-related performance metrics of a frame. +/// +/// If you're using the whole Flutter framework, please use +/// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using +/// [PlatformDispatcher.onReportTimings] directly because +/// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If +/// [SchedulerBinding] is unavailable, then see [PlatformDispatcher.onReportTimings] +/// for how to get this. +/// +/// The metrics in debug mode (`flutter run` without any flags) may be very +/// different from those in profile and release modes due to the debug overhead. +/// Therefore it's recommended to only monitor and analyze performance metrics +/// in profile and release modes. +class FrameTiming { + /// Construct [FrameTiming] with raw timestamps in microseconds. + /// + /// This constructor is used for unit test only. Real [FrameTiming]s should + /// be retrieved from [PlatformDispatcher.onReportTimings]. + /// + /// If the [frameNumber] is not provided, it defaults to `-1`. + factory FrameTiming({ + required int vsyncStart, + required int buildStart, + required int buildFinish, + required int rasterStart, + required int rasterFinish, + required int rasterFinishWallTime, + int frameNumber = -1, + }) { + return FrameTiming._([ + vsyncStart, + buildStart, + buildFinish, + rasterStart, + rasterFinish, + rasterFinishWallTime, + frameNumber, + ]); + } + + /// Construct [FrameTiming] with raw timestamps in microseconds. + /// + /// List [timestamps] must have the same number of elements as + /// [FramePhase.values]. + /// + /// This constructor is usually only called by the Flutter engine, or a test. + /// To get the [FrameTiming] of your app, see [PlatformDispatcher.onReportTimings]. + FrameTiming._(this._timestamps) + : assert(_timestamps.length == FramePhase.values.length + 1); + + /// This is a raw timestamp in microseconds from some epoch. The epoch in all + /// [FrameTiming] is the same, but it may not match [DateTime]'s epoch. + int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; + + Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); + + /// The duration to build the frame on the UI thread. + /// + /// The build starts approximately when [PlatformDispatcher.onBeginFrame] is + /// called. The [Duration] in the [PlatformDispatcher.onBeginFrame] callback + /// is exactly the `Duration(microseconds: + /// timestampInMicroseconds(FramePhase.buildStart))`. + /// + /// The build finishes when [FlutterView.render] is called. + /// + /// {@template dart.ui.FrameTiming.fps_smoothness_milliseconds} + /// To ensure smooth animations of X fps, this should not exceed 1000/X + /// milliseconds. + /// {@endtemplate} + /// {@template dart.ui.FrameTiming.fps_milliseconds} + /// That's about 16ms for 60fps, and 8ms for 120fps. + /// {@endtemplate} + Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); + + /// The duration to rasterize the frame on the raster thread. + /// + /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} + /// {@macro dart.ui.FrameTiming.fps_milliseconds} + Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); + + /// The duration between receiving the vsync signal and starting building the + /// frame. + Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); + + /// The timespan between vsync start and raster finish. + /// + /// To achieve the lowest latency on an X fps display, this should not exceed + /// 1000/X milliseconds. + /// {@macro dart.ui.FrameTiming.fps_milliseconds} + /// + /// See also [vsyncOverhead], [buildDuration] and [rasterDuration]. + Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); + + /// The frame key associated with this frame measurement. + int get frameNumber => _timestamps.last; + + final List _timestamps; // in microseconds + + String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms'; + + @override + String toString() { + return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, vsyncOverhead: ${_formatMS(vsyncOverhead)}, totalSpan: ${_formatMS(totalSpan)}, frameNumber: ${_timestamps.last})'; + } +} + +/// States that an application can be in. +/// +/// The values below describe notifications from the operating system. +/// Applications should not expect to always receive all possible +/// notifications. For example, if the users pulls out the battery from the +/// device, no notification will be sent before the application is suddenly +/// terminated, along with the rest of the operating system. +/// +/// See also: +/// +/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state +/// from the widgets layer. +enum AppLifecycleState { + /// The application is visible and responding to user input. + resumed, + + /// The application is in an inactive state and is not receiving user input. + /// + /// On iOS, this state corresponds to an app or the Flutter host view running + /// in the foreground inactive state. Apps transition to this state when in + /// a phone call, responding to a TouchID request, when entering the app + /// switcher or the control center, or when the UIViewController hosting the + /// Flutter app is transitioning. + /// + /// On Android, this corresponds to an app or the Flutter host view running + /// in the foreground inactive state. Apps transition to this state when + /// another activity is focused, such as a split-screen app, a phone call, + /// a picture-in-picture app, a system dialog, or another window. + /// + /// Apps in this state should assume that they may be [paused] at any time. + inactive, + + /// The application is not currently visible to the user, not responding to + /// user input, and running in the background. + /// + /// When the application is in this state, the engine will not call the + /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] + /// callbacks. + paused, + + /// The application is still hosted on a flutter engine but is detached from + /// any host views. + /// + /// When the application is in this state, the engine is running without + /// a view. It can either be in the progress of attaching a view when engine + /// was first initializes, or after the view being destroyed due to a Navigator + /// pop. + detached, +} + +/// A representation of distances for each of the four edges of a rectangle, +/// used to encode the view insets and padding that applications should place +/// around their user interface, as exposed by [FlutterView.viewInsets] and +/// [FlutterView.padding]. View insets and padding are preferably read via +/// [MediaQuery.of]. +/// +/// For a generic class that represents distances around a rectangle, see the +/// [EdgeInsets] class. +/// +/// See also: +/// +/// * [WidgetsBindingObserver], for a widgets layer mechanism to receive +/// notifications when the padding changes. +/// * [MediaQuery.of], for the preferred mechanism for accessing these values. +/// * [Scaffold], which automatically applies the padding in material design +/// applications. +class WindowPadding { + const WindowPadding._({ required this.left, required this.top, required this.right, required this.bottom }); + + /// The distance from the left edge to the first unpadded pixel, in physical pixels. + final double left; + + /// The distance from the top edge to the first unpadded pixel, in physical pixels. + final double top; + + /// The distance from the right edge to the first unpadded pixel, in physical pixels. + final double right; + + /// The distance from the bottom edge to the first unpadded pixel, in physical pixels. + final double bottom; + + /// A window padding that has zeros for each edge. + static const WindowPadding zero = WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); + + @override + String toString() { + return 'WindowPadding(left: $left, top: $top, right: $right, bottom: $bottom)'; + } +} + +/// An identifier used to select a user's language and formatting preferences. +/// +/// This represents a [Unicode Language +/// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) +/// (i.e. without Locale extensions), except variants are not supported. +/// +/// Locales are canonicalized according to the "preferred value" entries in the +/// [IANA Language Subtag +/// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). +/// For example, `const Locale('he')` and `const Locale('iw')` are equal and +/// both have the [languageCode] `he`, because `iw` is a deprecated language +/// subtag that was replaced by the subtag `he`. +/// +/// See also: +/// +/// * [PlatformDispatcher.locale], which specifies the system's currently selected +/// [Locale]. +class Locale { + /// Creates a new Locale object. The first argument is the + /// primary language subtag, the second is the region (also + /// referred to as 'country') subtag. + /// + /// For example: + /// + /// ```dart + /// const Locale swissFrench = Locale('fr', 'CH'); + /// const Locale canadianFrench = Locale('fr', 'CA'); + /// ``` + /// + /// The primary language subtag must not be null. The region subtag is + /// optional. When there is no region/country subtag, the parameter should + /// be omitted or passed `null` instead of an empty-string. + /// + /// The subtag values are _case sensitive_ and must be one of the valid + /// subtags according to CLDR supplemental data: + /// [language](https://github.com/unicode-org/cldr/blob/master/common/validity/language.xml), + /// [region](https://github.com/unicode-org/cldr/blob/master/common/validity/region.xml). The + /// primary language subtag must be at least two and at most eight lowercase + /// letters, but not four letters. The region region subtag must be two + /// uppercase letters or three digits. See the [Unicode Language + /// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) + /// specification. + /// + /// Validity is not checked by default, but some methods may throw away + /// invalid data. + /// + /// See also: + /// + /// * [Locale.fromSubtags], which also allows a [scriptCode] to be + /// specified. + const Locale( + this._languageCode, [ + this._countryCode, + ]) : assert(_languageCode != null), + assert(_languageCode != ''), + scriptCode = null; + + /// Creates a new Locale object. + /// + /// The keyword arguments specify the subtags of the Locale. + /// + /// The subtag values are _case sensitive_ and must be valid subtags according + /// to CLDR supplemental data: + /// [language](https://github.com/unicode-org/cldr/blob/master/common/validity/language.xml), + /// [script](https://github.com/unicode-org/cldr/blob/master/common/validity/script.xml) and + /// [region](https://github.com/unicode-org/cldr/blob/master/common/validity/region.xml) for + /// each of languageCode, scriptCode and countryCode respectively. + /// + /// The [languageCode] subtag is optional. When there is no language subtag, + /// the parameter should be omitted or set to "und". When not supplied, the + /// [languageCode] defaults to "und", an undefined language code. + /// + /// The [countryCode] subtag is optional. When there is no country subtag, + /// the parameter should be omitted or passed `null` instead of an empty-string. + /// + /// Validity is not checked by default, but some methods may throw away + /// invalid data. + const Locale.fromSubtags({ + String languageCode = 'und', + this.scriptCode, + String? countryCode, + }) : assert(languageCode != null), + assert(languageCode != ''), + _languageCode = languageCode, + assert(scriptCode != ''), + assert(countryCode != ''), + _countryCode = countryCode; + + /// The primary language subtag for the locale. + /// + /// This must not be null. It may be 'und', representing 'undefined'. + /// + /// This is expected to be string registered in the [IANA Language Subtag + /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) + /// with the type "language". The string specified must match the case of the + /// string in the registry. + /// + /// Language subtags that are deprecated in the registry and have a preferred + /// code are changed to their preferred code. For example, `const + /// Locale('he')` and `const Locale('iw')` are equal, and both have the + /// [languageCode] `he`, because `iw` is a deprecated language subtag that was + /// replaced by the subtag `he`. + /// + /// This must be a valid Unicode Language subtag as listed in [Unicode CLDR + /// supplemental + /// data](https://github.com/unicode-org/cldr/blob/master/common/validity/language.xml). + /// + /// See also: + /// + /// * [Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; + final String _languageCode; + + // This map is generated by //flutter/tools/gen_locale.dart + // Mappings generated for language subtag registry as of 2019-02-27. + static const Map _deprecatedLanguageSubtagMap = { + 'in': 'id', // Indonesian; deprecated 1989-01-01 + 'iw': 'he', // Hebrew; deprecated 1989-01-01 + 'ji': 'yi', // Yiddish; deprecated 1989-01-01 + 'jw': 'jv', // Javanese; deprecated 2001-08-13 + 'mo': 'ro', // Moldavian, Moldovan; deprecated 2008-11-22 + 'aam': 'aas', // Aramanik; deprecated 2015-02-12 + 'adp': 'dz', // Adap; deprecated 2015-02-12 + 'aue': 'ktz', // ǂKxʼauǁʼein; deprecated 2015-02-12 + 'ayx': 'nun', // Ayi (China); deprecated 2011-08-16 + 'bgm': 'bcg', // Baga Mboteni; deprecated 2016-05-30 + 'bjd': 'drl', // Bandjigali; deprecated 2012-08-12 + 'ccq': 'rki', // Chaungtha; deprecated 2012-08-12 + 'cjr': 'mom', // Chorotega; deprecated 2010-03-11 + 'cka': 'cmr', // Khumi Awa Chin; deprecated 2012-08-12 + 'cmk': 'xch', // Chimakum; deprecated 2010-03-11 + 'coy': 'pij', // Coyaima; deprecated 2016-05-30 + 'cqu': 'quh', // Chilean Quechua; deprecated 2016-05-30 + 'drh': 'khk', // Darkhat; deprecated 2010-03-11 + 'drw': 'prs', // Darwazi; deprecated 2010-03-11 + 'gav': 'dev', // Gabutamon; deprecated 2010-03-11 + 'gfx': 'vaj', // Mangetti Dune ǃXung; deprecated 2015-02-12 + 'ggn': 'gvr', // Eastern Gurung; deprecated 2016-05-30 + 'gti': 'nyc', // Gbati-ri; deprecated 2015-02-12 + 'guv': 'duz', // Gey; deprecated 2016-05-30 + 'hrr': 'jal', // Horuru; deprecated 2012-08-12 + 'ibi': 'opa', // Ibilo; deprecated 2012-08-12 + 'ilw': 'gal', // Talur; deprecated 2013-09-10 + 'jeg': 'oyb', // Jeng; deprecated 2017-02-23 + 'kgc': 'tdf', // Kasseng; deprecated 2016-05-30 + 'kgh': 'kml', // Upper Tanudan Kalinga; deprecated 2012-08-12 + 'koj': 'kwv', // Sara Dunjo; deprecated 2015-02-12 + 'krm': 'bmf', // Krim; deprecated 2017-02-23 + 'ktr': 'dtp', // Kota Marudu Tinagas; deprecated 2016-05-30 + 'kvs': 'gdj', // Kunggara; deprecated 2016-05-30 + 'kwq': 'yam', // Kwak; deprecated 2015-02-12 + 'kxe': 'tvd', // Kakihum; deprecated 2015-02-12 + 'kzj': 'dtp', // Coastal Kadazan; deprecated 2016-05-30 + 'kzt': 'dtp', // Tambunan Dusun; deprecated 2016-05-30 + 'lii': 'raq', // Lingkhim; deprecated 2015-02-12 + 'lmm': 'rmx', // Lamam; deprecated 2014-02-28 + 'meg': 'cir', // Mea; deprecated 2013-09-10 + 'mst': 'mry', // Cataelano Mandaya; deprecated 2010-03-11 + 'mwj': 'vaj', // Maligo; deprecated 2015-02-12 + 'myt': 'mry', // Sangab Mandaya; deprecated 2010-03-11 + 'nad': 'xny', // Nijadali; deprecated 2016-05-30 + 'ncp': 'kdz', // Ndaktup; deprecated 2018-03-08 + 'nnx': 'ngv', // Ngong; deprecated 2015-02-12 + 'nts': 'pij', // Natagaimas; deprecated 2016-05-30 + 'oun': 'vaj', // ǃOǃung; deprecated 2015-02-12 + 'pcr': 'adx', // Panang; deprecated 2013-09-10 + 'pmc': 'huw', // Palumata; deprecated 2016-05-30 + 'pmu': 'phr', // Mirpur Panjabi; deprecated 2015-02-12 + 'ppa': 'bfy', // Pao; deprecated 2016-05-30 + 'ppr': 'lcq', // Piru; deprecated 2013-09-10 + 'pry': 'prt', // Pray 3; deprecated 2016-05-30 + 'puz': 'pub', // Purum Naga; deprecated 2014-02-28 + 'sca': 'hle', // Sansu; deprecated 2012-08-12 + 'skk': 'oyb', // Sok; deprecated 2017-02-23 + 'tdu': 'dtp', // Tempasuk Dusun; deprecated 2016-05-30 + 'thc': 'tpo', // Tai Hang Tong; deprecated 2016-05-30 + 'thx': 'oyb', // The; deprecated 2015-02-12 + 'tie': 'ras', // Tingal; deprecated 2011-08-16 + 'tkk': 'twm', // Takpa; deprecated 2011-08-16 + 'tlw': 'weo', // South Wemale; deprecated 2012-08-12 + 'tmp': 'tyj', // Tai Mène; deprecated 2016-05-30 + 'tne': 'kak', // Tinoc Kallahan; deprecated 2016-05-30 + 'tnf': 'prs', // Tangshewi; deprecated 2010-03-11 + 'tsf': 'taj', // Southwestern Tamang; deprecated 2015-02-12 + 'uok': 'ema', // Uokha; deprecated 2015-02-12 + 'xba': 'cax', // Kamba (Brazil); deprecated 2016-05-30 + 'xia': 'acn', // Xiandao; deprecated 2013-09-10 + 'xkh': 'waw', // Karahawyana; deprecated 2016-05-30 + 'xsj': 'suj', // Subi; deprecated 2015-02-12 + 'ybd': 'rki', // Yangbye; deprecated 2012-08-12 + 'yma': 'lrr', // Yamphe; deprecated 2012-08-12 + 'ymt': 'mtm', // Mator-Taygi-Karagas; deprecated 2015-02-12 + 'yos': 'zom', // Yos; deprecated 2013-09-10 + 'yuu': 'yug', // Yugh; deprecated 2014-02-28 + }; + + /// The script subtag for the locale. + /// + /// This may be null, indicating that there is no specified script subtag. + /// + /// This must be a valid Unicode Language Identifier script subtag as listed + /// in [Unicode CLDR supplemental + /// data](https://github.com/unicode-org/cldr/blob/master/common/validity/script.xml). + /// + /// See also: + /// + /// * [Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + final String? scriptCode; + + /// The region subtag for the locale. + /// + /// This may be null, indicating that there is no specified region subtag. + /// + /// This is expected to be string registered in the [IANA Language Subtag + /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) + /// with the type "region". The string specified must match the case of the + /// string in the registry. + /// + /// Region subtags that are deprecated in the registry and have a preferred + /// code are changed to their preferred code. For example, `const Locale('de', + /// 'DE')` and `const Locale('de', 'DD')` are equal, and both have the + /// [countryCode] `DE`, because `DD` is a deprecated language subtag that was + /// replaced by the subtag `DE`. + /// + /// See also: + /// + /// * [Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + String? get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; + final String? _countryCode; + + // This map is generated by //flutter/tools/gen_locale.dart + // Mappings generated for language subtag registry as of 2019-02-27. + static const Map _deprecatedRegionSubtagMap = { + 'BU': 'MM', // Burma; deprecated 1989-12-05 + 'DD': 'DE', // German Democratic Republic; deprecated 1990-10-30 + 'FX': 'FR', // Metropolitan France; deprecated 1997-07-14 + 'TP': 'TL', // East Timor; deprecated 2002-05-20 + 'YD': 'YE', // Democratic Yemen; deprecated 1990-08-14 + 'ZR': 'CD', // Zaire; deprecated 1997-07-14 + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) + return true; + if (other is! Locale) { + return false; + } + final String? countryCode = _countryCode; + final String? otherCountryCode = other.countryCode; + return other.languageCode == languageCode + && other.scriptCode == scriptCode // scriptCode cannot be '' + && (other.countryCode == countryCode // Treat '' as equal to null. + || otherCountryCode != null && otherCountryCode.isEmpty && countryCode == null + || countryCode != null && countryCode.isEmpty && other.countryCode == null); + } + + @override + int get hashCode => hashValues(languageCode, scriptCode, countryCode == '' ? null : countryCode); + + static Locale? _cachedLocale; + static String? _cachedLocaleString; + + /// Returns a string representing the locale. + /// + /// This identifier happens to be a valid Unicode Locale Identifier using + /// underscores as separator, however it is intended to be used for debugging + /// purposes only. For parsable results, use [toLanguageTag] instead. + @keepToString + @override + String toString() { + if (!identical(_cachedLocale, this)) { + _cachedLocale = this; + _cachedLocaleString = _rawToString('_'); + } + return _cachedLocaleString!; + } + + /// Returns a syntactically valid Unicode BCP47 Locale Identifier. + /// + /// Some examples of such identifiers: "en", "es-419", "hi-Deva-IN" and + /// "zh-Hans-CN". See http://www.unicode.org/reports/tr35/ for technical + /// details. + String toLanguageTag() => _rawToString('-'); + + String _rawToString(String separator) { + final StringBuffer out = StringBuffer(languageCode); + if (scriptCode != null && scriptCode!.isNotEmpty) + out.write('$separator$scriptCode'); + if (_countryCode != null && _countryCode!.isNotEmpty) + out.write('$separator$countryCode'); + return out.toString(); + } +} diff --git a/lib/ui/plugins.dart b/lib/ui/plugins.dart index 9622852fd7566..e21e1bdcb8c20 100644 --- a/lib/ui/plugins.dart +++ b/lib/ui/plugins.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// A wrapper for a raw callback handle. @@ -15,7 +15,7 @@ class CallbackHandle { /// Only values produced by a call to [CallbackHandle.toRawHandle] should be /// used, otherwise this object will be an invalid handle. CallbackHandle.fromRawHandle(this._handle) - : assert(_handle != null, "'_handle' must not be null."); // ignore: unnecessary_null_comparison + : assert(_handle != null, "'_handle' must not be null."); final int _handle; @@ -61,7 +61,7 @@ class PluginUtilities { /// original callback. If `callback` is not a top-level or static function, /// null is returned. static CallbackHandle? getCallbackHandle(Function callback) { - assert(callback != null, "'callback' must not be null."); // ignore: unnecessary_null_comparison + assert(callback != null, "'callback' must not be null."); return _forwardCache.putIfAbsent(callback, () { final int? handle = _getCallbackHandle(callback); return handle != null ? CallbackHandle.fromRawHandle(handle) : null; @@ -77,7 +77,7 @@ class PluginUtilities { /// [PluginUtilities.getCallbackHandle], null is returned. Otherwise, a /// tear-off of the callback associated with `handle` is returned. static Function? getCallbackFromHandle(CallbackHandle handle) { - assert(handle != null, "'handle' must not be null."); // ignore: unnecessary_null_comparison + assert(handle != null, "'handle' must not be null."); return _backwardCache.putIfAbsent( handle, () => _getCallbackFromHandle(handle.toRawHandle())); } diff --git a/lib/ui/plugins/callback_cache.cc b/lib/ui/plugins/callback_cache.cc index 0194eb507bcdd..218134504712f 100644 --- a/lib/ui/plugins/callback_cache.cc +++ b/lib/ui/plugins/callback_cache.cc @@ -1,7 +1,6 @@ // 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. -// FLUTTER_NOLINT #include "flutter/lib/ui/plugins/callback_cache.h" diff --git a/lib/ui/pointer.dart b/lib/ui/pointer.dart index 551763f9d03f4..1b2bafbd11e7e 100644 --- a/lib/ui/pointer.dart +++ b/lib/ui/pointer.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// How the pointer has changed since the last report. @@ -306,7 +306,7 @@ class PointerData { /// A sequence of reports about the state of pointers. class PointerDataPacket { /// Creates a packet of pointer data reports. - const PointerDataPacket({ this.data = const [] }) : assert(data != null); // ignore: unnecessary_null_comparison + const PointerDataPacket({ this.data = const [] }) : assert(data != null); /// Data about the individual pointers in this packet. /// diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index 56e371eb770b6..b702109bc4244 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; /// The possible actions that can be conveyed from the operating system @@ -15,7 +15,7 @@ part of dart.ui; /// See also: /// - file://./../../lib/ui/semantics/semantics_node.h class SemanticsAction { - const SemanticsAction._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison + const SemanticsAction._(this.index) : assert(index != null); static const int _kTapIndex = 1 << 0; static const int _kLongPressIndex = 1 << 1; @@ -38,6 +38,7 @@ class SemanticsAction { static const int _kDismissIndex = 1 << 18; static const int _kMoveCursorForwardByWordIndex = 1 << 19; static const int _kMoveCursorBackwardByWordIndex = 1 << 20; + static const int _kSetText = 1 << 21; // READ THIS: if you add an action here, you MUST update the // numSemanticsActions value in testing/dart/semantics_test.dart, or tests // will fail. @@ -115,6 +116,14 @@ class SemanticsAction { /// movement should extend (or start) a selection. static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._(_kMoveCursorBackwardByCharacterIndex); + /// Replaces the current text in the text field. + /// + /// This is for example used by the text editing in voice access. + /// + /// The action includes a string argument, which is the new text to + /// replace. + static const SemanticsAction setText = SemanticsAction._(_kSetText); + /// Set the text selection to the given range. /// /// The provided argument is a Map which includes the keys `base` @@ -218,6 +227,7 @@ class SemanticsAction { _kDismissIndex: dismiss, _kMoveCursorForwardByWordIndex: moveCursorForwardByWord, _kMoveCursorBackwardByWordIndex: moveCursorBackwardByWord, + _kSetText: setText, }; @override @@ -265,6 +275,8 @@ class SemanticsAction { return 'SemanticsAction.moveCursorForwardByWord'; case _kMoveCursorBackwardByWordIndex: return 'SemanticsAction.moveCursorBackwardByWord'; + case _kSetText: + return 'SemanticsAction.setText'; } assert(false, 'Unhandled index: $index'); return ''; @@ -301,13 +313,14 @@ class SemanticsFlag { static const int _kIsFocusableIndex = 1 << 21; static const int _kIsLinkIndex = 1 << 22; static const int _kIsSliderIndex = 1 << 23; + static const int _kIsKeyboardKeyIndex = 1 << 24; // READ THIS: if you add a flag here, you MUST update the numSemanticsFlags // value in testing/dart/semantics_test.dart, or tests will fail. Also, // please update the Flag enum in // flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java, // and the SemanticsFlag class in lib/web_ui/lib/src/ui/semantics.dart. - const SemanticsFlag._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison + const SemanticsFlag._(this.index) : assert(index != null); /// The numerical value for this flag. /// @@ -362,6 +375,9 @@ class SemanticsFlag { /// Whether the semantic node represents a slider. static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex); + /// Whether the semantic node represents a keyboard key. + static const SemanticsFlag isKeyboardKey = SemanticsFlag._(_kIsKeyboardKeyIndex); + /// Whether the semantic node is read only. /// /// Only applicable when [isTextField] is true. @@ -479,6 +495,10 @@ class SemanticsFlag { /// the semantics tree altogether. Hidden elements are only included in the /// semantics tree to work around platform limitations and they are mainly /// used to implement accessibility scrolling on iOS. + /// + /// See also: + /// + /// * [RenderObject.describeSemanticsClip] static const SemanticsFlag isHidden = SemanticsFlag._(_kIsHiddenIndex); /// Whether the semantics node represents an image. @@ -559,6 +579,7 @@ class SemanticsFlag { _kIsFocusableIndex: isFocusable, _kIsLinkIndex: isLink, _kIsSliderIndex: isSlider, + _kIsKeyboardKeyIndex: isKeyboardKey, }; @override @@ -612,18 +633,129 @@ class SemanticsFlag { return 'SemanticsFlag.isLink'; case _kIsSliderIndex: return 'SemanticsFlag.isSlider'; + case _kIsKeyboardKeyIndex: + return 'SemanticsFlag.isKeyboardKey'; } assert(false, 'Unhandled index: $index'); return ''; } } +// When adding a new StringAttribute, the classes in these files must be +// updated as well. +// * engine/src/flutter/lib/web_ui/lib/src/ui/semantics.dart +// * engine/src/flutter/lib/ui/semantics/string_attribute.h +// * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java + +/// An abstract interface for string attributes that affects how assistive +/// technologies, e.g. VoiceOver or TalkBack, treat the text. +/// +/// See also: +/// +/// * [AttributedString], where the string attributes are used. +/// * [SpellOutStringAttribute], which causes the assistive technologies to +/// spell out the string character by character when announcing the string. +/// * [LocaleStringAttribute], which causes the assistive technologies to +/// treat the string in the specific language. +abstract class StringAttribute extends NativeFieldWrapperClass1 { + StringAttribute._({ + required this.range, + }); + + /// The range of the text to which this attribute applies. + final TextRange range; + + /// Creates a new attribute with all properties copied except for range, which + /// is updated to the specified value. + /// + /// For example, the [LocaleStringAttribute] specifies a [Locale] for its + /// range of characters. Copying it will result in a new + /// [LocaleStringAttribute] that has the same locale but an updated + /// [TextRange]. + StringAttribute copy({required TextRange range}); +} + +/// A string attribute that causes the assistive technologies, e.g. VoiceOver, +/// to spell out the string character by character. +/// +/// See also: +/// +/// * [AttributedString], where the string attributes are used. +/// * [LocaleStringAttribute], which causes the assistive technologies to +/// treat the string in the specific language. +class SpellOutStringAttribute extends StringAttribute { + /// Creates a string attribute that denotes the text in [range] must be + /// spell out when the assistive technologies announce the string. + SpellOutStringAttribute({ + required TextRange range, + }) : super._(range: range) { + _initSpellOutStringAttribute(this, range.start, range.end); + } + + void _initSpellOutStringAttribute( + SpellOutStringAttribute instance, + int start, + int end, + ) native 'NativeStringAttribute_initSpellOutStringAttribute'; + + @override + StringAttribute copy({required TextRange range}) { + return SpellOutStringAttribute(range: range); + } + + @override + String toString() { + return 'SpellOutStringAttribute($range)'; + } +} + +/// A string attribute that causes the assistive technologies, e.g. VoiceOver, +/// to treat string as a certain language. +/// +/// See also: +/// +/// * [AttributedString], where the string attributes are used. +/// * [SpellOutStringAttribute], which causes the assistive technologies to +/// spell out the string character by character when announcing the string. +class LocaleStringAttribute extends StringAttribute { + /// Creates a string attribute that denotes the text in [range] must be + /// treated as the language specified by the [locale] when the assistive + /// technologies announce the string. + LocaleStringAttribute({ + required TextRange range, + required this.locale, + }) : super._(range: range) { + _initLocaleStringAttribute(this, range.start, range.end, locale.toLanguageTag()); + } + + /// The lanuage of this attribute. + final Locale locale; + + void _initLocaleStringAttribute( + LocaleStringAttribute instance, + int start, + int end, + String locale, + ) native 'NativeStringAttribute_initLocaleStringAttribute'; + + @override + StringAttribute copy({required TextRange range}) { + return LocaleStringAttribute(range: range, locale: locale); + } + + @override + String toString() { + return 'LocaleStringAttribute($range, ${locale.toLanguageTag()})'; + } +} + /// An object that creates [SemanticsUpdate] objects. /// /// Once created, the [SemanticsUpdate] objects can be passed to -/// [Window.updateSemantics] to update the semantics conveyed to the user. +/// [PlatformDispatcher.updateSemantics] to update the semantics conveyed to the +/// user. @pragma('vm:entry-point') -class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { +class SemanticsUpdateBuilder extends NativeFieldWrapperClass1 { /// Creates an empty [SemanticsUpdateBuilder] object. @pragma('vm:entry-point') SemanticsUpdateBuilder() { _constructor(); } @@ -649,10 +781,10 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// /// The `actions` are a bit field of [SemanticsAction]s that can be undertaken /// by this node. If the user wishes to undertake one of these actions on this - /// node, the [Window.onSemanticsAction] will be called with `id` and one of - /// the possible [SemanticsAction]s. Because the semantics tree is maintained - /// asynchronously, the [Window.onSemanticsAction] callback might be called - /// with an action that is no longer possible. + /// node, the [PlatformDispatcher.onSemanticsAction] will be called with `id` + /// and one of the possible [SemanticsAction]s. Because the semantics tree is + /// maintained asynchronously, the [PlatformDispatcher.onSemanticsAction] + /// callback might be called with an action that is no longer possible. /// /// The `label` is a string that describes this node. The `value` property /// describes the current value of the node as a string. The `increasedValue` @@ -662,6 +794,12 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// string describes what result an action performed on this node has. The /// reading direction of all these strings is given by `textDirection`. /// + /// The `labelAttirbutes`, `valueAttirbutes`, `hintAttributes`, + /// `increasedValueAttirbutes`, and `decreasedValueAttributes` are the lists of + /// [StringAttribute] carried by the `label`, `value`, `hint`, `increasedValue`, + /// and `decreasedValue` respectively. Their contents must not be changed during + /// the semantics update. + /// /// The fields `textSelectionBase` and `textSelectionExtent` describe the /// currently selected text within `value`. A value of -1 indicates no /// current text selection base or extent. @@ -719,10 +857,15 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { required double thickness, required Rect rect, required String label, - required String hint, + List? labelAttributes, required String value, + List? valueAttributes, required String increasedValue, + List? increasedValueAttributes, required String decreasedValue, + List? decreasedValueAttributes, + required String hint, + List? hintAttributes, TextDirection? textDirection, required Float64List transform, required Int32List childrenInTraversalOrder, @@ -731,7 +874,6 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { }) { assert(_matrix4IsValid(transform)); assert( - // ignore: unnecessary_null_comparison scrollChildren == 0 || scrollChildren == null || (scrollChildren > 0 && childrenInHitTestOrder != null), 'If a node has scrollChildren, it must have childrenInHitTestOrder', ); @@ -756,10 +898,15 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { elevation, thickness, label, - hint, + labelAttributes, value, + valueAttributes, increasedValue, + increasedValueAttributes, decreasedValue, + decreasedValueAttributes, + hint, + hintAttributes, textDirection != null ? textDirection.index + 1 : 0, transform, childrenInTraversalOrder, @@ -788,10 +935,15 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { double elevation, double thickness, String label, - String hint, + List? labelAttributes, String value, + List? valueAttributes, String increasedValue, + List? increasedValueAttributes, String decreasedValue, + List? decreasedValueAttributes, + String hint, + List? hintAttributes, int textDirection, Float64List transform, Int32List childrenInTraversalOrder, @@ -815,8 +967,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// [SemanticsAction.index] value. For custom actions this argument should not be /// provided. void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}) { - assert(id != null); // ignore: unnecessary_null_comparison - assert(overrideId != null); // ignore: unnecessary_null_comparison + assert(id != null); + assert(overrideId != null); _updateCustomAction(id, label, hint, overrideId); } void _updateCustomAction( @@ -828,8 +980,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Creates a [SemanticsUpdate] object that encapsulates the updates recorded /// by this object. /// - /// The returned object can be passed to [Window.updateSemantics] to actually - /// update the semantics retained by the system. + /// The returned object can be passed to [PlatformDispatcher.updateSemantics] + /// to actually update the semantics retained by the system. SemanticsUpdate build() { final SemanticsUpdate semanticsUpdate = SemanticsUpdate._(); _build(semanticsUpdate); @@ -843,9 +995,9 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. /// /// Semantics updates can be applied to the system's retained semantics tree -/// using the [Window.updateSemantics] method. +/// using the [PlatformDispatcher.updateSemantics] method. @pragma('vm:entry-point') -class SemanticsUpdate extends NativeFieldWrapperClass2 { +class SemanticsUpdate extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// diff --git a/lib/ui/semantics/semantics_node.h b/lib/ui/semantics/semantics_node.h index 861a1ad0f0a1c..b75d94eb5a0fc 100644 --- a/lib/ui/semantics/semantics_node.h +++ b/lib/ui/semantics/semantics_node.h @@ -13,6 +13,8 @@ #include "third_party/skia/include/core/SkM44.h" #include "third_party/skia/include/core/SkRect.h" +#include "flutter/lib/ui/semantics/string_attribute.h" + namespace flutter { // Must match the SemanticsAction enum in semantics.dart and in each of the @@ -39,14 +41,20 @@ enum class SemanticsAction : int32_t { kDismiss = 1 << 18, kMoveCursorForwardByWordIndex = 1 << 19, kMoveCursorBackwardByWordIndex = 1 << 20, + kSetText = 1 << 21, }; -const int kScrollableSemanticsActions = - static_cast(SemanticsAction::kScrollLeft) | - static_cast(SemanticsAction::kScrollRight) | +const int kVerticalScrollSemanticsActions = static_cast(SemanticsAction::kScrollUp) | static_cast(SemanticsAction::kScrollDown); +const int kHorizontalScrollSemanticsActions = + static_cast(SemanticsAction::kScrollLeft) | + static_cast(SemanticsAction::kScrollRight); + +const int kScrollableSemanticsActions = + kVerticalScrollSemanticsActions | kHorizontalScrollSemanticsActions; + /// C/C++ representation of `SemanticsFlags` defined in /// `lib/ui/semantics.dart`. ///\warning This must match the `SemanticsFlags` enum in @@ -79,6 +87,7 @@ enum class SemanticsFlags : int32_t { kIsFocusable = 1 << 21, kIsLink = 1 << 22, kIsSlider = 1 << 23, + kIsKeyboardKey = 1 << 24, }; const int kScrollableSemanticsFlags = @@ -113,14 +122,19 @@ struct SemanticsNode { double elevation = 0.0; double thickness = 0.0; std::string label; + StringAttributes labelAttributes; std::string hint; + StringAttributes hintAttributes; std::string value; + StringAttributes valueAttributes; std::string increasedValue; + StringAttributes increasedValueAttributes; std::string decreasedValue; + StringAttributes decreasedValueAttributes; int32_t textDirection = 0; // 0=unknown, 1=rtl, 2=ltr - SkRect rect = SkRect::MakeEmpty(); - SkM44 transform = SkM44{}; // Identity + SkRect rect = SkRect::MakeEmpty(); // Local space, relative to parent. + SkM44 transform = SkM44{}; // Identity std::vector childrenInTraversalOrder; std::vector childrenInHitTestOrder; std::vector customAccessibilityActions; diff --git a/lib/ui/semantics/semantics_update_builder.cc b/lib/ui/semantics/semantics_update_builder.cc index 3b92bc0c9d269..beae3a3b1e447 100644 --- a/lib/ui/semantics/semantics_update_builder.cc +++ b/lib/ui/semantics/semantics_update_builder.cc @@ -13,6 +13,14 @@ namespace flutter { +void pushStringAttributes( + StringAttributes& destination, + const std::vector& native_attributes) { + for (const auto& native_attribute : native_attributes) { + destination.push_back(native_attribute->GetAttribute()); + } +} + static void SemanticsUpdateBuilder_constructor(Dart_NativeArguments args) { UIDartState::ThrowIfUIOperationsProhibited(); DartCallConstructor(&SemanticsUpdateBuilder::create, args); @@ -59,10 +67,15 @@ void SemanticsUpdateBuilder::updateNode( double elevation, double thickness, std::string label, - std::string hint, + std::vector labelAttributes, std::string value, + std::vector valueAttributes, std::string increasedValue, + std::vector increasedValueAttributes, std::string decreasedValue, + std::vector decreasedValueAttributes, + std::string hint, + std::vector hintAttributes, int textDirection, const tonic::Float64List& transform, const tonic::Int32List& childrenInTraversalOrder, @@ -92,10 +105,15 @@ void SemanticsUpdateBuilder::updateNode( node.elevation = elevation; node.thickness = thickness; node.label = label; - node.hint = hint; + pushStringAttributes(node.labelAttributes, labelAttributes); node.value = value; + pushStringAttributes(node.valueAttributes, valueAttributes); node.increasedValue = increasedValue; + pushStringAttributes(node.increasedValueAttributes, increasedValueAttributes); node.decreasedValue = decreasedValue; + pushStringAttributes(node.decreasedValueAttributes, decreasedValueAttributes); + node.hint = hint; + pushStringAttributes(node.hintAttributes, hintAttributes); node.textDirection = textDirection; SkScalar scalarTransform[16]; for (int i = 0; i < 16; ++i) { diff --git a/lib/ui/semantics/semantics_update_builder.h b/lib/ui/semantics/semantics_update_builder.h index a183e1384b637..96c29a1bceaa3 100644 --- a/lib/ui/semantics/semantics_update_builder.h +++ b/lib/ui/semantics/semantics_update_builder.h @@ -5,6 +5,9 @@ #ifndef FLUTTER_LIB_UI_SEMANTICS_SEMANTICS_UPDATE_BUILDER_H_ #define FLUTTER_LIB_UI_SEMANTICS_SEMANTICS_UPDATE_BUILDER_H_ +#include +#include + #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/semantics/semantics_update.h" #include "third_party/tonic/typed_data/typed_list.h" @@ -43,10 +46,15 @@ class SemanticsUpdateBuilder double elevation, double thickness, std::string label, - std::string hint, + std::vector labelAttributes, std::string value, + std::vector valueAttributes, std::string increasedValue, + std::vector increasedValueAttributes, std::string decreasedValue, + std::vector decreasedValueAttributes, + std::string hint, + std::vector hintAttributes, int textDirection, const tonic::Float64List& transform, const tonic::Int32List& childrenInTraversalOrder, @@ -64,7 +72,6 @@ class SemanticsUpdateBuilder private: explicit SemanticsUpdateBuilder(); - SemanticsNodeUpdates nodes_; CustomAccessibilityActionUpdates actions_; }; diff --git a/lib/ui/semantics/semantics_update_builder_unittests.cc b/lib/ui/semantics/semantics_update_builder_unittests.cc new file mode 100644 index 0000000000000..fd89d345d9757 --- /dev/null +++ b/lib/ui/semantics/semantics_update_builder_unittests.cc @@ -0,0 +1,93 @@ +// 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/lib/ui/semantics/semantics_update_builder.h" +#include "flutter/shell/common/shell_test.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +using SemanticsUpdateBuilderTest = ShellTest; + +TEST_F(SemanticsUpdateBuilderTest, CanHandleAttributedStrings) { + auto message_latch = std::make_shared(); + + auto nativeSemanticsUpdate = [message_latch](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + intptr_t peer = 0; + Dart_Handle result = Dart_GetNativeInstanceField( + handle, tonic::DartWrappable::kPeerIndex, &peer); + ASSERT_FALSE(Dart_IsError(result)); + SemanticsUpdate* update = reinterpret_cast(peer); + SemanticsNodeUpdates nodes = update->takeNodes(); + ASSERT_EQ(nodes.size(), (size_t)1); + auto node = nodes.find(0)->second; + // Should match the updateNode in ui_test.dart. + ASSERT_EQ(node.label, "label"); + ASSERT_EQ(node.labelAttributes.size(), (size_t)1); + ASSERT_EQ(node.labelAttributes[0]->start, 1); + ASSERT_EQ(node.labelAttributes[0]->end, 2); + ASSERT_EQ(node.labelAttributes[0]->type, StringAttributeType::kSpellOut); + + ASSERT_EQ(node.value, "value"); + ASSERT_EQ(node.valueAttributes.size(), (size_t)1); + ASSERT_EQ(node.valueAttributes[0]->start, 2); + ASSERT_EQ(node.valueAttributes[0]->end, 3); + ASSERT_EQ(node.valueAttributes[0]->type, StringAttributeType::kSpellOut); + + ASSERT_EQ(node.hint, "hint"); + ASSERT_EQ(node.hintAttributes.size(), (size_t)1); + ASSERT_EQ(node.hintAttributes[0]->start, 0); + ASSERT_EQ(node.hintAttributes[0]->end, 1); + ASSERT_EQ(node.hintAttributes[0]->type, StringAttributeType::kLocale); + auto local_attribute = + std::static_pointer_cast(node.hintAttributes[0]); + ASSERT_EQ(local_attribute->locale, "en-MX"); + + ASSERT_EQ(node.increasedValue, "increasedValue"); + ASSERT_EQ(node.increasedValueAttributes.size(), (size_t)1); + ASSERT_EQ(node.increasedValueAttributes[0]->start, 4); + ASSERT_EQ(node.increasedValueAttributes[0]->end, 5); + ASSERT_EQ(node.increasedValueAttributes[0]->type, + StringAttributeType::kSpellOut); + + ASSERT_EQ(node.decreasedValue, "decreasedValue"); + ASSERT_EQ(node.decreasedValueAttributes.size(), (size_t)1); + ASSERT_EQ(node.decreasedValueAttributes[0]->start, 5); + ASSERT_EQ(node.decreasedValueAttributes[0]->end, 6); + ASSERT_EQ(node.decreasedValueAttributes[0]->type, + StringAttributeType::kSpellOut); + message_latch->Signal(); + }; + + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", // label + GetCurrentTaskRunner(), // platform + CreateNewThread(), // raster + CreateNewThread(), // ui + CreateNewThread() // io + ); + + AddNativeCallback("SemanticsUpdate", + CREATE_NATIVE_ENTRY(nativeSemanticsUpdate)); + + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + ASSERT_TRUE(shell->IsSetup()); + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("sendSemanticsUpdate"); + + shell->RunEngine(std::move(configuration), [](auto result) { + ASSERT_EQ(result, Engine::RunStatus::Success); + }); + + message_latch->Wait(); + DestroyShell(std::move(shell), std::move(task_runners)); +} + +} // namespace testing +} // namespace flutter diff --git a/lib/ui/semantics/string_attribute.cc b/lib/ui/semantics/string_attribute.cc new file mode 100644 index 0000000000000..f1eac9c998b11 --- /dev/null +++ b/lib/ui/semantics/string_attribute.cc @@ -0,0 +1,69 @@ +// 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/lib/ui/semantics/string_attribute.h" + +#include "flutter/fml/logging.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "third_party/tonic/dart_args.h" +#include "third_party/tonic/dart_binding_macros.h" + +#include + +namespace flutter { + +IMPLEMENT_WRAPPERTYPEINFO(ui, NativeStringAttribute); + +#define FOR_EACH_BINDING(V) \ + V(NativeStringAttribute, initLocaleStringAttribute) \ + V(NativeStringAttribute, initSpellOutStringAttribute) + +FOR_EACH_BINDING(DART_NATIVE_CALLBACK) + +void NativeStringAttribute::RegisterNatives( + tonic::DartLibraryNatives* natives) { + natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); +} + +NativeStringAttribute::NativeStringAttribute() {} + +NativeStringAttribute::~NativeStringAttribute() {} + +void NativeStringAttribute::initSpellOutStringAttribute( + Dart_Handle string_attribute_handle, + int32_t start, + int32_t end) { + UIDartState::ThrowIfUIOperationsProhibited(); + auto native_string_attribute = fml::MakeRefCounted(); + native_string_attribute->AssociateWithDartWrapper(string_attribute_handle); + + native_string_attribute->attribute_ = + std::make_shared(); + native_string_attribute->attribute_->start = start; + native_string_attribute->attribute_->end = end; + native_string_attribute->attribute_->type = StringAttributeType::kSpellOut; +} + +void NativeStringAttribute::initLocaleStringAttribute( + Dart_Handle string_attribute_handle, + int32_t start, + int32_t end, + std::string locale) { + UIDartState::ThrowIfUIOperationsProhibited(); + auto native_string_attribute = fml::MakeRefCounted(); + native_string_attribute->AssociateWithDartWrapper(string_attribute_handle); + + auto locale_attribute = std::make_shared(); + locale_attribute->start = start; + locale_attribute->end = end; + locale_attribute->type = StringAttributeType::kLocale; + locale_attribute->locale = locale; + native_string_attribute->attribute_ = locale_attribute; +} + +const StringAttributePtr NativeStringAttribute::GetAttribute() const { + return attribute_; +} + +} // namespace flutter diff --git a/lib/ui/semantics/string_attribute.h b/lib/ui/semantics/string_attribute.h new file mode 100644 index 0000000000000..5fbcf84c0e5fb --- /dev/null +++ b/lib/ui/semantics/string_attribute.h @@ -0,0 +1,87 @@ +// 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_LIB_UI_SEMANTICS_STRING_ATTRIBUTE_H_ +#define FLUTTER_LIB_UI_SEMANTICS_STRING_ATTRIBUTE_H_ + +#include "flutter/lib/ui/dart_wrapper.h" +#include "third_party/tonic/dart_library_natives.h" + +namespace flutter { + +struct StringAttribute; + +using StringAttributePtr = std::shared_ptr; +using StringAttributes = std::vector; + +// When adding a new StringAttributeType, the classes in these file must be +// updated as well. +// * engine/src/flutter/lib/ui/semantics.dart +// * engine/src/flutter/lib/web_ui/lib/src/ui/semantics.dart +// * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java + +enum class StringAttributeType : int32_t { + kSpellOut, + kLocale, +}; + +//------------------------------------------------------------------------------ +/// The c++ representation of the StringAttribute, this struct serves as an +/// abstract interface for the subclasses and should not be used directly. +struct StringAttribute { + virtual ~StringAttribute() = default; + int32_t start = -1; + int32_t end = -1; + StringAttributeType type; +}; + +//------------------------------------------------------------------------------ +/// Indicates the string needs to be spelled out character by character when the +/// assistive technologies announce the string. +struct SpellOutStringAttribute : StringAttribute {}; + +//------------------------------------------------------------------------------ +/// Indicates the string needs to be treated as a specific language when the +/// assistive technologies announce the string. +struct LocaleStringAttribute : StringAttribute { + std::string locale; +}; + +//------------------------------------------------------------------------------ +/// The peer class for all of the StringAttribute subclasses in semantics.dart. +class NativeStringAttribute + : public RefCountedDartWrappable { + DEFINE_WRAPPERTYPEINFO(); + FML_FRIEND_MAKE_REF_COUNTED(NativeStringAttribute); + + public: + ~NativeStringAttribute() override; + + //---------------------------------------------------------------------------- + /// The init method for SpellOutStringAttribute constructor + static void initSpellOutStringAttribute(Dart_Handle string_attribute_handle, + int32_t start, + int32_t end); + + //---------------------------------------------------------------------------- + /// The init method for LocaleStringAttribute constructor + static void initLocaleStringAttribute(Dart_Handle string_attribute_handle, + int32_t start, + int32_t end, + std::string locale); + + //---------------------------------------------------------------------------- + /// Returns the c++ representataion of StringAttribute. + const StringAttributePtr GetAttribute() const; + + static void RegisterNatives(tonic::DartLibraryNatives* natives); + + private: + NativeStringAttribute(); + StringAttributePtr attribute_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_SEMANTICS_STRING_ATTRIBUTE_H_ diff --git a/lib/ui/snapshot_delegate.h b/lib/ui/snapshot_delegate.h index ad9b8ef1f3612..a1f7c618b0653 100644 --- a/lib/ui/snapshot_delegate.h +++ b/lib/ui/snapshot_delegate.h @@ -12,6 +12,10 @@ namespace flutter { class SnapshotDelegate { public: + virtual sk_sp MakeRasterSnapshot( + std::function draw_callback, + SkISize picture_size) = 0; + virtual sk_sp MakeRasterSnapshot(sk_sp picture, SkISize picture_size) = 0; diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 8a03b68c68fa1..4dc0de493b8cb 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 - +// @dart = 2.12 part of dart.ui; /// Whether to slant the glyphs in the font @@ -82,7 +81,7 @@ class FontWeight { /// Values for `t` are usually obtained from an [Animation], such as /// an [AnimationController]. static FontWeight? lerp(FontWeight? a, FontWeight? b, double t) { - assert(t != null); // ignore: unnecessary_null_comparison + assert(t != null); if (a == null && b == null) return null; return values[_lerpInt((a ?? normal).index, (b ?? normal).index, t).round().clamp(0, 8)]; @@ -106,76 +105,90 @@ class FontWeight { /// A feature tag and value that affect the selection of glyphs in a font. /// -/// {@tool sample --template=freeform} +/// Different fonts support different features. Consider using a tool +/// such as to examine your fonts to +/// determine what features are available. /// -/// This example shows usage of several OpenType font features, including -/// Small Caps (smcp), old-style figures, fractional ligatures and stylistic -/// sets. +/// {@tool sample --template=stateless_widget_material} /// -/// ```dart -///class TypePage extends StatelessWidget { -/// // The Cardo, Milonga and Raleway Dots fonts can be downloaded from -/// // Google Fonts (https://www.google.com/fonts). +/// This example shows usage of several OpenType font features, +/// including Small Caps (selected manually using the "smcp" code), +/// old-style figures, fractional ligatures, and stylistic sets. +/// +/// ```dart dartImports +/// import 'dart:ui'; +/// ``` /// -/// final titleStyle = TextStyle( -/// fontSize: 18, -/// fontFeatures: [FontFeature.enable('smcp')], -/// color: Colors.blueGrey[600], -/// ); +/// ```dart preamble +/// final TextStyle titleStyle = TextStyle( +/// fontSize: 18, +/// fontFeatures: const [FontFeature.enable('smcp')], +/// color: Colors.blueGrey[600], +/// ); +/// ``` /// -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: Center( -/// child: Column( -/// mainAxisAlignment: MainAxisAlignment.center, -/// children: [ -/// Spacer(flex: 5), -/// Text('regular numbers have their place:', style: titleStyle), -/// Text('The 1972 cup final was a 1-1 draw.', -/// style: TextStyle( -/// fontFamily: 'Cardo', -/// fontSize: 24, -/// )), -/// Spacer(), -/// Text('but old-style figures blend well with lower case:', -/// style: titleStyle), -/// Text('The 1972 cup final was a 1-1 draw.', -/// style: TextStyle( -/// fontFamily: 'Cardo', -/// fontSize: 24, -/// fontFeatures: [FontFeature.oldstyleFigures()])), -/// Spacer(), -/// Divider(), -/// Spacer(), -/// Text('fractions look better with a custom ligature:', -/// style: titleStyle), -/// Text('Add 1/2 tsp of flour and stir.', -/// style: TextStyle( -/// fontFamily: 'Milonga', -/// fontSize: 24, -/// fontFeatures: [FontFeature.enable('frac')])), -/// Spacer(), -/// Divider(), -/// Spacer(), -/// Text('multiple stylistic sets in one font:', style: titleStyle), -/// Text('Raleway Dots', -/// style: TextStyle(fontFamily: 'Raleway Dots', fontSize: 48)), -/// Text('Raleway Dots', -/// style: TextStyle( -/// fontFeatures: [FontFeature.stylisticSet(1)], -/// fontFamily: 'Raleway Dots', -/// fontSize: 48, -/// )), -/// Spacer(flex: 5), -/// ], -/// ), -/// ), -/// ); -/// } -///} +/// ```dart +/// Widget build(BuildContext context) { +/// // The Cardo, Milonga and Raleway Dots fonts can be downloaded from +/// // Google Fonts (https://www.google.com/fonts). +/// return Scaffold( +/// body: Center( +/// child: Column( +/// mainAxisAlignment: MainAxisAlignment.center, +/// children: [ +/// const Spacer(flex: 5), +/// Text('regular numbers have their place:', style: titleStyle), +/// const Text('The 1972 cup final was a 1-1 draw.', +/// style: TextStyle( +/// fontFamily: 'Cardo', +/// fontSize: 24, +/// )), +/// const Spacer(), +/// Text('but old-style figures blend well with lower case:', +/// style: titleStyle), +/// const Text('The 1972 cup final was a 1-1 draw.', +/// style: TextStyle( +/// fontFamily: 'Cardo', +/// fontSize: 24, +/// fontFeatures: [FontFeature.oldstyleFigures()])), +/// const Spacer(), +/// const Divider(), +/// const Spacer(), +/// Text('fractions look better with a custom ligature:', +/// style: titleStyle), +/// const Text('Add 1/2 tsp of flour and stir.', +/// style: TextStyle( +/// fontFamily: 'Milonga', +/// fontSize: 24, +/// fontFeatures: [FontFeature.alternativeFractions()])), +/// const Spacer(), +/// const Divider(), +/// const Spacer(), +/// Text('multiple stylistic sets in one font:', style: titleStyle), +/// const Text('Raleway Dots', +/// style: TextStyle(fontFamily: 'Raleway Dots', fontSize: 48)), +/// Text('Raleway Dots', +/// style: TextStyle( +/// fontFeatures: [FontFeature.stylisticSet(1)], +/// fontFamily: 'Raleway Dots', +/// fontSize: 48, +/// )), +/// const Spacer(flex: 5), +/// ], +/// ), +/// ), +/// ); +/// } /// ``` /// {@end-tool} +/// +/// See also: +/// +/// * , +/// Wikipedia's description of these typographic features. +/// +/// * , +/// Microsoft's registry of these features. class FontFeature { /// Creates a [FontFeature] object, which can be added to a [TextStyle] to /// change how the engine selects glyphs when rendering text. @@ -183,13 +196,18 @@ class FontFeature { /// `feature` is the four-character tag that identifies the feature. /// These tags are specified by font formats such as OpenType. /// - /// `value` is the value that the feature will be set to. The behavior - /// of the value depends on the specific feature. Many features are + /// `value` is the value that the feature will be set to. The behavior + /// of the value depends on the specific feature. Many features are /// flags whose value can be 1 (when enabled) or 0 (when disabled). /// /// See - // ignore: unnecessary_null_comparison - const FontFeature(this.feature, [ this.value = 1 ]) : assert(feature != null), assert(feature.length == 4), assert(value != null), assert(value >= 0); + const FontFeature( + this.feature, + [ this.value = 1 ] + ) : assert(feature != null), + assert(feature.length == 4, 'Feature tag must be exactly four characters long.'), + assert(value != null), + assert(value >= 0, 'Feature value must be zero or a positive integer.'); /// Create a [FontFeature] object that enables the feature with the given tag. const FontFeature.enable(String feature) : this(feature, 1); @@ -197,57 +215,748 @@ class FontFeature { /// Create a [FontFeature] object that disables the feature with the given tag. const FontFeature.disable(String feature) : this(feature, 0); - /// Randomize the alternate forms used in text. - /// - /// For example, this can be used with suitably-prepared handwriting fonts to - /// vary the forms used for each character, so that, for instance, the word - /// "cross-section" would be rendered with two different "c"s, two different "o"s, - /// and three different "s"s. + // Features below should be alphabetic by feature tag. This makes it + // easier to determine when a feature is missing so that we avoid + // adding duplicates. + // + // The full list is extremely long, and many of the features are + // language-specific, or indeed force-enabled for particular locales + // by HarfBuzz, so we don't even attempt to be comprehensive here. + // Features listed below are those we deemed "interesting enough" to + // have their own constructor, mostly on the basis of whether we + // could find a font where the feature had a useful effect that + // could be demonstrated. + + // Start of feature tag list. + // ------------------------------------------------------------------------ + + // aalt + /// Access alternative glyphs. (`aalt`) + /// + /// This feature selects the given glyph variant for glyphs in the span. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Raleway font supports several alternate glyphs. The code + /// below shows how specific glyphs can be selected. With `aalt` set + /// to zero, the default, the normal glyphs are used. With a + /// non-zero value, Raleway substitutes small caps for lower case + /// letters. With value 2, the lowercase "a" changes to a stemless + /// "a", whereas the lowercase "t" changes to a vertical bar instead + /// of having a curve. By targeting specific letters in the text + /// (using [Text.rich]), the desired rendering for each glyph can be + /// achieved. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Raleway font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'The infamous Tuna Torture.', + /// style: TextStyle( + /// fontFamily: 'Raleway', + /// fontFeatures: [ + /// FontFeature.alternative(1), // or 2, or 3, or... + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_aalt.png) + /// {@end-tool} /// /// See also: /// - /// * - const FontFeature.randomize() : feature = 'rand', value = 1; + /// * + const FontFeature.alternative(this.value) : feature = 'aalt'; - /// Select a stylistic set. + // afrc + /// Use alternative ligatures to represent fractions. (`afrc`) + /// + /// When this feature is enabled (and the font supports it), + /// sequences of digits separated by U+002F SOLIDUS character (/) or + /// U+2044 FRACTION SLASH (⁄) are replaced by ligatures that + /// represent the corresponding fraction. These ligatures may differ + /// from those used by the [FontFeature.fractions] feature. + /// + /// This feature overrides all other features. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Ubuntu Mono font supports the `afrc` feature. It causes digits + /// before slashes to become superscripted and digits after slashes to become + /// subscripted. This contrasts to the effect seen with [FontFeature.fractions]. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Ubuntu Mono font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Fractions: 1/2 2/3 3/4 4/5', + /// style: TextStyle( + /// fontFamily: 'Ubuntu Mono', + /// fontFeatures: [ + /// FontFeature.alternativeFractions(), + /// ], + /// ), + /// ); + /// } + /// ``` /// - /// Fonts may have up to 20 stylistic sets, numbered 1 through 20. + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_afrc.png) + /// {@end-tool} /// /// See also: /// - /// * - factory FontFeature.stylisticSet(int value) { + /// * [FontFeature.fractions], which has a similar (but different) effect. + /// * + const FontFeature.alternativeFractions() : feature = 'afrc', value = 1; + + // calt + /// Enable contextual alternates. (`calt`) + /// + /// With this feature enabled, specific glyphs may be replaced by + /// alternatives based on nearby text. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Barriecito font supports the `calt` feature. It causes some + /// letters in close proximity to other instances of themselves to + /// use different glyphs, to give the appearance of more variation + /// in the glyphs, rather than having each letter always use a + /// particular glyph. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Barriecito font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// "Ooohh, we weren't going to tell him that.", + /// style: TextStyle( + /// fontFamily: 'Barriecito', + /// fontFeatures: [ + /// FontFeature.contextualAlternates(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_calt.png) + /// {@end-tool} + /// + /// See also: + /// + /// * [FontFeature.randomize], which is more a rarely supported but more + /// powerful way to get a similar effect. + /// * + const FontFeature.contextualAlternates() : feature = 'calt', value = 1; + + // case + /// Enable case-sensitive forms. (`case`) + /// + /// Some glyphs, for example parentheses or operators, are typically + /// designed to fit nicely with mixed case, or even predominantly + /// lowercase, text. When these glyphs are placed near strings of + /// capital letters, they appear a little off-center. + /// + /// This feature, when supported by the font, causes these glyphs to + /// be shifted slightly, or otherwise adjusted, so as to form a more + /// aethestically pleasing combination with capital letters. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `case` feature. It causes + /// parentheses, brackets, braces, guillemets, slashes, bullets, and + /// some other glyphs (not shown below) to be shifted up slightly so + /// that capital letters appear centered in comparison. When the + /// feature is disabled, those glyphs are optimized for use with + /// lowercase letters, and so capital letters appear to ride higher + /// relative to the punctuation marks. + /// + /// The difference is very subtle. It may be most obvious when + /// examining the square brackets compared to the capital A. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// '(A) [A] {A} «A» A/B A•B', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.caseSensitiveForms(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_case.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.caseSensitiveForms() : feature = 'case', value = 1; + + // cvXX + /// Select a character variant. (`cv01` through `cv99`) + /// + /// Fonts may have up to 99 character variant sets, numbered 1 + /// through 99, each of which can be independently enabled or + /// disabled. + /// + /// Related character variants are typically grouped into stylistic + /// sets, controlled by the [FontFeature.stylisticSet] feature + /// (`ssXX`). + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Source Code Pro font supports the `cvXX` feature for several + /// characters. In the example below, variants 1 (`cv01`), 2 + /// (`cv02`), and 4 (`cv04`) are selected. Variant 1 changes the + /// rendering of the "a" character, variant 2 changes the lowercase + /// "g" character, and variant 4 changes the lowercase "i" and "l" + /// characters. There are also variants (not shown here) that + /// control the rendering of various greek characters such as beta + /// and theta. + /// + /// Notably, this can be contrasted with the stylistic sets, where + /// the set which affects the "a" character also affects beta, and + /// the set which affects the "g" character also affects theta and + /// delta. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Source Code Pro font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return Text( + /// 'aáâ β gǵĝ θб Iiíî Ll', + /// style: TextStyle( + /// fontFamily: 'Source Code Pro', + /// fontFeatures: [ + /// FontFeature.characterVariant(1), + /// FontFeature.characterVariant(2), + /// FontFeature.characterVariant(4), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_cvXX.png) + /// {@end-tool} + /// + /// See also: + /// + /// * [FontFeature.stylisticSet], which allows for groups of characters + /// variants to be selected at once, as opposed to individual character variants. + /// * + factory FontFeature.characterVariant(int value) { assert(value >= 1); - assert(value <= 20); - return FontFeature('ss${value.toString().padLeft(2, "0")}'); + assert(value <= 99); + return FontFeature('cv${value.toString().padLeft(2, "0")}'); } - /// Use the slashed zero. + // dnom + /// Display digits as denominators. (`dnom`) /// - /// Some fonts contain both a circular zero and a zero with a slash. This - /// enables the use of the latter form. + /// This is typically used automatically by the font rendering + /// system as part of the implementation of `frac` for the denominator + /// part of fractions (see [FontFeature.fractions]). /// - /// This is overridden by [FontFeature.oldstyleFigures]. + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `dnom` feature. It causes + /// the digits to be rendered smaller and near the bottom of the EM box. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Fractions: 1/2 2/3 3/4 4/5', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.denominator(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_dnom.png) + /// {@end-tool} /// /// See also: /// - /// * - const FontFeature.slashedZero() : feature = 'zero', value = 1; + /// * + const FontFeature.denominator() : feature = 'dnom', value = 1; + + // frac + /// Use ligatures to represent fractions. (`afrc`) + /// + /// When this feature is enabled (and the font supports it), + /// sequences of digits separated by U+002F SOLIDUS character (/) or + /// U+2044 FRACTION SLASH (⁄) are replaced by ligatures that + /// represent the corresponding fraction. + /// + /// This feature may imply the [FontFeature.numerator] and + /// [FontFeature.denominator] features. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Ubuntu Mono font supports the `frac` feature. It causes + /// digits around slashes to be turned into dedicated fraction + /// glpyhs. This contrasts to the effect seen with + /// [FontFeature.alternativeFractions]. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Ubuntu Mono font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Fractions: 1/2 2/3 3/4 4/5', + /// style: TextStyle( + /// fontFamily: 'Ubuntu Mono', + /// fontFeatures: [ + /// FontFeature.fractions(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_frac.png) + /// {@end-tool} + /// + /// See also: + /// + /// * [FontFeature.alternativeFractions], which has a similar (but different) effect. + /// * + const FontFeature.fractions() : feature = 'frac', value = 1; - /// Use oldstyle figures. + // hist + /// Use historical forms. (`hist`) + /// + /// Some fonts have alteratives for letters whose forms have changed + /// through the ages. In the Latin alphabet, this is common for + /// example with the long-form "s" or the Fraktur "k". This feature enables + /// those alternative glyphs. + /// + /// This does not enable legacy ligatures, only single-character alternatives. + /// To enable historical ligatures, use [FontFeature.historicalLigatures]. + /// + /// This feature may override other glyph-substitution features. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Cardo font supports the `hist` feature specifically for the + /// letter "s": it changes occurrences of that letter for the glyph + /// used by U+017F LATIN SMALL LETTER LONG S. /// - /// Some fonts have variants of the figures (e.g. the digit 9) that, when - /// this feature is enabled, render with descenders under the baseline instead - /// of being entirely above the baseline. + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Cardo font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'VIBRANT fish assisted his business.', + /// style: TextStyle( + /// fontFamily: 'Sorts Mill Goudy', + /// fontFeatures: [ + /// FontFeature.historicalForms(), // Enables "hist". + /// // Use FontFeature.historicalLigatures() to enable "hlig" as well. + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_historical.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.historicalForms() : feature = 'hist', value = 1; + + // hlig + /// Use historical ligatures. (`hlig`) + /// + /// Some fonts support ligatures that have fallen out of favor today, + /// but were historically in common use. This feature enables those + /// ligatures. + /// + /// For example, the "long s" glyph was historically typeset with + /// characters such as "t" and "h" as a single ligature. + /// + /// This does not enable the legacy forms, only ligatures. See + /// [FontFeature.historicalForms] to enable single characters to be + /// replaced with their historical alternatives. Combining both is + /// usually desired since the ligatures typically apply specifically + /// to characters that have historical forms as well. For example, + /// the historical forms feature might replace the "s" character + /// with the "long s" (ſ) character, while the historical ligatures + /// feature might specifically apply to cases where "long s" is + /// followed by other characters such as "t". In such cases, without + /// the historical forms being enabled, the ligatures would only + /// apply when the "long s" is used explicitly. + /// + /// This feature may override other glyph-substitution features. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Cardo font supports the `hlig` feature. It has legacy + /// ligatures for "VI" and "NT", and various ligatures involving the + /// "long s". In the example below, both historical forms (`hist 1`) + /// and historical ligatures (`hlig 1`) are enabled, so, for + /// instance, "fish" becomes "fiſh" which is then rendered using a + /// ligature for the last two characters. + /// + /// Similarly, the word "business" is turned into "buſineſſ" by + /// `hist`, and the `ſi` and `ſſ` pairs are ligated by `hlig`. + /// Observe in particular the position of the dot of the "i" in + /// "business" in the various combinations of these features. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Cardo font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'VIBRANT fish assisted his business.', + /// style: TextStyle( + /// fontFamily: 'Sorts Mill Goudy', + /// fontFeatures: [ + /// FontFeature.historicalForms(), // Enables "hist". + /// FontFeature.historicalLigatures() // Enables "hlig". + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_historical.png) + /// {@end-tool} + /// + /// See also: /// - /// This overrides [FontFeature.slashedZero]. + /// * + const FontFeature.historicalLigatures() : feature = 'hlig', value = 1; + + // lnum + /// Use lining figures. (`lnum`) + /// + /// Some fonts have digits that, like lowercase latin letters, have + /// both descenders and ascenders. In some situations, especially in + /// conjunction with capital letters, this leads to an aesthetically + /// questionable irregularity. Lining figures, on the other hand, + /// have a uniform height, and align with the baseline and the + /// height of capital letters. Conceptually, they can be thought of + /// as "capital digits". + /// + /// This feature may conflict with [FontFeature.oldstyleFigures]. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Sorts Mill Goudy font supports the `lnum` feature. It causes + /// digits to fit more seamlessly with capital letters. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Sorts Mill Goudy font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'CALL 311-555-2368 NOW!', + /// style: TextStyle( + /// fontFamily: 'Sorts Mill Goudy', + /// fontFeatures: [ + /// FontFeature.liningFigures(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_lnum.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.liningFigures() : feature = 'lnum', value = 1; + + // locl + /// Use locale-specific glyphs. (`locl`) + /// + /// Some characters, most notably those in the Unicode Han + /// Unification blocks, vary in presentation based on the locale in + /// use. For example, the ideograph for "grass" (U+8349, 草) has a + /// broken top line in Traditional Chinese, but a solid top line in + /// Simplified Chinese, Japanese, Korean, and Vietnamese. This kind + /// of variation also exists with other alphabets, for example + /// Cyrilic characters as used in the Bulgarian and Serbian + /// alphabets vary from their Russian counterparts. + /// + /// A particular font may default to the forms for the locale for + /// which it was constructed, but still support alternative forms + /// for other locales. When this feature is enabled, the locale (as + /// specified using [painting.TextStyle.locale], for instance) is + /// used to determine which glyphs to use when locale-specific + /// alternatives exist. Disabling this feature causes the font + /// rendering to ignore locale information and only use the default + /// glyphs. + /// + /// This feature is enabled by default. Using + /// `FontFeature.localeAware(enable: false)` disables the + /// locale-awareness. (So does not specifying the locale in the + /// first place, of course.) + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Noto Sans CJK font supports the `locl` feature for CJK characters. + /// In this example, the `localeAware` feature is not explicitly used, as it is + /// enabled by default. This example instead shows how to set the locale, + /// thus demonstrating how Noto Sans adapts the glyph shapes to the locale. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Noto family of fonts can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// '次 化 刃 直 入 令', + /// locale: Locale('zh', 'CN'), // or Locale('ja'), Locale('ko'), Locale('zh', 'TW'), etc + /// style: TextStyle( + /// fontFamily: 'Noto Sans', + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_locl.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + /// * + /// * + const FontFeature.localeAware({ bool enable = true }) : feature = 'locl', value = enable ? 1 : 0; + + // nalt + /// Display alternative glyphs for numerals (alternate annotation forms). (`nalt`) + /// + /// Replaces glyphs used in numbering lists (e.g. 1, 2, 3...; or a, b, c...) with notational + /// variants that might be more typographically interesting. + /// + /// Fonts sometimes support multiple alternatives, and the argument + /// selects the set to use (a positive integer, or 0 to disable the + /// feature). The default set if none is specified is 1. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Gothic A1 font supports several notational variant sets via + /// the `nalt` feature. + /// + /// Set 1 changes the spacing of the glyphs. Set 2 parenthesizes the + /// latin letters and reduces the numerals to subscripts. Set 3 + /// circles the glyphs. Set 4 parenthesizes the digits. Set 5 uses + /// reverse-video circles for the digits. Set 7 superscripts the + /// digits. + /// + /// The code below shows how to select set 3. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Gothic A1 font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'abc 123', + /// style: TextStyle( + /// fontFamily: 'Gothic A1', + /// fontFeatures: [ + /// FontFeature.notationalForms(3), // circled letters and digits + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_nalt.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.notationalForms([this.value = 1]) : feature = 'nalt', assert(value >= 0); + + // numr + /// Display digits as numerators. (`numr`) + /// + /// This is typically used automatically by the font rendering + /// system as part of the implementation of `frac` for the numerator + /// part of fractions (see [FontFeature.fractions]). + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `numr` feature. It causes + /// the digits to be rendered smaller and near the top of the EM box. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Fractions: 1/2 2/3 3/4 4/5', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.numerators(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_numr.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.numerators() : feature = 'numr', value = 1; + + // onum + /// Use oldstyle figures. (`onum`) + /// + /// Some fonts have variants of the figures (e.g. the digit 9) that, + /// when this feature is enabled, render with descenders under the + /// baseline instead of being entirely above the baseline. If the + /// default digits are lining figures, this allows the selection of + /// digits that fit better with mixed case (uppercase and lowercase) + /// text. + /// + /// This overrides [FontFeature.slashedZero] and may conflict with + /// [FontFeature.liningFigures]. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `onum` feature. It causes + /// digits to extend below the baseline. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Call 311-555-2368 now!', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.oldstyleFigures(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_onum.png) + /// {@end-tool} /// /// See also: /// /// * + /// * const FontFeature.oldstyleFigures() : feature = 'onum', value = 1; - /// Use proportional (varying width) figures. + // ordn + /// Use ordinal forms for alphabetic glyphs. (`ordn`) + /// + /// Some fonts have variants of the alphabetic glyphs intended for + /// use after numbers when expressing ordinals, as in "1st", "2nd", + /// "3rd". This feature enables those alternative glyphs. + /// + /// This may override other features that substitute glyphs. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `ordn` feature. It causes + /// alphabetic glyphs to become smaller and superscripted. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// '1st, 2nd, 3rd, 4th...', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.ordinalForms(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_ordn.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.ordinalForms() : feature = 'ordn', value = 1; + + // pnum + /// Use proportional (varying width) figures. (`pnum`) /// /// For fonts that have both proportional and tabular (monospace) figures, /// this enables the proportional figures. @@ -256,35 +965,491 @@ class FontFeature { /// /// The default behavior varies from font to font. /// + /// {@tool sample --template=stateless_widget} + /// + /// The Kufam font supports the `pnum` feature. It causes the digits + /// to become proportionally-sized, rather than all being the same + /// width. In this font this is especially noticeable with the digit + /// "1": normally, the 1 has very noticeable serifs in this + /// sans-serif font, but with the proportionally figures enabled, + /// the digit becomes much narrower. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Kufam font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Call 311-555-2368 now!', + /// style: TextStyle( + /// fontFamily: 'Kufam', + /// fontFeatures: [ + /// FontFeature.proportionalFigures(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_pnum.png) + /// {@end-tool} + /// /// See also: /// /// * const FontFeature.proportionalFigures() : feature = 'pnum', value = 1; - /// Use tabular (monospace) figures. + // rand + /// Randomize the alternate forms used in text. (`rand`) + /// + /// For example, this can be used with suitably-prepared handwriting fonts to + /// vary the forms used for each character, so that, for instance, the word + /// "cross-section" would be rendered with two different "c"s, two different "o"s, + /// and three different "s"s. + /// + /// Contextual alternates ([FontFeature.contextualAlternates]) + /// provide a similar effect in some fonts, without using + /// randomness. + /// + /// See also: + /// + /// * + const FontFeature.randomize() : feature = 'rand', value = 1; + + // salt + /// Enable stylistic alternates. (`salt`) + /// + /// Some fonts have alternative forms that are not tied to a + /// particular purpose (such as being historical forms, or + /// contextually relevant alternatives, or ligatures, etc). This + /// font feature enables these purely stylistic alternatives. + /// + /// This may override other features that substitute glyphs. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Source Code Pro font supports the `salt` feature. It causes + /// some glyphs to be rendered differently, for example the "a" and + /// "g" glyphs change from their typographically common + /// double-storey forms to simpler single-storey forms, the dollar + /// sign's line changes from discontinuous to continuous (and is + /// angled), and the "0" rendering changes from a center dot to a + /// slash. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Source Code Pro font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// r'Agile Game - $100 initial bet', + /// style: TextStyle( + /// fontFamily: 'Source Code Pro', + /// fontFeatures: [ + /// FontFeature.stylisticAlternates(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_salt.png) + /// {@end-tool} + /// + /// See also: + /// + /// * [FontFeature.contextualAlternates], which is enables alternates specific to certain contexts. + /// * + const FontFeature.stylisticAlternates() : feature = 'salt', value = 1; + + // sinf + /// Use scientific inferiors. (`sinf`) + /// + /// Some fonts have variants of the figures (e.g. the digit 2) that, + /// when this feature is enabled, render in a manner more + /// appropriate for subscripted digits ("inferiors") used in + /// scientific contexts, e.g. the subscripts in chemical formulae. + /// + /// This may override other features that substitute glyphs. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `sinf` feature. It causes + /// digits to be smaller and subscripted. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'C8H10N4O2', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.scientificInferiors(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_sinf.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.scientificInferiors() : feature = 'sinf', value = 1; + + // ssXX + /// Select a stylistic set. (`ss01` through `ss20`) + /// + /// Fonts may have up to 20 stylistic sets, numbered 1 through 20, + /// each of which can be independently enabled or disabled. + /// + /// For more fine-grained control, in some fonts individual + /// character variants can also be controlled by the + /// [FontFeature.characterVariant] feature (`cvXX`). + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Source Code Pro font supports the `ssXX` feature for several + /// sets. In the example below, stylistic sets 2 (`ss02`), 3 + /// (`ss03`), and 4 (`ss04`) are selected. Stylistic set 2 changes + /// the rendering of the "a" character and the beta character, + /// stylistic set 3 changes the lowercase "g", theta, and delta + /// characters, and stylistic set 4 changes the lowercase "i" and + /// "l" characters. + /// + /// This font also supports character variants (see + /// [FontFeature.characterVariant]). + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Source Code Pro font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return Text( + /// 'aáâ β gǵĝ θб Iiíî Ll', + /// style: TextStyle( + /// fontFamily: 'Source Code Pro', + /// fontFeatures: [ + /// FontFeature.stylisticSet(2), + /// FontFeature.stylisticSet(3), + /// FontFeature.stylisticSet(4), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_ssXX_1.png) + /// {@end-tool} + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `ssXX` feature for for more + /// elaborate stylistic effects. Set 1 turns some Latin characters + /// into Roman numerals, set 2 enables some ASCII characters to be + /// used to create pretty arrows, and so forth. + /// + /// _These_ stylistic sets do _not_ correspond to character variants. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return Text( + /// '-> MCMXCVII <-', // 1997 + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.stylisticSet(1), + /// FontFeature.stylisticSet(2), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_ssXX_2.png) + /// {@end-tool} + /// + /// See also: + /// + /// * [FontFeature.characterVariant], which allows for individual character + /// variants to be selected, as opposed to entire sets. + /// * + factory FontFeature.stylisticSet(int value) { + assert(value >= 1); + assert(value <= 20); + return FontFeature('ss${value.toString().padLeft(2, "0")}'); + } + + // subs + /// Enable subscripts. (`subs`) + /// + /// This feature causes some fonts to change some glyphs to their subscripted form. + /// + /// It typically does not affect all glyphs, and so is not appropriate for generally causing + /// all text to be subscripted. + /// + /// This may override other features that substitute glyphs. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `subs` feature. It causes + /// digits to be smaller and subscripted. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Line from x1,y1 to x2,y2', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.subscripts(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_subs.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + /// * [FontFeature.scientificInferiors], which is similar but intended specifically for + /// subscripts used in scientific contexts. + /// * [FontFeature.superscripts], which is similar but for subscripting. + const FontFeature.subscripts() : feature = 'subs', value = 1; + + // sups + /// Enable superscripts. (`sups`) + /// + /// This feature causes some fonts to change some glyphs to their + /// superscripted form. This may be more than just changing their + /// position. For example, digits might change to lining figures + /// (see [FontFeature.liningFigures]) in addition to being raised + /// and shrunk. + /// + /// It typically does not affect all glyphs, and so is not + /// appropriate for generally causing all text to be superscripted. + /// + /// This may override other features that substitute glyphs. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Sorts Mill Goudy font supports the `sups` feature. It causes + /// digits to be smaller, superscripted, and changes them to lining + /// figures (so they are all the same height). + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Sorts Mill Goudy font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'The isotope 238U decays to 206Pb', + /// style: TextStyle( + /// fontFamily: 'Sorts Mill Goudy', + /// fontFeatures: [ + /// FontFeature.superscripts(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_sups.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + /// * [FontFeature.subscripts], which is similar but for subscripting. + const FontFeature.superscripts() : feature = 'sups', value = 1; + + // swsh + /// Enable swash glyphs. (`swsh`) + /// + /// Some fonts have beautiful flourishes on some characters. These + /// come in many forms, such as exaggerated serifs, long tails, long + /// entry strokes, or other forms of decorative extensions to the + /// base character. + /// + /// This feature enables the rendering of these flourishes. Some + /// fonts have many swashes per character; the argument, if + /// specified, selects which swash to use (0 disables them + /// altogether). + /// + /// Some fonts have an absurd number of alternative swashes. For + /// example, Adobe's Poetica famously has 63 different ampersand + /// forms available through this feature! + /// + /// {@tool sample --template=stateless_widget} + /// + /// The BioRhyme Expanded font supports the `swsh` feature specifically + /// for the capital "Q" and "R" glyphs and the ampersand. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The BioRhyme Expanded font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Queer & Romantic', + /// style: TextStyle( + /// fontFamily: 'BioRhyme Expanded', + /// fontFeatures: [ + /// FontFeature.swash(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_swsh.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + /// * + const FontFeature.swash([this.value = 1]) : feature = 'swsh', assert(value >= 0); + + // tnum + /// Use tabular (monospace) figures. (`tnum`) /// /// For fonts that have both proportional (varying width) and tabular figures, - /// this enables the tabular figures. + /// this enables the tabular figures. Tabular figures are monospaced (all the + /// same width), so that they align in tables of figures. /// /// This is mutually exclusive with [FontFeature.proportionalFigures]. /// /// The default behavior varies from font to font. /// + /// {@tool sample --template=stateless_widget} + /// + /// The Piazzolla font supports the `tnum` feature. It causes the + /// digits to become uniformally-sized, rather than having variable + /// widths. In this font this is especially noticeable with the + /// digit "1"; with tabular figures enabled, the "1" digit is more + /// widely spaced. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Piazzolla font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'Call 311-555-2368 now!', + /// style: TextStyle( + /// fontFamily: 'Piazzolla', + /// fontFeatures: [ + /// FontFeature.tabularFigures(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_tnum.png) + /// {@end-tool} + /// /// See also: /// /// * const FontFeature.tabularFigures() : feature = 'tnum', value = 1; + // zero + /// Use the slashed zero. (`zero`) + /// + /// Some fonts contain both a circular zero and a zero with a slash. This + /// enables the use of the latter form. + /// + /// This is overridden by [FontFeature.oldstyleFigures]. + /// + /// {@tool sample --template=stateless_widget} + /// + /// The Source Code Pro font supports the `zero` feature. It causes the + /// zero digit to be drawn with a slash rather than the default rendering, + /// which in this case has a dot through the zero rather than a slash. + /// + /// ```dart dartImports + /// import 'dart:ui'; + /// ``` + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // The Source Code Pro font can be downloaded from Google Fonts (https://www.google.com/fonts). + /// return const Text( + /// 'One million is: 1,000,000.00', + /// style: TextStyle( + /// fontFamily: 'Source Code Pro', + /// fontFeatures: [ + /// FontFeature.slashedZero(), + /// ], + /// ), + /// ); + /// } + /// ``` + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/font_feature_zero.png) + /// {@end-tool} + /// + /// See also: + /// + /// * + const FontFeature.slashedZero() : feature = 'zero', value = 1; + + // ------------------------------------------------------------------------ + // End of feature tags list. + /// The tag that identifies the effect of this feature. Must consist of 4 /// ASCII characters (typically lowercase letters). /// - /// See + /// These features are defined in a registry maintained by Microsoft: + /// final String feature; /// The value assigned to this feature. /// - /// Must be a positive integer. Many features are Boolean values that accept - /// values of either 0 (feature is disabled) or 1 (feature is enabled). + /// Must be a positive integer. Many features are Boolean values that accept + /// values of either 0 (feature is disabled) or 1 (feature is enabled). Other + /// features have a bound range of values (which may be documented in these + /// API docs for features that have dedicated constructors, and are generally + /// documented in the official registry). In some cases the precise supported + /// range depends on the font. + /// + /// See also: + /// + /// * final int value; static const int _kEncodedSize = 8; @@ -299,8 +1464,6 @@ class FontFeature { @override bool operator ==(Object other) { - if (identical(this, other)) - return true; if (other.runtimeType != runtimeType) return false; return other is FontFeature @@ -312,7 +1475,7 @@ class FontFeature { int get hashCode => hashValues(feature, value); @override - String toString() => 'FontFeature($feature, $value)'; + String toString() => "FontFeature('$feature', $value)"; } /// Whether and how to align text horizontally. @@ -364,7 +1527,7 @@ class TextDecoration { /// Creates a decoration that paints the union of all the given decorations. factory TextDecoration.combine(List decorations) { int mask = 0; - for (TextDecoration decoration in decorations) + for (final TextDecoration decoration in decorations) mask |= decoration._mask; return TextDecoration._(mask); } @@ -432,14 +1595,46 @@ enum TextDecorationStyle { wavy } -/// {@template flutter.dart:ui.textHeightBehavior} -/// Defines how the paragraph will apply [TextStyle.height] to the ascent of the -/// first line and descent of the last line. +/// {@macro dart.ui.textLeadingDistribution} +enum TextLeadingDistribution { + /// Distributes the [leading](https://en.wikipedia.org/wiki/Leading) + /// of the text proportionally above and below the text, to the font's + /// ascent/discent ratio. + /// + /// {@template dart.ui.leading} + /// The leading of a text run is defined as + /// `TextStyle.height * TextStyle.fontSize - TextStyle.fontSize`. When + /// [TextStyle.height] is not set, the text run uses the leading specified by + /// the font instead. + /// {@endtemplate} + proportional, + + /// Distributes the ["leading"](https://en.wikipedia.org/wiki/Leading) + /// of the text evenly above and below the text (i.e. evenly above the + /// font's ascender and below the descender). + /// + /// {@macro dart.ui.leading} + /// + /// The leading can become negative when [TextStyle.height] is smaller than + /// 1.0. + /// + /// This is the default strategy used by CSS, known as + /// ["half-leading"](https://www.w3.org/TR/css-inline-3/#half-leading). + even, +} + +/// {@template dart.ui.textHeightBehavior} +/// Defines how to apply [TextStyle.height] over and under text. +/// +/// [applyHeightToFirstAscent] and [applyHeightToLastDescent] represent whether +/// the [TextStyle.height] modifier will be applied to the corresponding metric. +/// By default both properties are true, and [TextStyle.height] is applied as +/// normal. When set to false, the font's default ascent will be used. +/// +/// [leadingDistribution] determines how the [leading] is distributed over and +/// under text. This property applies before [applyHeightToFirstAscent] and +/// [applyHeightToLastDescent]. /// -/// Each boolean value represents whether the [TextStyle.height] modifier will -/// be applied to the corresponding metric. By default, all properties are true, -/// and [TextStyle.height] is applied as normal. When set to false, the font's -/// default ascent will be used. /// {@endtemplate} class TextHeightBehavior { @@ -451,21 +1646,23 @@ class TextHeightBehavior { /// * applyHeightToLastDescent: When true, the [TextStyle.height] modifier /// will be applied to the descent of the last line. When false, the font's /// default descent will be used. + /// * leadingDistribution: How the [leading] is distributed over and under + /// text. /// /// All properties default to true (height modifications applied as normal). const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, + this.leadingDistribution = TextLeadingDistribution.proportional, }); /// Creates a new TextHeightBehavior object from an encoded form. /// - /// See [encode] for the creation of the encoded form. - const TextHeightBehavior.fromEncoded(int encoded) + /// See [_encode] for the creation of the encoded form. + const TextHeightBehavior._fromEncoded(int encoded, this.leadingDistribution) : applyHeightToFirstAscent = (encoded & 0x1) == 0, applyHeightToLastDescent = (encoded & 0x2) == 0; - /// Whether to apply the [TextStyle.height] modifier to the ascent of the first /// line in the paragraph. /// @@ -490,9 +1687,23 @@ class TextHeightBehavior { /// Defaults to true (height modifications applied as normal). final bool applyHeightToLastDescent; - /// Returns an encoded int representation of this object. - int encode() { - return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); + /// {@template dart.ui.textLeadingDistribution} + /// How the ["leading"](https://en.wikipedia.org/wiki/Leading) is distributed + /// over and under the text. + /// + /// Does not affect layout when [TextStyle.height] is not specified. The + /// leading can become negative, for example, when [TextLeadingDistribution.even] + /// is used with a [TextStyle.height] much smaller than 1.0. + /// {@endtemplate} + /// + /// Defaults to [TextLeadingDistribution.proportional], + final TextLeadingDistribution leadingDistribution; + + /// Returns an encoded int representation of this object (excluding + /// [leadingDistribution]). + int _encode() { + return (applyHeightToFirstAscent ? 0 : 1 << 0) + | (applyHeightToLastDescent ? 0 : 1 << 1); } @override @@ -501,7 +1712,8 @@ class TextHeightBehavior { return false; return other is TextHeightBehavior && other.applyHeightToFirstAscent == applyHeightToFirstAscent - && other.applyHeightToLastDescent == applyHeightToLastDescent; + && other.applyHeightToLastDescent == applyHeightToLastDescent + && other.leadingDistribution == leadingDistribution; } @override @@ -509,6 +1721,7 @@ class TextHeightBehavior { return hashValues( applyHeightToFirstAscent, applyHeightToLastDescent, + leadingDistribution.index, ); } @@ -516,7 +1729,8 @@ class TextHeightBehavior { String toString() { return 'TextHeightBehavior(' 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent' + 'applyHeightToLastDescent: $applyHeightToLastDescent, ' + 'leadingDistribution: $leadingDistribution' ')'; } } @@ -545,7 +1759,8 @@ bool _listEquals(List? a, List? b) { // - Element 0: A bit field where the ith bit indicates whether the ith element // has a non-null value. Bits 8 to 12 indicate whether |fontFamily|, // |fontSize|, |letterSpacing|, |wordSpacing|, and |height| are non-null, -// respectively. Bit 0 is unused. +// respectively. Bit 0 indicates the [TextLeadingDistribution] of the text +// style. // // - Element 1: The |color| in ARGB with 8 bits per channel. // @@ -584,7 +1799,9 @@ Int32List _encodeTextStyle( List? shadows, List? fontFeatures, ) { - final Int32List result = Int32List(8); + final Int32List result = Int32List(9); + // The 0th bit of result[0] is reserved for leadingDistribution. + if (color != null) { result[0] |= 1 << 1; result[1] = color.value; @@ -656,6 +1873,7 @@ Int32List _encodeTextStyle( result[0] |= 1 << 18; // Passed separately to native. } + return result; } @@ -690,6 +1908,8 @@ class TextStyle { /// * `textBaseline`: The common baseline that should be aligned between this text span and its parent text span, or, for the root text spans, with the line box. /// * `height`: The height of this text span, as a multiplier of the font size. Omitting `height` will allow the line height /// to take the height as defined by the font, which may not be exactly the height of the fontSize. + /// * `leadingDistribution`: When `height` is specified, how the extra vertical space should be distributed over and under the text. Defaults + /// to the paragraph's [TextHeightBehavior] if left unspecified. /// * `locale`: The locale used to select region-specific glyphs. /// * `background`: The paint drawn as a background for the text. /// * `foreground`: The paint used to draw the text. If this is specified, `color` must be null. @@ -709,6 +1929,7 @@ class TextStyle { double? letterSpacing, double? wordSpacing, double? height, + TextLeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, @@ -739,6 +1960,7 @@ class TextStyle { shadows, fontFeatures, ), + _leadingDistribution = leadingDistribution, _fontFamily = fontFamily ?? '', _fontFamilyFallback = fontFamilyFallback, _fontSize = fontSize, @@ -765,12 +1987,14 @@ class TextStyle { final Paint? _foreground; final List? _shadows; final List? _fontFeatures; + final TextLeadingDistribution? _leadingDistribution; @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is TextStyle + && other._leadingDistribution == _leadingDistribution && other._fontFamily == _fontFamily && other._fontSize == _fontSize && other._letterSpacing == _letterSpacing @@ -787,34 +2011,35 @@ class TextStyle { } @override - int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontFamilyFallback, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground, hashList(_shadows), _decorationThickness, hashList(_fontFeatures)); + int get hashCode => hashValues(hashList(_encoded), _leadingDistribution, _fontFamily, _fontFamilyFallback, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground, hashList(_shadows), _decorationThickness, hashList(_fontFeatures)); @override String toString() { return 'TextStyle(' - 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' - 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' - 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' - 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' + 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' + 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' + 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' + 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' // The decorationThickness is not in encoded order in order to keep it near the other decoration properties. - 'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100 ? _decorationThickness : "unspecified"}, ' - 'fontWeight: ${ _encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' - 'fontStyle: ${ _encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' - 'textBaseline: ${ _encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' + 'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100 ? _decorationThickness : "unspecified"}, ' + 'fontWeight: ${ _encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' + 'fontStyle: ${ _encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' + 'textBaseline: ${ _encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' 'fontFamily: ${ _encoded[0] & 0x00200 == 0x00200 - && _fontFamily != '' ? _fontFamily : "unspecified"}, ' + && _fontFamily != '' ? _fontFamily : "unspecified"}, ' 'fontFamilyFallback: ${ _encoded[0] & 0x00200 == 0x00200 && _fontFamilyFallback != null - && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' - 'fontSize: ${ _encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' - 'letterSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' - 'wordSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' - 'height: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_height}x" : "unspecified"}, ' - 'locale: ${ _encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' - 'background: ${ _encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' - 'foreground: ${ _encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' - 'shadows: ${ _encoded[0] & 0x20000 == 0x20000 ? _shadows : "unspecified"}, ' - 'fontFeatures: ${ _encoded[0] & 0x40000 == 0x40000 ? _fontFeatures : "unspecified"}' + && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' + 'fontSize: ${ _encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' + 'letterSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' + 'wordSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' + 'height: ${ _encoded[0] & 0x02000 == 0x02000 ? "${_height}x" : "unspecified"}, ' + 'leadingDistribution: ${_leadingDistribution ?? "unspecified"}, ' + 'locale: ${ _encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' + 'background: ${ _encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' + 'foreground: ${ _encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' + 'shadows: ${ _encoded[0] & 0x20000 == 0x20000 ? _shadows : "unspecified"}, ' + 'fontFeatures: ${ _encoded[0] & 0x40000 == 0x40000 ? _fontFeatures : "unspecified"}' ')'; } } @@ -838,8 +2063,8 @@ class TextStyle { // // - Element 5: The value of |maxLines|. // -// - Element 6: The encoded value of |textHeightBehavior|. -// +// - Element 6: The encoded value of |textHeightBehavior|, except its leading +// distribution. Int32List _encodeParagraphStyle( TextAlign? textAlign, TextDirection? textDirection, @@ -877,7 +2102,7 @@ Int32List _encodeParagraphStyle( } if (textHeightBehavior != null) { result[0] |= 1 << 6; - result[6] = textHeightBehavior.encode(); + result[6] = textHeightBehavior._encode(); } if (fontFamily != null) { result[0] |= 1 << 7; @@ -944,6 +2169,10 @@ class ParagraphStyle { /// * `textHeightBehavior`: Specifies how the `height` multiplier is /// applied to ascent of the first line and the descent of the last line. /// + /// * `leadingDistribution`: Specifies how the extra vertical space added by + /// the `height` multiplier should be distributed over and under the text. + /// Defaults to [TextLeadingDistribution.proportional]. + /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). /// @@ -996,7 +2225,8 @@ class ParagraphStyle { _height = height, _strutStyle = strutStyle, _ellipsis = ellipsis, - _locale = locale; + _locale = locale, + _leadingDistribution = textHeightBehavior?.leadingDistribution ?? TextLeadingDistribution.proportional; final Int32List _encoded; final String? _fontFamily; @@ -1005,6 +2235,7 @@ class ParagraphStyle { final StrutStyle? _strutStyle; final String? _ellipsis; final Locale? _locale; + final TextLeadingDistribution _leadingDistribution; @override bool operator ==(Object other) { @@ -1019,11 +2250,12 @@ class ParagraphStyle { && other._strutStyle == _strutStyle && other._ellipsis == _ellipsis && other._locale == _locale + && other._leadingDistribution == _leadingDistribution && _listEquals(other._encoded, _encoded); } @override - int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale); + int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution); @override String toString() { @@ -1035,7 +2267,7 @@ class ParagraphStyle { 'maxLines: ${ _encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, ' 'textHeightBehavior: ${ _encoded[0] & 0x040 == 0x040 ? - TextHeightBehavior.fromEncoded(_encoded[6]).toString() : "unspecified"}, ' + TextHeightBehavior._fromEncoded(_encoded[6], _leadingDistribution).toString() : "unspecified"}, ' 'fontFamily: ${ _encoded[0] & 0x080 == 0x080 ? _fontFamily : "unspecified"}, ' 'fontSize: ${ _encoded[0] & 0x100 == 0x100 ? _fontSize : "unspecified"}, ' 'height: ${ _encoded[0] & 0x200 == 0x200 ? "${_height}x" : "unspecified"}, ' @@ -1049,7 +2281,8 @@ class ParagraphStyle { // compactness. The first 8 bits is a bitmask that records which properties // are null. The rest of the values are encoded in the same order encountered // in the bitmask. The final returned value truncates any unused bytes -// at the end. +// at the end. For ease of decoding, all 8 bit ints are stored before any 32 bit +// ints. // // We serialize this more thoroughly than ParagraphStyle because it is // much more likely that the strut is empty/null and we wish to add @@ -1059,6 +2292,7 @@ ByteData _encodeStrut( List? fontFamilyFallback, double? fontSize, double? height, + TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -1066,13 +2300,14 @@ ByteData _encodeStrut( if (fontFamily == null && fontSize == null && height == null && + leadingDistribution == null && leading == null && fontWeight == null && fontStyle == null && forceStrutHeight == null) return ByteData(0); - final ByteData data = ByteData(15); // Max size is 15 bytes + final ByteData data = ByteData(16); // Max size is 16 bytes int bitmask = 0; // 8 bit mask int byteCount = 1; if (fontWeight != null) { @@ -1089,30 +2324,33 @@ ByteData _encodeStrut( bitmask |= 1 << 2; // passed separately to native } + + // The 3rd bit (0-indexed) is reserved for leadingDistribution. + if (fontSize != null) { - bitmask |= 1 << 3; + bitmask |= 1 << 4; data.setFloat32(byteCount, fontSize, _kFakeHostEndian); byteCount += 4; } if (height != null) { - bitmask |= 1 << 4; + bitmask |= 1 << 5; data.setFloat32(byteCount, height, _kFakeHostEndian); byteCount += 4; + } if (leading != null) { - bitmask |= 1 << 5; + bitmask |= 1 << 6; data.setFloat32(byteCount, leading, _kFakeHostEndian); byteCount += 4; } - if (forceStrutHeight != null) { - bitmask |= 1 << 6; - // We store this boolean directly in the bitmask since there is - // extra space in the 16 bit int. - bitmask |= (forceStrutHeight ? 1 : 0) << 7; + if (forceStrutHeight ?? false) { + bitmask |= 1 << 7; } data.setInt8(0, bitmask); + assert(byteCount <= 16); + assert(bitmask >> 8 == 0, 'strut bitmask overflow: $bitmask'); return ByteData.view(data.buffer, 0, byteCount); } @@ -1143,7 +2381,15 @@ class StrutStyle { /// be provided for this property to take effect. /// /// * `leading`: The minimum amount of leading between lines as a multiple of - /// the font size. `fontSize` must be provided for this property to take effect. + /// the font size. `fontSize` must be provided for this property to take + /// effect. The leading added by this property is distributed evenly over + /// and under the text, regardless of `leadingDistribution`. + /// + /// * `leadingDistribution`: how the extra vertical space added by the + /// `height` multiplier should be distributed over and under the text, + /// independent of `leading` (which is always distributed evenly over and + /// under text). Defaults to the paragraph's [TextHeightBehavior]'s leading + /// distribution. /// /// * `fontWeight`: The typeface thickness to use when painting the text /// (e.g., bold). @@ -1163,6 +2409,7 @@ class StrutStyle { List? fontFamilyFallback, double? fontSize, double? height, + TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, @@ -1172,18 +2419,22 @@ class StrutStyle { fontFamilyFallback, fontSize, height, + leadingDistribution, leading, fontWeight, fontStyle, forceStrutHeight, ), + _leadingDistribution = leadingDistribution, _fontFamily = fontFamily, _fontFamilyFallback = fontFamilyFallback; final ByteData _encoded; // Most of the data for strut is encoded. final String? _fontFamily; final List? _fontFamilyFallback; + final TextLeadingDistribution? _leadingDistribution; + bool get _enabled => _encoded.lengthInBytes > 0; @override bool operator ==(Object other) { @@ -1193,12 +2444,13 @@ class StrutStyle { return false; return other is StrutStyle && other._fontFamily == _fontFamily + && other._leadingDistribution == _leadingDistribution && _listEquals(other._fontFamilyFallback, _fontFamilyFallback) && _listEquals(other._encoded.buffer.asInt8List(), _encoded.buffer.asInt8List()); } @override - int get hashCode => hashValues(hashList(_encoded.buffer.asInt8List()), _fontFamily); + int get hashCode => hashValues(hashList(_encoded.buffer.asInt8List()), _fontFamily, _leadingDistribution); } @@ -1441,8 +2693,8 @@ class TextPosition { const TextPosition({ required this.offset, this.affinity = TextAffinity.downstream, - }) : assert(offset != null), // ignore: unnecessary_null_comparison - assert(affinity != null); // ignore: unnecessary_null_comparison + }) : assert(offset != null), + assert(affinity != null); /// The index of the character that immediately follows the position in the /// string representation of the text. @@ -1494,14 +2746,14 @@ class TextRange { const TextRange({ required this.start, required this.end, - }) : assert(start != null && start >= -1), // ignore: unnecessary_null_comparison - assert(end != null && end >= -1); // ignore: unnecessary_null_comparison + }) : assert(start != null && start >= -1), + assert(end != null && end >= -1); /// A text range that starts and ends at offset. /// /// The [offset] argument must be non-null and greater than or equal to -1. const TextRange.collapsed(int offset) - : assert(offset != null && offset >= -1), // ignore: unnecessary_null_comparison + : assert(offset != null && offset >= -1), start = offset, end = offset; @@ -1576,7 +2828,7 @@ class ParagraphConstraints { /// The [width] argument must not be null. const ParagraphConstraints({ required this.width, - }) : assert(width != null); // ignore: unnecessary_null_comparison + }) : assert(width != null); /// The width the paragraph should use whey computing the positions of glyphs. /// @@ -1638,7 +2890,7 @@ enum BoxHeightStyle { /// The top and bottom of each box will cover half of the /// space above and half of the space below the line. /// - /// {@template flutter.dart:ui.boxHeightStyle.includeLineSpacing} + /// {@template dart.ui.boxHeightStyle.includeLineSpacing} /// The top edge of each line should be the same as the bottom edge /// of the line above. There should be no gaps in vertical coverage given any /// amount of line spacing. Line spacing is not included above the first line @@ -1650,14 +2902,14 @@ enum BoxHeightStyle { /// /// The line spacing will be added to the top of the box. /// - /// {@macro flutter.dart:ui.boxHeightStyle.includeLineSpacing} + /// {@macro dart.ui.boxHeightStyle.includeLineSpacing} includeLineSpacingTop, /// Extends the bottom edge of the bounds to fully cover any line spacing. /// /// The line spacing will be added to the bottom of the box. /// - /// {@macro flutter.dart:ui.boxHeightStyle.includeLineSpacing} + /// {@macro dart.ui.boxHeightStyle.includeLineSpacing} includeLineSpacingBottom, /// Calculate box heights based on the metrics of this paragraph's [StrutStyle]. @@ -1742,9 +2994,6 @@ enum PlaceholderAlignment { /// method. class LineMetrics { /// Creates a [LineMetrics] object with only the specified values. - /// - /// Omitted values will remain null. [Paragraph.computeLineMetrics] produces - /// fully defined [LineMetrics] with no null values. LineMetrics({ required this.hardBreak, required this.ascent, @@ -1867,7 +3116,7 @@ class LineMetrics { /// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph] /// method. @pragma('vm:entry-point') -class Paragraph extends NativeFieldWrapperClass2 { +class Paragraph extends NativeFieldWrapperClass1 { /// This class is created by the engine, and should not be instantiated /// or extended directly. /// @@ -1956,8 +3205,8 @@ class Paragraph extends NativeFieldWrapperClass2 { /// /// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option. List getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}) { - assert(boxHeightStyle != null); // ignore: unnecessary_null_comparison - assert(boxWidthStyle != null); // ignore: unnecessary_null_comparison + assert(boxHeightStyle != null); + assert(boxWidthStyle != null); return _decodeTextBoxes(_getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index)); } // See paragraph.cc for the layout of this return value. @@ -2004,7 +3253,25 @@ class Paragraph extends NativeFieldWrapperClass2 { /// metrics, so use it sparingly. TextRange getLineBoundary(TextPosition position) { final List boundary = _getLineBoundary(position.offset); - return TextRange(start: boundary[0], end: boundary[1]); + final TextRange line = TextRange(start: boundary[0], end: boundary[1]); + + final List nextBoundary = _getLineBoundary(position.offset + 1); + final TextRange nextLine = TextRange(start: nextBoundary[0], end: nextBoundary[1]); + // If there is no next line, because we're at the end of the field, return + // line. + if (!nextLine.isValid) { + return line; + } + + // _getLineBoundary only considers the offset and assumes that the + // TextAffinity is upstream. In the case that TextPosition is just after a + // wordwrap (downstream), we need to return the line for the next offset. + if (position.affinity == TextAffinity.downstream && line != nextLine + && position.offset == line.end && line.end == nextLine.start) { + final List nextBoundary = _getLineBoundary(position.offset + 1); + return TextRange(start: nextBoundary[0], end: nextBoundary[1]); + } + return line; } List _getLineBoundary(int offset) native 'Paragraph_getLineBoundary'; @@ -2057,32 +3324,43 @@ class Paragraph extends NativeFieldWrapperClass2 { /// /// After constructing a [Paragraph], call [Paragraph.layout] on it and then /// paint it with [Canvas.drawParagraph]. -class ParagraphBuilder extends NativeFieldWrapperClass2 { +class ParagraphBuilder extends NativeFieldWrapperClass1 { /// Creates a new [ParagraphBuilder] object, which is used to create a /// [Paragraph]. @pragma('vm:entry-point') - ParagraphBuilder(ParagraphStyle style) { - List? strutFontFamilies; - final StrutStyle? strutStyle = style._strutStyle; - if (strutStyle != null) { - strutFontFamilies = []; - final String? fontFamily = strutStyle._fontFamily; - if (fontFamily != null) - strutFontFamilies.add(fontFamily); - if (strutStyle._fontFamilyFallback != null) - strutFontFamilies.addAll(strutStyle._fontFamilyFallback!); - } - _constructor( - style._encoded, - strutStyle?._encoded, - style._fontFamily, - strutFontFamilies, - style._fontSize, - style._height, - style._ellipsis, - _encodeLocale(style._locale) - ); + ParagraphBuilder(ParagraphStyle style) + : _defaultLeadingDistribution = style._leadingDistribution { + List? strutFontFamilies; + final StrutStyle? strutStyle = style._strutStyle; + final ByteData? encodedStrutStyle; + if (strutStyle != null && strutStyle._enabled) { + final String? fontFamily = strutStyle._fontFamily; + strutFontFamilies = [ + if (fontFamily != null) fontFamily, + ...?strutStyle._fontFamilyFallback, + ]; + + assert(TextLeadingDistribution.values.length <= 2); + final TextLeadingDistribution leadingDistribution = strutStyle._leadingDistribution + ?? style._leadingDistribution; + encodedStrutStyle = strutStyle._encoded; + int bitmask = encodedStrutStyle.getInt8(0); + bitmask |= (leadingDistribution.index) << 3; + encodedStrutStyle.setInt8(0, bitmask); + } else { + encodedStrutStyle = null; + } + _constructor( + style._encoded, + encodedStrutStyle, + style._fontFamily, + strutFontFamilies, + style._fontSize, + style._height, + style._ellipsis, + _encodeLocale(style._locale) + ); } void _constructor( @@ -2104,6 +3382,7 @@ class ParagraphBuilder extends NativeFieldWrapperClass2 { List get placeholderScales => _placeholderScales; List _placeholderScales = []; + final TextLeadingDistribution _defaultLeadingDistribution; /// Applies the given style to the added text until [pop] is called. /// /// See [pop] for details. @@ -2111,21 +3390,30 @@ class ParagraphBuilder extends NativeFieldWrapperClass2 { final List fullFontFamilies = []; fullFontFamilies.add(style._fontFamily); if (style._fontFamilyFallback != null) - fullFontFamilies.addAll(style._fontFamilyFallback!); + fullFontFamilies.addAll(style._fontFamilyFallback!); + + final Int32List encoded = style._encoded; + final TextLeadingDistribution finalLeadingDistribution = style._leadingDistribution ?? _defaultLeadingDistribution; + // ensure the enum can be represented using 1 bit. + assert(TextLeadingDistribution.values.length <= 2); + + // Use the leading distribution from the paragraph's style if it's not + // explicitly set in `style`. + encoded[0] |= finalLeadingDistribution.index << 0; ByteData? encodedFontFeatures; final List? fontFeatures = style._fontFeatures; if (fontFeatures != null) { encodedFontFeatures = ByteData(fontFeatures.length * FontFeature._kEncodedSize); int byteOffset = 0; - for (FontFeature feature in fontFeatures) { + for (final FontFeature feature in fontFeatures) { feature._encode(ByteData.view(encodedFontFeatures.buffer, byteOffset, FontFeature._kEncodedSize)); byteOffset += FontFeature._kEncodedSize; } } _pushStyle( - style._encoded, + encoded, fullFontFamilies, style._fontSize, style._letterSpacing, @@ -2225,24 +3513,24 @@ class ParagraphBuilder extends NativeFieldWrapperClass2 { /// /// The `scale` parameter will scale the `width` and `height` by the specified amount, /// and keep track of the scale. The scales of placeholders added can be accessed - /// through [placeholderScales]. This is primarily used for acessibility scaling. + /// through [placeholderScales]. This is primarily used for accessibility scaling. void addPlaceholder(double width, double height, PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, TextBaseline? baseline, }) { // Require a baseline to be specified if using a baseline-based alignment. - assert((alignment == PlaceholderAlignment.aboveBaseline || + assert(!(alignment == PlaceholderAlignment.aboveBaseline || alignment == PlaceholderAlignment.belowBaseline || - alignment == PlaceholderAlignment.baseline) ? baseline != null : true); + alignment == PlaceholderAlignment.baseline) || baseline != null); // Default the baselineOffset to height if null. This will place the placeholder // fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline]. baselineOffset = baselineOffset ?? height; - _addPlaceholder(width * scale, height * scale, alignment.index, baselineOffset * scale, baseline == null ? null : baseline.index); + _addPlaceholder(width * scale, height * scale, alignment.index, baselineOffset * scale, baseline?.index); _placeholderCount++; _placeholderScales.add(scale); } - String _addPlaceholder(double width, double height, int alignment, double baselineOffset, int? baseline) native 'ParagraphBuilder_addPlaceholder'; + String? _addPlaceholder(double width, double height, int alignment, double baselineOffset, int? baseline) native 'ParagraphBuilder_addPlaceholder'; /// Applies the given paragraph style and returns a [Paragraph] containing the /// added text and associated styling. @@ -2264,7 +3552,9 @@ class ParagraphBuilder extends NativeFieldWrapperClass2 { /// If this is not provided, then the family name will be extracted from the font file. Future loadFontFromList(Uint8List list, {String? fontFamily}) { return _futurize( - (_Callback callback) => _loadFontFromList(list, callback, fontFamily) + (_Callback callback) { + _loadFontFromList(list, callback, fontFamily); + } ).then((_) => _sendFontChangeMessage()); } @@ -2273,11 +3563,30 @@ final ByteData _fontChangeMessage = utf8.encoder.convert( ).buffer.asByteData(); FutureOr _sendFontChangeMessage() async { - window.onPlatformMessage?.call( - 'flutter/system', - _fontChangeMessage, - (_) {}, - ); + const String kSystemChannelName = 'flutter/system'; + if (PlatformDispatcher.instance.onPlatformMessage != null) { + _invoke3( + PlatformDispatcher.instance.onPlatformMessage, + PlatformDispatcher.instance._onPlatformMessageZone, + kSystemChannelName, + _fontChangeMessage, + (ByteData? responseData) { }, + ); + } else { + channelBuffers.push(kSystemChannelName, _fontChangeMessage, (ByteData? responseData) { }); + } } -String _loadFontFromList(Uint8List list, _Callback callback, String? fontFamily) native 'loadFontFromList'; +// TODO(gspencergoog): remove this template block once the framework templates +// are renamed to not reference it. +/// {@template flutter.dart:ui.textHeightBehavior} +/// Defines how the paragraph will apply [TextStyle.height] to the ascent of the +/// first line and descent of the last line. +/// +/// Each boolean value represents whether the [TextStyle.height] modifier will +/// be applied to the corresponding metric. By default, all properties are true, +/// and [TextStyle.height] is applied as normal. When set to false, the font's +/// default ascent will be used. +/// {@endtemplate} + +void _loadFontFromList(Uint8List list, _Callback callback, String? fontFamily) native 'loadFontFromList'; diff --git a/lib/ui/text/asset_manager_font_provider.cc b/lib/ui/text/asset_manager_font_provider.cc index c1ae48e137f60..b07472f7e7292 100644 --- a/lib/ui/text/asset_manager_font_provider.cc +++ b/lib/ui/text/asset_manager_font_provider.cc @@ -117,6 +117,8 @@ SkTypeface* AssetManagerFontStyleSet::createTypeface(int i) { // Ownership of the stream is transferred. asset.typeface = SkTypeface::MakeFromStream(std::move(stream)); if (!asset.typeface) { + FML_DLOG(ERROR) << "Unable to load font asset for family: " + << family_name_; return nullptr; } } diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 31897c82d9b3b..e8186be6c4067 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -132,20 +132,19 @@ tonic::Float32List Paragraph::getRectsForPlaceholders() { } Dart_Handle Paragraph::getPositionForOffset(double dx, double dy) { - Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); txt::Paragraph::PositionWithAffinity pos = m_paragraph->GetGlyphPositionAtCoordinate(dx, dy); - Dart_ListSetAt(result, 0, ToDart(pos.position)); - Dart_ListSetAt(result, 1, ToDart(static_cast(pos.affinity))); - return result; + std::vector result = { + pos.position, // size_t already + static_cast(pos.affinity) // affinity (enum) + }; + return tonic::DartConverter::ToDart(result); } Dart_Handle Paragraph::getWordBoundary(unsigned offset) { txt::Paragraph::Range point = m_paragraph->GetWordBoundary(offset); - Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); - Dart_ListSetAt(result, 0, ToDart(point.start)); - Dart_ListSetAt(result, 1, ToDart(point.end)); - return result; + std::vector result = {point.start, point.end}; + return tonic::DartConverter::ToDart(result); } Dart_Handle Paragraph::getLineBoundary(unsigned offset) { @@ -159,10 +158,8 @@ Dart_Handle Paragraph::getLineBoundary(unsigned offset) { break; } } - Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2); - Dart_ListSetAt(result, 0, ToDart(line_start)); - Dart_ListSetAt(result, 1, ToDart(line_end)); - return result; + std::vector result = {line_start, line_end}; + return tonic::DartConverter::ToDart(result); } tonic::Float64List Paragraph::computeLineMetrics() { diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 1cd8f7d65cbec..2ef379a2fe8c7 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -32,6 +32,7 @@ namespace { // TextStyle +const int tsLeadingDistributionIndex = 0; const int tsColorIndex = 1; const int tsTextDecorationIndex = 2; const int tsTextDecorationColorIndex = 3; @@ -51,6 +52,7 @@ const int tsForegroundIndex = 16; const int tsTextShadowsIndex = 17; const int tsFontFeaturesIndex = 18; +const int tsLeadingDistributionMask = 1 << tsLeadingDistributionIndex; const int tsColorMask = 1 << tsColorIndex; const int tsTextDecorationMask = 1 << tsTextDecorationIndex; const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex; @@ -116,14 +118,16 @@ constexpr uint32_t kFontFeatureTagLength = 4; const int sFontWeightIndex = 0; const int sFontStyleIndex = 1; const int sFontFamilyIndex = 2; -const int sFontSizeIndex = 3; -const int sHeightIndex = 4; -const int sLeadingIndex = 5; -const int sForceStrutHeightIndex = 6; +const int sLeadingDistributionIndex = 3; +const int sFontSizeIndex = 4; +const int sHeightIndex = 5; +const int sLeadingIndex = 6; +const int sForceStrutHeightIndex = 7; const int sFontWeightMask = 1 << sFontWeightIndex; const int sFontStyleMask = 1 << sFontStyleIndex; const int sFontFamilyMask = 1 << sFontFamilyIndex; +const int sLeadingDistributionMask = 1 << sLeadingDistributionIndex; const int sFontSizeMask = 1 << sFontSizeIndex; const int sHeightMask = 1 << sHeightIndex; const int sLeadingMask = 1 << sLeadingIndex; @@ -198,6 +202,8 @@ void decodeStrut(Dart_Handle strut_data, static_cast(uint8_data[byte_count++]); } + paragraph_style.strut_half_leading = mask & sLeadingDistributionMask; + std::vector float_data; float_data.resize((byte_data.length_in_bytes() - byte_count) / 4); memcpy(float_data.data(), @@ -214,10 +220,10 @@ void decodeStrut(Dart_Handle strut_data, if (mask & sLeadingMask) { paragraph_style.strut_leading = float_data[float_count++]; } - if (mask & sForceStrutHeightMask) { - // The boolean is stored as the last bit in the bitmask. - paragraph_style.force_strut_height = (mask & 1 << 7) != 0; - } + + // The boolean is stored as the last bit in the bitmask, as null + // and false have the same behavior. + paragraph_style.force_strut_height = mask & sForceStrutHeightMask; if (mask & sFontFamilyMask) { paragraph_style.strut_font_families = strut_font_families; @@ -295,14 +301,23 @@ ParagraphBuilder::ParagraphBuilder( ->client() ->GetFontCollection(); + typedef std::unique_ptr (*ParagraphBuilderFactory)( + const txt::ParagraphStyle& style, + std::shared_ptr font_collection); + ParagraphBuilderFactory factory = txt::ParagraphBuilder::CreateTxtBuilder; + #if FLUTTER_ENABLE_SKSHAPER -#define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateSkiaBuilder +#if FLUTTER_ALWAYS_USE_SKSHAPER + bool enable_skparagraph = true; #else -#define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateTxtBuilder + bool enable_skparagraph = UIDartState::Current()->enable_skparagraph(); #endif + if (enable_skparagraph) { + factory = txt::ParagraphBuilder::CreateSkiaBuilder; + } +#endif // FLUTTER_ENABLE_SKSHAPER - m_paragraphBuilder = - FLUTTER_PARAGRAPH_BUILDER(style, font_collection.GetFontCollection()); + m_paragraphBuilder = factory(style, font_collection.GetFontCollection()); } ParagraphBuilder::~ParagraphBuilder() = default; @@ -364,7 +379,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, Dart_Handle foreground_data, Dart_Handle shadows_data, Dart_Handle font_features_data) { - FML_DCHECK(encoded.num_elements() == 8); + FML_DCHECK(encoded.num_elements() == 9); int32_t mask = encoded[0]; @@ -372,6 +387,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, // explicitly given. txt::TextStyle style = m_paragraphBuilder->PeekStyle(); + style.half_leading = mask & tsLeadingDistributionMask; // Only change the style property from the previous value if a new explicitly // set value is available if (mask & tsColorMask) { diff --git a/lib/ui/text/paragraph_builder.h b/lib/ui/text/paragraph_builder.h index 4f4a00b5b94ab..01baed167f9b0 100644 --- a/lib/ui/text/paragraph_builder.h +++ b/lib/ui/text/paragraph_builder.h @@ -57,7 +57,7 @@ class ParagraphBuilder : public RefCountedDartWrappable { Dart_Handle addText(const std::u16string& text); - // Pushes the information requried to leave an open space, where Flutter may + // Pushes the information required to leave an open space, where Flutter may // draw a custom placeholder into. // // Internally, this method adds a single object replacement character (0xFFFC) diff --git a/lib/ui/text/text_box.h b/lib/ui/text/text_box.h index c3eea00aa8999..27f60670f232f 100644 --- a/lib/ui/text/text_box.h +++ b/lib/ui/text/text_box.h @@ -25,18 +25,4 @@ struct TextBox { } // namespace flutter -namespace tonic { - -template <> -struct DartConverter { - static Dart_Handle ToDart(const flutter::TextBox& val); -}; - -template <> -struct DartListFactory { - static Dart_Handle NewList(intptr_t length); -}; - -} // namespace tonic - #endif // FLUTTER_LIB_UI_TEXT_TEXT_BOX_H_ diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index fe0e4fa16bcd6..23d85835c1d92 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -9,10 +9,9 @@ /// This library exposes the lowest-level services that Flutter frameworks use /// to bootstrap applications, such as classes for driving the input, graphics /// text, layout, and rendering subsystems. -// @dart = 2.10 +// @dart = 2.12 library dart.ui; -import 'dart:_internal' hide Symbol; // ignore: unused_import import 'dart:async'; import 'dart:collection' as collection; import 'dart:convert'; @@ -30,9 +29,11 @@ part 'geometry.dart'; part 'hash_codes.dart'; part 'hooks.dart'; part 'isolate_name_server.dart'; +part 'key.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; +part 'platform_dispatcher.dart'; part 'plugins.dart'; part 'pointer.dart'; part 'semantics.dart'; diff --git a/lib/ui/ui_benchmarks.cc b/lib/ui/ui_benchmarks.cc index 293251400bcd3..e659b30b942a7 100644 --- a/lib/ui/ui_benchmarks.cc +++ b/lib/ui/ui_benchmarks.cc @@ -4,6 +4,7 @@ #include "flutter/benchmarking/benchmarking.h" #include "flutter/common/settings.h" +#include "flutter/lib/ui/volatile_path_tracker.h" #include "flutter/lib/ui/window/platform_message_response_dart.h" #include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/shell/common/thread_host.h" @@ -18,10 +19,9 @@ class Fixture : public testing::FixtureTest { void TestBody() override{}; }; -static void BM_PlatformMessageResponseDartComplete( - benchmark::State& state) { // NOLINT +static void BM_PlatformMessageResponseDartComplete(benchmark::State& state) { ThreadHost thread_host("test", - ThreadHost::Type::Platform | ThreadHost::Type::GPU | + ThreadHost::Type::Platform | ThreadHost::Type::RASTER | ThreadHost::Type::IO | ThreadHost::Type::UI); TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), thread_host.raster_thread->GetTaskRunner(), @@ -32,7 +32,7 @@ static void BM_PlatformMessageResponseDartComplete( auto vm_ref = DartVMRef::Create(settings); auto isolate = testing::RunDartCodeInIsolate(vm_ref, settings, task_runners, "main", {}, - testing::GetFixturesPath(), {}); + testing::GetDefaultKernelFilePath(), {}); while (state.KeepRunning()) { state.PauseTiming(); @@ -67,7 +67,54 @@ static void BM_PlatformMessageResponseDartComplete( } } +static void BM_PathVolatilityTracker(benchmark::State& state) { + ThreadHost thread_host("test", + ThreadHost::Type::Platform | ThreadHost::Type::RASTER | + ThreadHost::Type::IO | ThreadHost::Type::UI); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + + VolatilePathTracker tracker(task_runners.GetUITaskRunner(), true); + + while (state.KeepRunning()) { + std::vector> paths; + constexpr int path_count = 1000; + for (int i = 0; i < path_count; i++) { + auto path = std::make_shared(); + path->path = SkPath(); + path->path.setIsVolatile(true); + paths.push_back(std::move(path)); + } + + fml::AutoResetWaitableEvent latch; + task_runners.GetUITaskRunner()->PostTask([&]() { + for (auto path : paths) { + tracker.Insert(path); + } + latch.Signal(); + }); + + latch.Wait(); + + task_runners.GetUITaskRunner()->PostTask([&]() { tracker.OnFrame(); }); + + for (int i = 0; i < path_count - 10; ++i) { + tracker.Erase(paths[i]); + } + + task_runners.GetUITaskRunner()->PostTask([&]() { tracker.OnFrame(); }); + + latch.Reset(); + task_runners.GetUITaskRunner()->PostTask([&]() { latch.Signal(); }); + latch.Wait(); + } +} + BENCHMARK(BM_PlatformMessageResponseDartComplete) ->Unit(benchmark::kMicrosecond); +BENCHMARK(BM_PathVolatilityTracker)->Unit(benchmark::kMillisecond); + } // namespace flutter diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index eac218548cfbf..06411c4d3ce98 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -4,44 +4,70 @@ #include "flutter/lib/ui/ui_dart_state.h" +#include + #include "flutter/fml/message_loop.h" #include "flutter/lib/ui/window/platform_configuration.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_message_handler.h" +#if defined(OS_ANDROID) +#include +#elif defined(OS_IOS) +extern "C" { +// Cannot import the syslog.h header directly because of macro collision. +extern void syslog(int, const char*, ...); +} +#endif + using tonic::ToDart; namespace flutter { -UIDartState::UIDartState( - TaskRunners task_runners, - TaskObserverAdd add_callback, - TaskObserverRemove remove_callback, +UIDartState::Context::Context(const TaskRunners& task_runners) + : task_runners(task_runners) {} + +UIDartState::Context::Context( + const TaskRunners& task_runners, fml::WeakPtr snapshot_delegate, - fml::WeakPtr hint_freed_delegate, fml::WeakPtr io_manager, - fml::RefPtr skia_unref_queue, + fml::RefPtr unref_queue, fml::WeakPtr image_decoder, + fml::WeakPtr image_generator_registry, std::string advisory_script_uri, std::string advisory_script_entrypoint, + std::shared_ptr volatile_path_tracker) + : task_runners(task_runners), + snapshot_delegate(snapshot_delegate), + io_manager(io_manager), + unref_queue(unref_queue), + image_decoder(image_decoder), + image_generator_registry(image_generator_registry), + advisory_script_uri(advisory_script_uri), + advisory_script_entrypoint(advisory_script_entrypoint), + volatile_path_tracker(volatile_path_tracker) {} + +UIDartState::UIDartState( + TaskObserverAdd add_callback, + TaskObserverRemove remove_callback, std::string logger_prefix, UnhandledExceptionCallback unhandled_exception_callback, + LogMessageCallback log_message_callback, std::shared_ptr isolate_name_server, - bool is_root_isolate) - : task_runners_(std::move(task_runners)), - add_callback_(std::move(add_callback)), + bool is_root_isolate, + bool enable_skparagraph, + bool enable_display_list, + const UIDartState::Context& context) + : add_callback_(std::move(add_callback)), remove_callback_(std::move(remove_callback)), - snapshot_delegate_(std::move(snapshot_delegate)), - hint_freed_delegate_(std::move(hint_freed_delegate)), - io_manager_(std::move(io_manager)), - skia_unref_queue_(std::move(skia_unref_queue)), - image_decoder_(std::move(image_decoder)), - advisory_script_uri_(std::move(advisory_script_uri)), - advisory_script_entrypoint_(std::move(advisory_script_entrypoint)), logger_prefix_(std::move(logger_prefix)), is_root_isolate_(is_root_isolate), unhandled_exception_callback_(unhandled_exception_callback), - isolate_name_server_(std::move(isolate_name_server)) { + log_message_callback_(log_message_callback), + isolate_name_server_(std::move(isolate_name_server)), + enable_skparagraph_(enable_skparagraph), + enable_display_list_(enable_display_list), + context_(std::move(context)) { AddOrRemoveTaskObserver(true /* add */); } @@ -50,19 +76,19 @@ UIDartState::~UIDartState() { } const std::string& UIDartState::GetAdvisoryScriptURI() const { - return advisory_script_uri_; + return context_.advisory_script_uri; } const std::string& UIDartState::GetAdvisoryScriptEntrypoint() const { - return advisory_script_entrypoint_; + return context_.advisory_script_entrypoint; } void UIDartState::DidSetIsolate() { main_port_ = Dart_GetMainPortId(); std::ostringstream debug_name; // main.dart$main-1234 - debug_name << advisory_script_uri_ << "$" << advisory_script_entrypoint_ - << "-" << main_port_; + debug_name << context_.advisory_script_uri << "$" + << context_.advisory_script_entrypoint << "-" << main_port_; SetDebugName(debug_name.str()); } @@ -95,15 +121,20 @@ void UIDartState::SetPlatformConfiguration( } const TaskRunners& UIDartState::GetTaskRunners() const { - return task_runners_; + return context_.task_runners; } fml::WeakPtr UIDartState::GetIOManager() const { - return io_manager_; + return context_.io_manager; } fml::RefPtr UIDartState::GetSkiaUnrefQueue() const { - return skia_unref_queue_; + return context_.unref_queue; +} + +std::shared_ptr UIDartState::GetVolatilePathTracker() + const { + return context_.volatile_path_tracker; } void UIDartState::ScheduleMicrotask(Dart_Handle closure) { @@ -119,7 +150,7 @@ void UIDartState::FlushMicrotasksNow() { } void UIDartState::AddOrRemoveTaskObserver(bool add) { - auto task_runner = task_runners_.GetUITaskRunner(); + auto task_runner = context_.task_runners.GetUITaskRunner(); if (!task_runner) { // This may happen in case the isolate has no thread affinity (for example, // the service isolate). @@ -135,22 +166,23 @@ void UIDartState::AddOrRemoveTaskObserver(bool add) { } fml::WeakPtr UIDartState::GetSnapshotDelegate() const { - return snapshot_delegate_; -} - -fml::WeakPtr UIDartState::GetHintFreedDelegate() const { - return hint_freed_delegate_; + return context_.snapshot_delegate; } fml::WeakPtr UIDartState::GetResourceContext() const { - if (!io_manager_) { + if (!context_.io_manager) { return {}; } - return io_manager_->GetResourceContext(); + return context_.io_manager->GetResourceContext(); } fml::WeakPtr UIDartState::GetImageDecoder() const { - return image_decoder_; + return context_.image_decoder; +} + +fml::WeakPtr UIDartState::GetImageGeneratorRegistry() + const { + return context_.image_generator_registry; } std::shared_ptr UIDartState::GetIsolateNameServer() const { @@ -178,4 +210,38 @@ void UIDartState::ReportUnhandledException(const std::string& error, << stack_trace; } +void UIDartState::LogMessage(const std::string& tag, + const std::string& message) const { + if (log_message_callback_) { + log_message_callback_(tag, message); + } else { + // Fall back to previous behavior if unspecified. +#if defined(OS_ANDROID) + __android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s", + (int)message.size(), message.c_str()); +#elif defined(OS_IOS) + std::stringstream stream; + if (tag.size() > 0) { + stream << tag << ": "; + } + stream << message; + std::string log = stream.str(); + syslog(1 /* LOG_ALERT */, "%.*s", (int)log.size(), log.c_str()); +#else + if (tag.size() > 0) { + std::cout << tag << ": "; + } + std::cout << message << std::endl; +#endif + } +} + +bool UIDartState::enable_skparagraph() const { + return enable_skparagraph_; +} + +bool UIDartState::enable_display_list() const { + return enable_display_list_; +} + } // namespace flutter diff --git a/lib/ui/ui_dart_state.h b/lib/ui/ui_dart_state.h index d5c22ffa3ff70..f79d584ad0090 100644 --- a/lib/ui/ui_dart_state.h +++ b/lib/ui/ui_dart_state.h @@ -15,11 +15,11 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/synchronization/waitable_event.h" -#include "flutter/lib/ui/hint_freed_delegate.h" #include "flutter/lib/ui/io_manager.h" #include "flutter/lib/ui/isolate_name_server/isolate_name_server.h" #include "flutter/lib/ui/painting/image_decoder.h" #include "flutter/lib/ui/snapshot_delegate.h" +#include "flutter/lib/ui/volatile_path_tracker.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/tonic/dart_microtask_queue.h" @@ -28,12 +28,69 @@ namespace flutter { class FontSelector; +class ImageGeneratorRegistry; class PlatformConfiguration; class UIDartState : public tonic::DartState { public: static UIDartState* Current(); + /// @brief The subset of state which is owned by the shell or engine + /// and passed through the RuntimeController into DartIsolates. + /// If a shell-owned resource needs to be exposed to the framework via + /// UIDartState, a pointer to the resource can be added to this + /// struct with appropriate default construction. + struct Context { + Context(const TaskRunners& task_runners); + + Context(const TaskRunners& task_runners, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, + fml::RefPtr unref_queue, + fml::WeakPtr image_decoder, + fml::WeakPtr image_generator_registry, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + std::shared_ptr volatile_path_tracker); + + /// The task runners used by the shell hosting this runtime controller. This + /// may be used by the isolate to scheduled asynchronous texture uploads or + /// post tasks to the platform task runner. + const TaskRunners task_runners; + + /// The snapshot delegate used by the + /// isolate to gather raster snapshots + /// of Flutter view hierarchies. + fml::WeakPtr snapshot_delegate; + + /// The IO manager used by the isolate for asynchronous texture uploads. + fml::WeakPtr io_manager; + + /// The unref queue used by the isolate to collect resources that may + /// reference resources on the GPU. + fml::RefPtr unref_queue; + + /// The image decoder. + fml::WeakPtr image_decoder; + + /// Cascading registry of image generator builders. Given compressed image + /// bytes as input, this is used to find and create image generators, which + /// can then be used for image decoding. + fml::WeakPtr image_generator_registry; + + /// The advisory script URI (only used for debugging). This does not affect + /// the code being run in the isolate in any way. + std::string advisory_script_uri; + + /// The advisory script entrypoint (only used for debugging). This does not + /// affect the code being run in the isolate in any way. The isolate must be + /// transitioned to the running state explicitly by the caller. + std::string advisory_script_entrypoint; + + /// Cache for tracking path volatility. + std::shared_ptr volatile_path_tracker; + }; + Dart_Port main_port() const { return main_port_; } // Root isolate of the VM application bool IsRootIsolate() const { return is_root_isolate_; } @@ -59,14 +116,16 @@ class UIDartState : public tonic::DartState { fml::RefPtr GetSkiaUnrefQueue() const; - fml::WeakPtr GetSnapshotDelegate() const; + std::shared_ptr GetVolatilePathTracker() const; - fml::WeakPtr GetHintFreedDelegate() const; + fml::WeakPtr GetSnapshotDelegate() const; fml::WeakPtr GetResourceContext() const; fml::WeakPtr GetImageDecoder() const; + fml::WeakPtr GetImageGeneratorRegistry() const; + std::shared_ptr GetIsolateNameServer() const; tonic::DartErrorHandleType GetLastError(); @@ -74,6 +133,18 @@ class UIDartState : public tonic::DartState { void ReportUnhandledException(const std::string& error, const std::string& stack_trace); + // Logs `print` messages from the application via an embedder-specified + // logging mechanism. + // + // @param[in] tag A component name or tag that identifies the logging + // application. + // @param[in] message The message to be logged. + void LogMessage(const std::string& tag, const std::string& message) const; + + bool enable_skparagraph() const; + + bool enable_display_list() const; + template static flutter::SkiaGPUObject CreateGPUObject(sk_sp object) { if (!object) { @@ -86,20 +157,16 @@ class UIDartState : public tonic::DartState { }; protected: - UIDartState(TaskRunners task_runners, - TaskObserverAdd add_callback, + UIDartState(TaskObserverAdd add_callback, TaskObserverRemove remove_callback, - fml::WeakPtr snapshot_delegate, - fml::WeakPtr hint_freed_delegate, - fml::WeakPtr io_manager, - fml::RefPtr skia_unref_queue, - fml::WeakPtr image_decoder, - std::string advisory_script_uri, - std::string advisory_script_entrypoint, std::string logger_prefix, UnhandledExceptionCallback unhandled_exception_callback, + LogMessageCallback log_message_callback, std::shared_ptr isolate_name_server, - bool is_root_isolate_); + bool is_root_isolate_, + bool enable_skparagraph, + bool enable_display_list, + const UIDartState::Context& context); ~UIDartState() override; @@ -113,16 +180,8 @@ class UIDartState : public tonic::DartState { private: void DidSetIsolate() override; - const TaskRunners task_runners_; const TaskObserverAdd add_callback_; const TaskObserverRemove remove_callback_; - fml::WeakPtr snapshot_delegate_; - fml::WeakPtr hint_freed_delegate_; - fml::WeakPtr io_manager_; - fml::RefPtr skia_unref_queue_; - fml::WeakPtr image_decoder_; - const std::string advisory_script_uri_; - const std::string advisory_script_entrypoint_; const std::string logger_prefix_; Dart_Port main_port_ = ILLEGAL_PORT; const bool is_root_isolate_; @@ -130,7 +189,11 @@ class UIDartState : public tonic::DartState { std::unique_ptr platform_configuration_; tonic::DartMicrotaskQueue microtask_queue_; UnhandledExceptionCallback unhandled_exception_callback_; + LogMessageCallback log_message_callback_; const std::shared_ptr isolate_name_server_; + const bool enable_skparagraph_; + const bool enable_display_list_; + UIDartState::Context context_; void AddOrRemoveTaskObserver(bool add); }; diff --git a/lib/ui/volatile_path_tracker.cc b/lib/ui/volatile_path_tracker.cc new file mode 100644 index 0000000000000..b82012bfa30a8 --- /dev/null +++ b/lib/ui/volatile_path_tracker.cc @@ -0,0 +1,85 @@ +// 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/lib/ui/volatile_path_tracker.h" + +namespace flutter { + +VolatilePathTracker::VolatilePathTracker( + fml::RefPtr ui_task_runner, + bool enabled) + : ui_task_runner_(ui_task_runner), enabled_(enabled) {} + +void VolatilePathTracker::Insert(std::shared_ptr path) { + FML_DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); + FML_DCHECK(path); + FML_DCHECK(path->path.isVolatile()); + if (!enabled_) { + path->path.setIsVolatile(false); + return; + } + paths_.insert(path); +} + +void VolatilePathTracker::Erase(std::shared_ptr path) { + if (!enabled_) { + return; + } + FML_DCHECK(path); + if (ui_task_runner_->RunsTasksOnCurrentThread()) { + paths_.erase(path); + return; + } + + std::scoped_lock lock(paths_to_remove_mutex_); + needs_drain_ = true; + paths_to_remove_.push_back(path); +} + +void VolatilePathTracker::OnFrame() { + FML_DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); + if (!enabled_) { + return; + } + std::string total_count = std::to_string(paths_.size()); + TRACE_EVENT1("flutter", "VolatilePathTracker::OnFrame", "total_count", + total_count.c_str()); + + Drain(); + + std::set> surviving_paths_; + for (const std::shared_ptr& path : paths_) { + path->frame_count++; + if (path->frame_count >= kFramesOfVolatility) { + path->path.setIsVolatile(false); + path->tracking_volatility = false; + } else { + surviving_paths_.insert(path); + } + } + paths_.swap(surviving_paths_); + std::string post_removal_count = std::to_string(paths_.size()); + TRACE_EVENT_INSTANT1("flutter", "VolatilePathTracker::OnFrame", + "remaining_count", post_removal_count.c_str()); +} + +void VolatilePathTracker::Drain() { + if (needs_drain_) { + TRACE_EVENT0("flutter", "VolatilePathTracker::Drain"); + std::deque> paths_to_remove; + { + std::scoped_lock lock(paths_to_remove_mutex_); + paths_to_remove.swap(paths_to_remove_); + needs_drain_ = false; + } + std::string count = std::to_string(paths_to_remove.size()); + TRACE_EVENT_INSTANT1("flutter", "VolatilePathTracker::Drain", "count", + count.c_str()); + for (auto& path : paths_to_remove) { + paths_.erase(path); + } + } +} + +} // namespace flutter diff --git a/lib/ui/volatile_path_tracker.h b/lib/ui/volatile_path_tracker.h new file mode 100644 index 0000000000000..40f28311a008a --- /dev/null +++ b/lib/ui/volatile_path_tracker.h @@ -0,0 +1,82 @@ +// 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_LIB_VOLATILE_PATH_TRACKER_H_ +#define FLUTTER_LIB_VOLATILE_PATH_TRACKER_H_ + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/trace_event.h" +#include "third_party/skia/include/core/SkPath.h" + +namespace flutter { + +/// A cache for paths drawn from dart:ui. +/// +/// Whenever a flutter::CanvasPath is created, it must Insert an entry into +/// this cache. Whenever a frame is drawn, the shell must call OnFrame. The +/// cache will flip the volatility bit on the SkPath and remove it from the +/// cache. If the Dart object is released, Erase must be called to avoid +/// tracking a path that is no longer referenced in Dart code. +/// +/// Enabling this cache may cause difficult to predict minor pixel differences +/// when paths are rendered. If deterministic rendering is needed, e.g. for a +/// screen diffing test, this class will not cache any paths and will +/// automatically set the volatility of the path to false. +class VolatilePathTracker { + public: + /// The fields of this struct must only accessed on the UI task runner. + struct TrackedPath { + bool tracking_volatility = false; + int frame_count = 0; + SkPath path; + }; + + VolatilePathTracker(fml::RefPtr ui_task_runner, + bool enabled); + + static constexpr int kFramesOfVolatility = 2; + + // Starts tracking a path. + // Must be called from the UI task runner. + // + // Callers should only insert paths that are currently volatile. + void Insert(std::shared_ptr path); + + // Removes a path from tracking. + // + // May be called from any thread. + void Erase(std::shared_ptr path); + + // Called by the shell at the end of a frame after notifying Dart about idle + // time. + // + // This method will flip the volatility bit to false for any paths that have + // survived the |kFramesOfVolatility|. + // + // Must be called from the UI task runner. + void OnFrame(); + + bool enabled() const { return enabled_; } + + private: + fml::RefPtr ui_task_runner_; + std::atomic_bool needs_drain_ = false; + std::mutex paths_to_remove_mutex_; + std::deque> paths_to_remove_; + std::set> paths_; + bool enabled_ = true; + + void Drain(); + + FML_DISALLOW_COPY_AND_ASSIGN(VolatilePathTracker); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_VOLATILE_PATH_TRACKER_H_ diff --git a/lib/ui/window.dart b/lib/ui/window.dart index a39c5407ad45e..8ddb931d184ea 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -2,617 +2,75 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// @dart = 2.12 part of dart.ui; -/// Signature of callbacks that have no arguments and return no data. -typedef VoidCallback = void Function(); - -/// Signature for [Window.onBeginFrame]. -typedef FrameCallback = void Function(Duration duration); - -/// Signature for [Window.onReportTimings]. -/// -/// {@template dart.ui.TimingsCallback.list} -/// The callback takes a list of [FrameTiming] because it may not be -/// immediately triggered after each frame. Instead, Flutter tries to batch -/// frames together and send all their timings at once to decrease the -/// overhead (as this is available in the release mode). The list is sorted in -/// ascending order of time (earliest frame first). The timing of any frame -/// will be sent within about 1 second (100ms if in the profile/debug mode) -/// even if there are no later frames to batch. The timing of the first frame -/// will be sent immediately without batching. -/// {@endtemplate} -typedef TimingsCallback = void Function(List timings); - -/// Signature for [Window.onPointerDataPacket]. -typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); - -/// Signature for [Window.onSemanticsAction]. -typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); - -/// Signature for responses to platform messages. -/// -/// Used as a parameter to [Window.sendPlatformMessage] and -/// [Window.onPlatformMessage]. -typedef PlatformMessageResponseCallback = void Function(ByteData? data); - -/// Signature for [Window.onPlatformMessage]. -typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); - -// Signature for _setNeedsReportTimings. -typedef _SetNeedsReportTimingsFunc = void Function(bool value); - -/// Various important time points in the lifetime of a frame. -/// -/// [FrameTiming] records a timestamp of each phase for performance analysis. -enum FramePhase { - /// The timestamp of the vsync signal given by the operating system. - /// - /// See also [FrameTiming.vsyncOverhead]. - vsyncStart, - - /// When the UI thread starts building a frame. - /// - /// See also [FrameTiming.buildDuration]. - buildStart, - - /// When the UI thread finishes building a frame. - /// - /// See also [FrameTiming.buildDuration]. - buildFinish, - - /// When the raster thread starts rasterizing a frame. - /// - /// See also [FrameTiming.rasterDuration]. - rasterStart, - - /// When the raster thread finishes rasterizing a frame. - /// - /// See also [FrameTiming.rasterDuration]. - rasterFinish, -} - -/// Time-related performance metrics of a frame. -/// -/// If you're using the whole Flutter framework, please use -/// [SchedulerBinding.addTimingsCallback] to get this. It's preferred over using -/// [Window.onReportTimings] directly because -/// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. If -/// [SchedulerBinding] is unavailable, then see [Window.onReportTimings] for how -/// to get this. -/// -/// The metrics in debug mode (`flutter run` without any flags) may be very -/// different from those in profile and release modes due to the debug overhead. -/// Therefore it's recommended to only monitor and analyze performance metrics -/// in profile and release modes. -class FrameTiming { - /// Construct [FrameTiming] with raw timestamps in microseconds. - /// - /// This constructor is used for unit test only. Real [FrameTiming]s should - /// be retrieved from [Window.onReportTimings]. - factory FrameTiming({ - required int vsyncStart, - required int buildStart, - required int buildFinish, - required int rasterStart, - required int rasterFinish, - }) { - return FrameTiming._([ - vsyncStart, - buildStart, - buildFinish, - rasterStart, - rasterFinish - ]); - } - - /// Construct [FrameTiming] with raw timestamps in microseconds. - /// - /// List [timestamps] must have the same number of elements as - /// [FramePhase.values]. - /// - /// This constructor is usually only called by the Flutter engine, or a test. - /// To get the [FrameTiming] of your app, see [Window.onReportTimings]. - FrameTiming._(List timestamps) - : assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps; - - /// This is a raw timestamp in microseconds from some epoch. The epoch in all - /// [FrameTiming] is the same, but it may not match [DateTime]'s epoch. - int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; - - Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); - - /// The duration to build the frame on the UI thread. - /// - /// The build starts approximately when [Window.onBeginFrame] is called. The - /// [Duration] in the [Window.onBeginFrame] callback is exactly the - /// `Duration(microseconds: timestampInMicroseconds(FramePhase.buildStart))`. - /// - /// The build finishes when [Window.render] is called. - /// - /// {@template dart.ui.FrameTiming.fps_smoothness_milliseconds} - /// To ensure smooth animations of X fps, this should not exceed 1000/X - /// milliseconds. - /// {@endtemplate} - /// {@template dart.ui.FrameTiming.fps_milliseconds} - /// That's about 16ms for 60fps, and 8ms for 120fps. - /// {@endtemplate} - Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); - - /// The duration to rasterize the frame on the raster thread. - /// - /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} - /// {@macro dart.ui.FrameTiming.fps_milliseconds} - Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); - - /// The duration between receiving the vsync signal and starting building the - /// frame. - Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); - - /// The timespan between vsync start and raster finish. - /// - /// To achieve the lowest latency on an X fps display, this should not exceed - /// 1000/X milliseconds. - /// {@macro dart.ui.FrameTiming.fps_milliseconds} - /// - /// See also [vsyncOverhead], [buildDuration] and [rasterDuration]. - Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); - - final List _timestamps; // in microseconds - - String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms'; - - @override - String toString() { - return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, vsyncOverhead: ${_formatMS(vsyncOverhead)}, totalSpan: ${_formatMS(totalSpan)})'; - } -} - -/// States that an application can be in. -/// -/// The values below describe notifications from the operating system. -/// Applications should not expect to always receive all possible -/// notifications. For example, if the users pulls out the battery from the -/// device, no notification will be sent before the application is suddenly -/// terminated, along with the rest of the operating system. -/// -/// See also: -/// -/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state -/// from the widgets layer. -enum AppLifecycleState { - /// The application is visible and responding to user input. - resumed, - - /// The application is in an inactive state and is not receiving user input. - /// - /// On iOS, this state corresponds to an app or the Flutter host view running - /// in the foreground inactive state. Apps transition to this state when in - /// a phone call, responding to a TouchID request, when entering the app - /// switcher or the control center, or when the UIViewController hosting the - /// Flutter app is transitioning. - /// - /// On Android, this corresponds to an app or the Flutter host view running - /// in the foreground inactive state. Apps transition to this state when - /// another activity is focused, such as a split-screen app, a phone call, - /// a picture-in-picture app, a system dialog, or another window. - /// - /// Apps in this state should assume that they may be [paused] at any time. - inactive, - - /// The application is not currently visible to the user, not responding to - /// user input, and running in the background. - /// - /// When the application is in this state, the engine will not call the - /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. - paused, - - /// The application is still hosted on a flutter engine but is detached from - /// any host views. - /// - /// When the application is in this state, the engine is running without - /// a view. It can either be in the progress of attaching a view when engine - /// was first initializes, or after the view being destroyed due to a Navigator - /// pop. - detached, -} - -/// A representation of distances for each of the four edges of a rectangle, -/// used to encode the view insets and padding that applications should place -/// around their user interface, as exposed by [Window.viewInsets] and -/// [Window.padding]. View insets and padding are preferably read via -/// [MediaQuery.of]. -/// -/// For a generic class that represents distances around a rectangle, see the -/// [EdgeInsets] class. -/// -/// See also: -/// -/// * [WidgetsBindingObserver], for a widgets layer mechanism to receive -/// notifications when the padding changes. -/// * [MediaQuery.of], for the preferred mechanism for accessing these values. -/// * [Scaffold], which automatically applies the padding in material design -/// applications. -class WindowPadding { - const WindowPadding._({ required this.left, required this.top, required this.right, required this.bottom }); - - /// The distance from the left edge to the first unpadded pixel, in physical pixels. - final double left; - - /// The distance from the top edge to the first unpadded pixel, in physical pixels. - final double top; - - /// The distance from the right edge to the first unpadded pixel, in physical pixels. - final double right; - - /// The distance from the bottom edge to the first unpadded pixel, in physical pixels. - final double bottom; - - /// A window padding that has zeros for each edge. - static const WindowPadding zero = WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); - - @override - String toString() { - return 'WindowPadding(left: $left, top: $top, right: $right, bottom: $bottom)'; - } -} - -/// An identifier used to select a user's language and formatting preferences. -/// -/// This represents a [Unicode Language -/// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) -/// (i.e. without Locale extensions), except variants are not supported. +/// A view into which a Flutter [Scene] is drawn. /// -/// Locales are canonicalized according to the "preferred value" entries in the -/// [IANA Language Subtag -/// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). -/// For example, `const Locale('he')` and `const Locale('iw')` are equal and -/// both have the [languageCode] `he`, because `iw` is a deprecated language -/// subtag that was replaced by the subtag `he`. -/// -/// See also: -/// -/// * [Window.locale], which specifies the system's currently selected -/// [Locale]. -class Locale { - /// Creates a new Locale object. The first argument is the - /// primary language subtag, the second is the region (also - /// referred to as 'country') subtag. - /// - /// For example: - /// - /// ```dart - /// const Locale swissFrench = Locale('fr', 'CH'); - /// const Locale canadianFrench = Locale('fr', 'CA'); - /// ``` - /// - /// The primary language subtag must not be null. The region subtag is - /// optional. When there is no region/country subtag, the parameter should - /// be omitted or passed `null` instead of an empty-string. - /// - /// The subtag values are _case sensitive_ and must be one of the valid - /// subtags according to CLDR supplemental data: - /// [language](http://unicode.org/cldr/latest/common/validity/language.xml), - /// [region](http://unicode.org/cldr/latest/common/validity/region.xml). The - /// primary language subtag must be at least two and at most eight lowercase - /// letters, but not four letters. The region region subtag must be two - /// uppercase letters or three digits. See the [Unicode Language - /// Identifier](https://www.unicode.org/reports/tr35/#Unicode_language_identifier) - /// specification. - /// - /// Validity is not checked by default, but some methods may throw away - /// invalid data. - /// - /// See also: - /// - /// * [Locale.fromSubtags], which also allows a [scriptCode] to be - /// specified. - const Locale( - this._languageCode, [ - this._countryCode, - ]) : assert(_languageCode != null), // ignore: unnecessary_null_comparison - assert(_languageCode != ''), - scriptCode = null; - - /// Creates a new Locale object. - /// - /// The keyword arguments specify the subtags of the Locale. - /// - /// The subtag values are _case sensitive_ and must be valid subtags according - /// to CLDR supplemental data: - /// [language](http://unicode.org/cldr/latest/common/validity/language.xml), - /// [script](http://unicode.org/cldr/latest/common/validity/script.xml) and - /// [region](http://unicode.org/cldr/latest/common/validity/region.xml) for - /// each of languageCode, scriptCode and countryCode respectively. - /// - /// The [countryCode] subtag is optional. When there is no country subtag, - /// the parameter should be omitted or passed `null` instead of an empty-string. - /// - /// Validity is not checked by default, but some methods may throw away - /// invalid data. - const Locale.fromSubtags({ - String languageCode = 'und', - this.scriptCode, - String? countryCode, - }) : assert(languageCode != null), // ignore: unnecessary_null_comparison - assert(languageCode != ''), - _languageCode = languageCode, - assert(scriptCode != ''), - assert(countryCode != ''), - _countryCode = countryCode; - - /// The primary language subtag for the locale. - /// - /// This must not be null. It may be 'und', representing 'undefined'. - /// - /// This is expected to be string registered in the [IANA Language Subtag - /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) - /// with the type "language". The string specified must match the case of the - /// string in the registry. - /// - /// Language subtags that are deprecated in the registry and have a preferred - /// code are changed to their preferred code. For example, `const - /// Locale('he')` and `const Locale('iw')` are equal, and both have the - /// [languageCode] `he`, because `iw` is a deprecated language subtag that was - /// replaced by the subtag `he`. - /// - /// This must be a valid Unicode Language subtag as listed in [Unicode CLDR - /// supplemental - /// data](http://unicode.org/cldr/latest/common/validity/language.xml). - /// - /// See also: - /// - /// * [Locale.fromSubtags], which describes the conventions for creating - /// [Locale] objects. - String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; - final String _languageCode; - - // This map is generated by //flutter/tools/gen_locale.dart - // Mappings generated for language subtag registry as of 2019-02-27. - static const Map _deprecatedLanguageSubtagMap = { - 'in': 'id', // Indonesian; deprecated 1989-01-01 - 'iw': 'he', // Hebrew; deprecated 1989-01-01 - 'ji': 'yi', // Yiddish; deprecated 1989-01-01 - 'jw': 'jv', // Javanese; deprecated 2001-08-13 - 'mo': 'ro', // Moldavian, Moldovan; deprecated 2008-11-22 - 'aam': 'aas', // Aramanik; deprecated 2015-02-12 - 'adp': 'dz', // Adap; deprecated 2015-02-12 - 'aue': 'ktz', // ǂKxʼauǁʼein; deprecated 2015-02-12 - 'ayx': 'nun', // Ayi (China); deprecated 2011-08-16 - 'bgm': 'bcg', // Baga Mboteni; deprecated 2016-05-30 - 'bjd': 'drl', // Bandjigali; deprecated 2012-08-12 - 'ccq': 'rki', // Chaungtha; deprecated 2012-08-12 - 'cjr': 'mom', // Chorotega; deprecated 2010-03-11 - 'cka': 'cmr', // Khumi Awa Chin; deprecated 2012-08-12 - 'cmk': 'xch', // Chimakum; deprecated 2010-03-11 - 'coy': 'pij', // Coyaima; deprecated 2016-05-30 - 'cqu': 'quh', // Chilean Quechua; deprecated 2016-05-30 - 'drh': 'khk', // Darkhat; deprecated 2010-03-11 - 'drw': 'prs', // Darwazi; deprecated 2010-03-11 - 'gav': 'dev', // Gabutamon; deprecated 2010-03-11 - 'gfx': 'vaj', // Mangetti Dune ǃXung; deprecated 2015-02-12 - 'ggn': 'gvr', // Eastern Gurung; deprecated 2016-05-30 - 'gti': 'nyc', // Gbati-ri; deprecated 2015-02-12 - 'guv': 'duz', // Gey; deprecated 2016-05-30 - 'hrr': 'jal', // Horuru; deprecated 2012-08-12 - 'ibi': 'opa', // Ibilo; deprecated 2012-08-12 - 'ilw': 'gal', // Talur; deprecated 2013-09-10 - 'jeg': 'oyb', // Jeng; deprecated 2017-02-23 - 'kgc': 'tdf', // Kasseng; deprecated 2016-05-30 - 'kgh': 'kml', // Upper Tanudan Kalinga; deprecated 2012-08-12 - 'koj': 'kwv', // Sara Dunjo; deprecated 2015-02-12 - 'krm': 'bmf', // Krim; deprecated 2017-02-23 - 'ktr': 'dtp', // Kota Marudu Tinagas; deprecated 2016-05-30 - 'kvs': 'gdj', // Kunggara; deprecated 2016-05-30 - 'kwq': 'yam', // Kwak; deprecated 2015-02-12 - 'kxe': 'tvd', // Kakihum; deprecated 2015-02-12 - 'kzj': 'dtp', // Coastal Kadazan; deprecated 2016-05-30 - 'kzt': 'dtp', // Tambunan Dusun; deprecated 2016-05-30 - 'lii': 'raq', // Lingkhim; deprecated 2015-02-12 - 'lmm': 'rmx', // Lamam; deprecated 2014-02-28 - 'meg': 'cir', // Mea; deprecated 2013-09-10 - 'mst': 'mry', // Cataelano Mandaya; deprecated 2010-03-11 - 'mwj': 'vaj', // Maligo; deprecated 2015-02-12 - 'myt': 'mry', // Sangab Mandaya; deprecated 2010-03-11 - 'nad': 'xny', // Nijadali; deprecated 2016-05-30 - 'ncp': 'kdz', // Ndaktup; deprecated 2018-03-08 - 'nnx': 'ngv', // Ngong; deprecated 2015-02-12 - 'nts': 'pij', // Natagaimas; deprecated 2016-05-30 - 'oun': 'vaj', // ǃOǃung; deprecated 2015-02-12 - 'pcr': 'adx', // Panang; deprecated 2013-09-10 - 'pmc': 'huw', // Palumata; deprecated 2016-05-30 - 'pmu': 'phr', // Mirpur Panjabi; deprecated 2015-02-12 - 'ppa': 'bfy', // Pao; deprecated 2016-05-30 - 'ppr': 'lcq', // Piru; deprecated 2013-09-10 - 'pry': 'prt', // Pray 3; deprecated 2016-05-30 - 'puz': 'pub', // Purum Naga; deprecated 2014-02-28 - 'sca': 'hle', // Sansu; deprecated 2012-08-12 - 'skk': 'oyb', // Sok; deprecated 2017-02-23 - 'tdu': 'dtp', // Tempasuk Dusun; deprecated 2016-05-30 - 'thc': 'tpo', // Tai Hang Tong; deprecated 2016-05-30 - 'thx': 'oyb', // The; deprecated 2015-02-12 - 'tie': 'ras', // Tingal; deprecated 2011-08-16 - 'tkk': 'twm', // Takpa; deprecated 2011-08-16 - 'tlw': 'weo', // South Wemale; deprecated 2012-08-12 - 'tmp': 'tyj', // Tai Mène; deprecated 2016-05-30 - 'tne': 'kak', // Tinoc Kallahan; deprecated 2016-05-30 - 'tnf': 'prs', // Tangshewi; deprecated 2010-03-11 - 'tsf': 'taj', // Southwestern Tamang; deprecated 2015-02-12 - 'uok': 'ema', // Uokha; deprecated 2015-02-12 - 'xba': 'cax', // Kamba (Brazil); deprecated 2016-05-30 - 'xia': 'acn', // Xiandao; deprecated 2013-09-10 - 'xkh': 'waw', // Karahawyana; deprecated 2016-05-30 - 'xsj': 'suj', // Subi; deprecated 2015-02-12 - 'ybd': 'rki', // Yangbye; deprecated 2012-08-12 - 'yma': 'lrr', // Yamphe; deprecated 2012-08-12 - 'ymt': 'mtm', // Mator-Taygi-Karagas; deprecated 2015-02-12 - 'yos': 'zom', // Yos; deprecated 2013-09-10 - 'yuu': 'yug', // Yugh; deprecated 2014-02-28 - }; - - /// The script subtag for the locale. - /// - /// This may be null, indicating that there is no specified script subtag. - /// - /// This must be a valid Unicode Language Identifier script subtag as listed - /// in [Unicode CLDR supplemental - /// data](http://unicode.org/cldr/latest/common/validity/script.xml). - /// - /// See also: - /// - /// * [Locale.fromSubtags], which describes the conventions for creating - /// [Locale] objects. - final String? scriptCode; - - /// The region subtag for the locale. - /// - /// This may be null, indicating that there is no specified region subtag. - /// - /// This is expected to be string registered in the [IANA Language Subtag - /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry) - /// with the type "region". The string specified must match the case of the - /// string in the registry. - /// - /// Region subtags that are deprecated in the registry and have a preferred - /// code are changed to their preferred code. For example, `const Locale('de', - /// 'DE')` and `const Locale('de', 'DD')` are equal, and both have the - /// [countryCode] `DE`, because `DD` is a deprecated language subtag that was - /// replaced by the subtag `DE`. - /// - /// See also: - /// - /// * [Locale.fromSubtags], which describes the conventions for creating - /// [Locale] objects. - String? get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; - final String? _countryCode; - - // This map is generated by //flutter/tools/gen_locale.dart - // Mappings generated for language subtag registry as of 2019-02-27. - static const Map _deprecatedRegionSubtagMap = { - 'BU': 'MM', // Burma; deprecated 1989-12-05 - 'DD': 'DE', // German Democratic Republic; deprecated 1990-10-30 - 'FX': 'FR', // Metropolitan France; deprecated 1997-07-14 - 'TP': 'TL', // East Timor; deprecated 2002-05-20 - 'YD': 'YE', // Democratic Yemen; deprecated 1990-08-14 - 'ZR': 'CD', // Zaire; deprecated 1997-07-14 - }; - - @override - bool operator ==(Object other) { - if (identical(this, other)) - return true; - if (other is! Locale) { - return false; - } - final String? countryCode = _countryCode; - final String? otherCountryCode = other.countryCode; - return other.languageCode == languageCode - && other.scriptCode == scriptCode // scriptCode cannot be '' - && (other.countryCode == countryCode // Treat '' as equal to null. - || otherCountryCode != null && otherCountryCode.isEmpty && countryCode == null - || countryCode != null && countryCode.isEmpty && other.countryCode == null); - } - - @override - int get hashCode => hashValues(languageCode, scriptCode, countryCode == '' ? null : countryCode); - - static Locale? _cachedLocale; - static String? _cachedLocaleString; - - /// Returns a string representing the locale. - /// - /// This identifier happens to be a valid Unicode Locale Identifier using - /// underscores as separator, however it is intended to be used for debugging - /// purposes only. For parseable results, use [toLanguageTag] instead. - @keepToString - @override - String toString() { - if (!identical(_cachedLocale, this)) { - _cachedLocale = this; - _cachedLocaleString = _rawToString('_'); - } - return _cachedLocaleString!; - } - - /// Returns a syntactically valid Unicode BCP47 Locale Identifier. - /// - /// Some examples of such identifiers: "en", "es-419", "hi-Deva-IN" and - /// "zh-Hans-CN". See http://www.unicode.org/reports/tr35/ for technical - /// details. - String toLanguageTag() => _rawToString('-'); - - String _rawToString(String separator) { - final StringBuffer out = StringBuffer(languageCode); - if (scriptCode != null && scriptCode!.isNotEmpty) - out.write('$separator$scriptCode'); - if (_countryCode != null && _countryCode!.isNotEmpty) - out.write('$separator$countryCode'); - return out.toString(); - } -} - -/// The most basic interface to the host operating system's user interface. -/// -/// It exposes the size of the display, the core scheduler API, the input event -/// callback, the graphics drawing API, and other such core services. -/// -/// There is a single Window instance in the system, which you can -/// obtain from `WidgetsBinding.instance.window`. -/// -/// There is also a [window] singleton object in `dart:ui` if `WidgetsBinding` -/// is unavailable. But we strongly advise to avoid statically referencing it. -/// See the document of [window] for more details of why it should be avoided. +/// Each [FlutterView] has its own layer tree that is rendered into an area +/// inside of a [FlutterWindow] whenever [render] is called with a [Scene]. /// /// ## Insets and Padding /// /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/widgets/window_padding.mp4} /// -/// In this diagram, the black areas represent system UI that the app cannot -/// draw over. The red area represents view padding that the application may not +/// In this illustration, the black areas represent system UI that the app +/// cannot draw over. The red area represents view padding that the view may not /// be able to detect gestures in and may not want to draw in. The grey area -/// represents the system keyboard, which can cover over the bottom view -/// padding when visible. +/// represents the system keyboard, which can cover over the bottom view padding +/// when visible. /// -/// The [Window.viewInsets] are the physical pixels which the operating +/// The [viewInsets] are the physical pixels which the operating /// system reserves for system UI, such as the keyboard, which would fully /// obscure any content drawn in that area. /// -/// The [Window.viewPadding] are the physical pixels on each side of the display -/// that may be partially obscured by system UI or by physical intrusions into -/// the display, such as an overscan region on a television or a "notch" on a -/// phone. Unlike the insets, these areas may have portions that show the user -/// application painted pixels without being obscured, such as a notch at the -/// top of a phone that covers only a subset of the area. Insets, on the other -/// hand, either partially or fully obscure the window, such as an opaque -/// keyboard or a partially transluscent statusbar, which cover an area without -/// gaps. +/// The [viewPadding] are the physical pixels on each side of the +/// display that may be partially obscured by system UI or by physical +/// intrusions into the display, such as an overscan region on a television or a +/// "notch" on a phone. Unlike the insets, these areas may have portions that +/// show the user view-painted pixels without being obscured, such as a +/// notch at the top of a phone that covers only a subset of the area. Insets, +/// on the other hand, either partially or fully obscure the window, such as an +/// opaque keyboard or a partially translucent status bar, which cover an area +/// without gaps. /// -/// The [Window.padding] property is computed from both [Window.viewInsets] and -/// [Window.viewPadding]. It will allow a view inset to consume view padding -/// where appropriate, such as when a phone's keyboard is covering the bottom -/// view padding and so "absorbs" it. +/// The [padding] property is computed from both +/// [viewInsets] and [viewPadding]. It will allow a +/// view inset to consume view padding where appropriate, such as when a phone's +/// keyboard is covering the bottom view padding and so "absorbs" it. /// /// Clients that want to position elements relative to the view padding -/// regardless of the view insets should use the [Window.viewPadding] property, -/// e.g. if you wish to draw a widget at the center of the screen with respect -/// to the iPhone "safe area" regardless of whether the keyboard is showing. +/// regardless of the view insets should use the [viewPadding] +/// property, e.g. if you wish to draw a widget at the center of the screen with +/// respect to the iPhone "safe area" regardless of whether the keyboard is +/// showing. /// -/// [Window.padding] is useful for clients that want to know how much padding -/// should be accounted for without concern for the current inset(s) state, e.g. -/// determining whether a gesture should be considered for scrolling purposes. -/// This value varies based on the current state of the insets. For example, a -/// visible keyboard will consume all gestures in the bottom part of the -/// [Window.viewPadding] anyway, so there is no need to account for that in the -/// [Window.padding], which is always safe to use for such calculations. -class Window { - Window._() { - _setNeedsReportTimings = _nativeSetNeedsReportTimings; - } +/// [padding] is useful for clients that want to know how much +/// padding should be accounted for without concern for the current inset(s) +/// state, e.g. determining whether a gesture should be considered for scrolling +/// purposes. This value varies based on the current state of the insets. For +/// example, a visible keyboard will consume all gestures in the bottom part of +/// the [viewPadding] anyway, so there is no need to account for +/// that in the [padding], which is always safe to use for such +/// calculations. +/// +/// See also: +/// +/// * [FlutterWindow], a special case of a [FlutterView] that is represented on +/// the platform as a separate window which can host other [FlutterView]s. +abstract class FlutterView { + /// The platform dispatcher that this view is registered with, and gets its + /// information from. + PlatformDispatcher get platformDispatcher; + + /// The configuration of this view. + ViewConfiguration get viewConfiguration; - /// The number of device pixels for each logical pixel. This number might not - /// be a power of two. Indeed, it might not even be an integer. For example, - /// the Nexus 6 has a device pixel ratio of 3.5. + /// The number of device pixels for each logical pixel for the screen this + /// view is displayed on. + /// + /// This number might not be a power of two. Indeed, it might not even be an + /// integer. For example, the Nexus 6 has a device pixel ratio of 3.5. /// /// Device pixels are also referred to as physical pixels. Logical pixels are /// also referred to as device-independent or resolution-independent pixels. @@ -633,39 +91,59 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double get devicePixelRatio => _devicePixelRatio; - double _devicePixelRatio = 1.0; + double get devicePixelRatio => viewConfiguration.devicePixelRatio; - /// The dimensions of the rectangle into which the application will be drawn, - /// in physical pixels. + /// The dimensions and location of the rectangle into which the scene rendered + /// in this view will be drawn on the screen, in physical pixels. /// /// When this changes, [onMetricsChanged] is called. /// - /// At startup, the size of the application window may not be known before Dart + /// At startup, the size and location of the view may not be known before Dart /// code runs. If this value is observed early in the application lifecycle, - /// it may report [Size.zero]. + /// it may report [Rect.zero]. /// /// This value does not take into account any on-screen keyboards or other /// system UI. The [padding] and [viewInsets] properties provide a view into - /// how much of each side of the application may be obscured by system UI. + /// how much of each side of the view may be obscured by system UI. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + Rect get physicalGeometry => viewConfiguration.geometry; + + /// The dimensions of the rectangle into which the scene rendered in this view + /// will be drawn on the screen, in physical pixels. + /// + /// When this changes, [onMetricsChanged] is called. + /// + /// At startup, the size of the view may not be known before Dart code runs. + /// If this value is observed early in the application lifecycle, it may + /// report [Size.zero]. + /// + /// This value does not take into account any on-screen keyboards or other + /// system UI. The [padding] and [viewInsets] properties provide information + /// about how much of each side of the view may be obscured by system UI. + /// + /// This value is the same as the `size` member of [physicalGeometry]. /// /// See also: /// + /// * [physicalGeometry], which reports the location of the view as well as + /// its size. /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - Size get physicalSize => _physicalSize; - Size _physicalSize = Size.zero; + Size get physicalSize => viewConfiguration.geometry.size; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but over which the operating system - /// will likely place system UI, such as the keyboard, that fully obscures - /// any content. + /// which the view can render, but over which the operating system will likely + /// place system UI, such as the keyboard, that fully obscures any content. /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [Window.viewInsets], [Window.viewPadding], - /// and [Window.padding] are described in more detail in the documentation for - /// [Window]. + /// The relationship between this [viewInsets], + /// [viewPadding], and [padding] are described in + /// more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -674,25 +152,24 @@ class Window { /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the view insets in material /// design applications. - WindowPadding get viewInsets => _viewInsets; - WindowPadding _viewInsets = WindowPadding.zero; + WindowPadding get viewInsets => viewConfiguration.viewInsets; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but which may be partially obscured by - /// system UI (such as the system notification area), or or physical - /// intrusions in the display (e.g. overscan regions on television screens or - /// phone sensor housings). + /// which the view can render, but which may be partially obscured by system + /// UI (such as the system notification area), or or physical intrusions in + /// the display (e.g. overscan regions on television screens or phone sensor + /// housings). /// - /// Unlike [Window.padding], this value does not change relative to - /// [Window.viewInsets]. For example, on an iPhone X, it will not change in - /// response to the soft keyboard being visible or hidden, whereas - /// [Window.padding] will. + /// Unlike [padding], this value does not change relative to + /// [viewInsets]. For example, on an iPhone X, it will not + /// change in response to the soft keyboard being visible or hidden, whereas + /// [padding] will. /// /// When this property changes, [onMetricsChanged] is called. /// - /// The relationship between this [Window.viewInsets], [Window.viewPadding], - /// and [Window.padding] are described in more detail in the documentation for - /// [Window]. + /// The relationship between this [viewInsets], + /// [viewPadding], and [padding] are described in + /// more detail in the documentation for [FlutterView]. /// /// See also: /// @@ -701,12 +178,11 @@ class Window { /// * [MediaQuery.of], a simpler mechanism for the same. /// * [Scaffold], which automatically applies the padding in material design /// applications. - WindowPadding get viewPadding => _viewPadding; - WindowPadding _viewPadding = WindowPadding.zero; + WindowPadding get viewPadding => viewConfiguration.viewPadding; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but where the operating system will - /// consume input gestures for the sake of system navigation. + /// which the view can render, but where the operating system will consume + /// input gestures for the sake of system navigation. /// /// For example, an operating system might use the vertical edges of the /// screen, where swiping inwards from the edges takes users backward @@ -719,85 +195,158 @@ class Window { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. /// * [MediaQuery.of], a simpler mechanism for the same. - WindowPadding get systemGestureInsets => _systemGestureInsets; - WindowPadding _systemGestureInsets = WindowPadding.zero; + WindowPadding get systemGestureInsets => viewConfiguration.systemGestureInsets; /// The number of physical pixels on each side of the display rectangle into - /// which the application can render, but which may be partially obscured by - /// system UI (such as the system notification area), or or physical - /// intrusions in the display (e.g. overscan regions on television screens or - /// phone sensor housings). - /// - /// This value is calculated by taking - /// `max(0.0, Window.viewPadding - Window.viewInsets)`. This will treat a - /// system IME that increases the bottom inset as consuming that much of the - /// bottom padding. For example, on an iPhone X, [EdgeInsets.bottom] of - /// [Window.padding] is the same as [EdgeInsets.bottom] of - /// [Window.viewPadding] when the soft keyboard is not drawn (to account for - /// the bottom soft button area), but will be `0.0` when the soft keyboard is - /// visible. + /// which the view can render, but which may be partially obscured by system + /// UI (such as the system notification area), or or physical intrusions in + /// the display (e.g. overscan regions on television screens or phone sensor + /// housings). + /// + /// This value is calculated by taking `max(0.0, FlutterView.viewPadding - + /// FlutterView.viewInsets)`. This will treat a system IME that increases the + /// bottom inset as consuming that much of the bottom padding. For example, on + /// an iPhone X, [EdgeInsets.bottom] of [FlutterView.padding] is the same as + /// [EdgeInsets.bottom] of [FlutterView.viewPadding] when the soft keyboard is + /// not drawn (to account for the bottom soft button area), but will be `0.0` + /// when the soft keyboard is visible. /// /// When this changes, [onMetricsChanged] is called. /// - /// The relationship between this [Window.viewInsets], [Window.viewPadding], - /// and [Window.padding] are described in more detail in the documentation for - /// [Window]. + /// The relationship between this [viewInsets], [viewPadding], and [padding] + /// are described in more detail in the documentation for [FlutterView]. /// /// See also: /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// observe when this value changes. - /// * [MediaQuery.of], a simpler mechanism for the same. - /// * [Scaffold], which automatically applies the padding in material design - /// applications. - WindowPadding get padding => _padding; - WindowPadding _padding = WindowPadding.zero; + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + /// * [MediaQuery.of], a simpler mechanism for the same. + /// * [Scaffold], which automatically applies the padding in material design + /// applications. + WindowPadding get padding => viewConfiguration.padding; - /// A callback that is invoked whenever the [devicePixelRatio], - /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] - /// values change, for example when the device is rotated or when the - /// application is resized (e.g. when showing applications side-by-side - /// on Android). + /// Updates the view's rendering on the GPU with the newly provided [Scene]. + /// + /// This function must be called within the scope of the + /// [PlatformDispatcher.onBeginFrame] or [PlatformDispatcher.onDrawFrame] + /// callbacks being invoked. + /// + /// If this function is called a second time during a single + /// [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame] + /// callback sequence or called outside the scope of those callbacks, the call + /// will be ignored. /// - /// The engine invokes this callback in the same zone in which the callback - /// was set. + /// To record graphical operations, first create a [PictureRecorder], then + /// construct a [Canvas], passing that [PictureRecorder] to its constructor. + /// After issuing all the graphical operations, call the + /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain + /// the final [Picture] that represents the issued graphical operations. /// - /// The framework registers with this callback and updates the layout - /// appropriately. + /// Next, create a [SceneBuilder], and add the [Picture] to it using + /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can + /// then obtain a [Scene] object, which you can display to the user via this + /// [render] function. /// /// See also: /// - /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to - /// register for notifications when this is called. - /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback? get onMetricsChanged => _onMetricsChanged; - VoidCallback? _onMetricsChanged; - Zone _onMetricsChangedZone = Zone.root; + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + void render(Scene scene) => _render(scene, this); + void _render(Scene scene, FlutterView view) native 'PlatformConfiguration_render'; +} + +/// A top-level platform window displaying a Flutter layer tree drawn from a +/// [Scene]. +/// +/// The current list of all Flutter views for the application is available from +/// `WidgetsBinding.instance.platformDispatcher.views`. Only views that are of type +/// [FlutterWindow] are top level platform windows. +/// +/// There is also a [PlatformDispatcher.instance] singleton object in `dart:ui` +/// if `WidgetsBinding` is unavailable, but we strongly advise avoiding a static +/// reference to it. See the documentation for [PlatformDispatcher.instance] for +/// more details about why it should be avoided. +/// +/// See also: +/// +/// * [PlatformDispatcher], which manages the current list of [FlutterView] (and +/// thus [FlutterWindow]) instances. +class FlutterWindow extends FlutterView { + FlutterWindow._(this._windowId, this.platformDispatcher); + + /// The opaque ID for this view. + final Object _windowId; + + @override + final PlatformDispatcher platformDispatcher; + + @override + ViewConfiguration get viewConfiguration { + assert(platformDispatcher._viewConfigurations.containsKey(_windowId)); + return platformDispatcher._viewConfigurations[_windowId]!; + } +} + +/// A [FlutterWindow] that includes access to setting callbacks and retrieving +/// properties that reside on the [PlatformDispatcher]. +/// +/// It is the type of the global [window] singleton used by applications that +/// only have a single main window. +/// +/// In addition to the properties of [FlutterView], this class provides access +/// to platform-specific properties. To modify or retrieve these properties, +/// applications designed for more than one main window should prefer using +/// `WidgetsBinding.instance.platformDispatcher` instead. +/// +/// Prefer access through `WidgetsBinding.instance.window` or +/// `WidgetsBinding.instance.platformDispatcher` over a static reference to +/// [window], or [PlatformDispatcher.instance]. See the documentation for +/// [PlatformDispatcher.instance] for more details about this recommendation. +class SingletonFlutterWindow extends FlutterWindow { + SingletonFlutterWindow._(Object windowId, PlatformDispatcher platformDispatcher) + : super._(windowId, platformDispatcher); + + /// A callback that is invoked whenever the [devicePixelRatio], + /// [physicalSize], [padding], [viewInsets], [PlatformDispatcher.views], or + /// [systemGestureInsets] values change. + /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// + /// See [PlatformDispatcher.onMetricsChanged] for more information. + VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged; set onMetricsChanged(VoidCallback? callback) { - _onMetricsChanged = callback; - _onMetricsChangedZone = Zone.current; + platformDispatcher.onMetricsChanged = callback; } /// The system-reported default locale of the device. /// - /// This establishes the language and formatting conventions that application + /// {@template dart.ui.window.accessorForwardWarning} + /// Accessing this value returns the value contained in the + /// [PlatformDispatcher] singleton, so instead of getting it from here, you + /// should consider getting it from `WidgetsBinding.instance.platformDispatcher` instead + /// (or, when `WidgetsBinding` isn't available, from + /// [PlatformDispatcher.instance]). The reason this value forwards to the + /// [PlatformDispatcher] is to provide convenience for applications that only + /// use a single main window. + /// {@endtemplate} + /// + /// This establishes the language and formatting conventions that window /// should, if possible, use to render their user interface. /// - /// This is the first locale selected by the user and is the user's - /// primary locale (the locale the device UI is displayed in) + /// This is the first locale selected by the user and is the user's primary + /// locale (the locale the device UI is displayed in) /// - /// This is equivalent to `locales.first` and will provide an empty non-null locale - /// if the [locales] list has not been set or is empty. - Locale? get locale { - if (_locales != null && _locales!.isNotEmpty) { - return _locales!.first; - } - return null; - } + /// This is equivalent to `locales.first` and will provide an empty non-null + /// locale if the [locales] list has not been set or is empty. + Locale get locale => platformDispatcher.locale; /// The full system-reported supported locales of the device. /// - /// This establishes the language and formatting conventions that application + /// {@macro dart.ui.window.accessorForwardWarning} + /// + /// This establishes the language and formatting conventions that window /// should, if possible, use to render their user interface. /// /// The list is ordered in order of priority, with lower-indexed locales being @@ -809,8 +358,7 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - List? get locales => _locales; - List? _locales; + List get locales => platformDispatcher.locales; /// Performs the platform-native locale resolution. /// @@ -821,27 +369,13 @@ class Window { /// This method returns synchronously and is a direct call to /// platform specific APIs without invoking method channels. Locale? computePlatformResolvedLocale(List supportedLocales) { - final List supportedLocalesData = []; - for (Locale locale in supportedLocales) { - supportedLocalesData.add(locale.languageCode); - supportedLocalesData.add(locale.countryCode); - supportedLocalesData.add(locale.scriptCode); - } - - final List result = _computePlatformResolvedLocale(supportedLocalesData); - - if (result.isNotEmpty) { - return Locale.fromSubtags( - languageCode: result[0], - countryCode: result[1] == '' ? null : result[1], - scriptCode: result[2] == '' ? null : result[2]); - } - return null; + return platformDispatcher.computePlatformResolvedLocale(supportedLocales); } - List _computePlatformResolvedLocale(List supportedLocalesData) native 'PlatformConfiguration_computePlatformResolvedLocale'; /// A callback that is invoked whenever [locale] changes value. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -849,32 +383,25 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback? get onLocaleChanged => _onLocaleChanged; - VoidCallback? _onLocaleChanged; - Zone _onLocaleChangedZone = Zone.root; + VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; set onLocaleChanged(VoidCallback? callback) { - _onLocaleChanged = callback; - _onLocaleChangedZone = Zone.current; + platformDispatcher.onLocaleChanged = callback; } /// The lifecycle state immediately after dart isolate initialization. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// This property will not be updated as the lifecycle changes. /// /// It is used to initialize [SchedulerBinding.lifecycleState] at startup /// with any buffered lifecycle state events. - String get initialLifecycleState { - _initialLifecycleStateAccessed = true; - return _initialLifecycleState; - } - late String _initialLifecycleState; - /// Tracks if the initial state has been accessed. Once accessed, we - /// will stop updating the [initialLifecycleState], as it is not the - /// preferred way to access the state. - bool _initialLifecycleStateAccessed = false; + String get initialLifecycleState => platformDispatcher.initialLifecycleState; /// The system-reported text scale. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// This establishes the text scaling factor to use when rendering text, /// according to the user's platform preferences. /// @@ -885,18 +412,20 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. - double get textScaleFactor => _textScaleFactor; - double _textScaleFactor = 1.0; + double get textScaleFactor => platformDispatcher.textScaleFactor; /// The setting indicating whether time should always be shown in the 24-hour /// format. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// This option is used by [showTimePicker]. - bool get alwaysUse24HourFormat => _alwaysUse24HourFormat; - bool _alwaysUse24HourFormat = false; + bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; /// A callback that is invoked whenever [textScaleFactor] changes value. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -904,21 +433,23 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; - VoidCallback? _onTextScaleFactorChanged; - Zone _onTextScaleFactorChangedZone = Zone.root; + VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; set onTextScaleFactorChanged(VoidCallback? callback) { - _onTextScaleFactorChanged = callback; - _onTextScaleFactorChangedZone = Zone.current; + platformDispatcher.onTextScaleFactorChanged = callback; } /// The setting indicating the current brightness mode of the host platform. - /// If the platform has no preference, [platformBrightness] defaults to [Brightness.light]. - Brightness get platformBrightness => _platformBrightness; - Brightness _platformBrightness = Brightness.light; + /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// + /// If the platform has no preference, [platformBrightness] defaults to + /// [Brightness.light]. + Brightness get platformBrightness => platformDispatcher.platformBrightness; /// A callback that is invoked whenever [platformBrightness] changes value. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -926,19 +457,20 @@ class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - VoidCallback? _onPlatformBrightnessChanged; - Zone _onPlatformBrightnessChangedZone = Zone.root; + VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; set onPlatformBrightnessChanged(VoidCallback? callback) { - _onPlatformBrightnessChanged = callback; - _onPlatformBrightnessChangedZone = Zone.current; + platformDispatcher.onPlatformBrightnessChanged = callback; } - /// A callback that is invoked to notify the application that it is an - /// appropriate time to provide a scene using the [SceneBuilder] API and the - /// [render] method. When possible, this is driven by the hardware VSync - /// signal. This is only called if [scheduleFrame] has been called since the - /// last time this callback was invoked. + /// A callback that is invoked to notify the window that it is an appropriate + /// time to provide a scene using the [SceneBuilder] API and the [render] + /// method. + /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// + /// When possible, this is driven by the hardware VSync signal. This is only + /// called if [scheduleFrame] has been called since the last time this + /// callback was invoked. /// /// The [onDrawFrame] callback is invoked immediately after [onBeginFrame], /// after draining any microtasks (e.g. completions of any [Future]s) queued @@ -953,18 +485,18 @@ class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - FrameCallback? get onBeginFrame => _onBeginFrame; - FrameCallback? _onBeginFrame; - Zone _onBeginFrameZone = Zone.root; + FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; set onBeginFrame(FrameCallback? callback) { - _onBeginFrame = callback; - _onBeginFrameZone = Zone.current; + platformDispatcher.onBeginFrame = callback; } /// A callback that is invoked for each frame after [onBeginFrame] has - /// completed and after the microtask queue has been drained. This can be - /// used to implement a second phase of frame rendering that happens - /// after any deferred work queued by the [onBeginFrame] phase. + /// completed and after the microtask queue has been drained. + /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// + /// This can be used to implement a second phase of frame rendering that + /// happens after any deferred work queued by the [onBeginFrame] phase. /// /// The framework invokes this callback in the same zone in which the /// callback was set. @@ -975,22 +507,21 @@ class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - VoidCallback? get onDrawFrame => _onDrawFrame; - VoidCallback? _onDrawFrame; - Zone _onDrawFrameZone = Zone.root; + VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; set onDrawFrame(VoidCallback? callback) { - _onDrawFrame = callback; - _onDrawFrameZone = Zone.current; + platformDispatcher.onDrawFrame = callback; } /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. /// - /// It's prefered to use [SchedulerBinding.addTimingsCallback] than to use - /// [Window.onReportTimings] directly because + /// {@macro dart.ui.window.accessorForwardWarning} + /// + /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use + /// [SingletonFlutterWindow.onReportTimings] directly because /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. /// - /// This can be used to see if the application has missed frames (through + /// This can be used to see if the window has missed frames (through /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high /// latencies (through [FrameTiming.totalSpan]). /// @@ -1004,22 +535,15 @@ class Window { /// Flutter spends less than 0.1ms every 1 second to report the timings /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for /// 60fps), or 0.01% CPU usage per second. - TimingsCallback? get onReportTimings => _onReportTimings; - TimingsCallback? _onReportTimings; - Zone _onReportTimingsZone = Zone.root; + TimingsCallback? get onReportTimings => platformDispatcher.onReportTimings; set onReportTimings(TimingsCallback? callback) { - if ((callback == null) != (_onReportTimings == null)) { - _setNeedsReportTimings(callback != null); - } - _onReportTimings = callback; - _onReportTimingsZone = Zone.current; + platformDispatcher.onReportTimings = callback; } - late _SetNeedsReportTimingsFunc _setNeedsReportTimings; - void _nativeSetNeedsReportTimings(bool value) native 'PlatformConfiguration_setNeedsReportTimings'; - /// A callback that is invoked when pointer data is available. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. /// @@ -1027,17 +551,25 @@ class Window { /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. - PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; - PointerDataPacketCallback? _onPointerDataPacket; - Zone _onPointerDataPacketZone = Zone.root; + PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback) { - _onPointerDataPacket = callback; - _onPointerDataPacketZone = Zone.current; + platformDispatcher.onPointerDataPacket = callback; + } + + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; } /// The route or path that the embedder requested when the application was /// launched. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// This will be the string "`/`" if no particular route was requested. /// /// ## Android @@ -1061,118 +593,99 @@ class Window { /// * [Navigator], a widget that handles routing. /// * [SystemChannels.navigation], which handles subsequent navigation /// requests from the embedder. - String get defaultRouteName => _defaultRouteName(); - String _defaultRouteName() native 'PlatformConfiguration_defaultRouteName'; - - /// Requests that, at the next appropriate opportunity, the [onBeginFrame] - /// and [onDrawFrame] callbacks be invoked. - /// - /// See also: - /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'; - - /// Updates the application's rendering on the GPU with the newly provided - /// [Scene]. This function must be called within the scope of the - /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function - /// is called a second time during a single [onBeginFrame]/[onDrawFrame] - /// callback sequence or called outside the scope of those callbacks, the call - /// will be ignored. - /// - /// To record graphical operations, first create a [PictureRecorder], then - /// construct a [Canvas], passing that [PictureRecorder] to its constructor. - /// After issuing all the graphical operations, call the - /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain - /// the final [Picture] that represents the issued graphical operations. - /// - /// Next, create a [SceneBuilder], and add the [Picture] to it using - /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can - /// then obtain a [Scene] object, which you can display to the user via this - /// [render] function. + String get defaultRouteName => platformDispatcher.defaultRouteName; + + /// Requests that, at the next appropriate opportunity, the [onBeginFrame] and + /// [onDrawFrame] callbacks be invoked. + /// + /// {@template dart.ui.window.functionForwardWarning} + /// Calling this function forwards the call to the same function on the + /// [PlatformDispatcher] singleton, so instead of calling it here, you should + /// consider calling it on `WidgetsBinding.instance.platformDispatcher` instead (or, when + /// `WidgetsBinding` isn't available, on [PlatformDispatcher.instance]). The + /// reason this function forwards to the [PlatformDispatcher] is to provide + /// convenience for applications that only use a single main window. + /// {@endtemplate} /// /// See also: /// - /// * [SchedulerBinding], the Flutter framework class which manages the - /// scheduling of frames. - /// * [RendererBinding], the Flutter framework class which manages layout and - /// painting. - void render(Scene scene) native 'PlatformConfiguration_render'; + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + void scheduleFrame() => platformDispatcher.scheduleFrame(); /// Whether the user has requested that [updateSemantics] be called when /// the semantic contents of window changes. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The [onSemanticsEnabledChanged] callback is called whenever this value /// changes. - bool get semanticsEnabled => _semanticsEnabled; - bool _semanticsEnabled = false; + bool get semanticsEnabled => platformDispatcher.semanticsEnabled; /// A callback that is invoked when the value of [semanticsEnabled] changes. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - VoidCallback? _onSemanticsEnabledChanged; - Zone _onSemanticsEnabledChangedZone = Zone.root; + VoidCallback? get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; set onSemanticsEnabledChanged(VoidCallback? callback) { - _onSemanticsEnabledChanged = callback; - _onSemanticsEnabledChangedZone = Zone.current; + platformDispatcher.onSemanticsEnabledChanged = callback; + } + + /// The [FrameData] object for the current frame. + FrameData get frameData => platformDispatcher.frameData; + + /// A callback that is invoked when the window updates the [FrameData]. + VoidCallback? get onFrameDataChanged => platformDispatcher.onFrameDataChanged; + set onFrameDataChanged(VoidCallback? callback) { + platformDispatcher.onFrameDataChanged = callback; } /// A callback that is invoked whenever the user requests an action to be /// performed. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// This callback is used when the user expresses the action they wish to /// perform based on the semantics supplied by [updateSemantics]. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction; - SemanticsActionCallback? _onSemanticsAction; - Zone _onSemanticsActionZone = Zone.root; + SemanticsActionCallback? get onSemanticsAction => platformDispatcher.onSemanticsAction; set onSemanticsAction(SemanticsActionCallback? callback) { - _onSemanticsAction = callback; - _onSemanticsActionZone = Zone.current; + platformDispatcher.onSemanticsAction = callback; } /// Additional accessibility features that may be enabled by the platform. - AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; - // The zero value matches the default value in `platform_data.h`. - AccessibilityFeatures _accessibilityFeatures = const AccessibilityFeatures._(0); + AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] changes. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; - VoidCallback? _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFeaturesChangedZone = Zone.root; + VoidCallback? get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback) { - _onAccessibilityFeaturesChanged = callback; - _onAccessibilityFeaturesChangedZone = Zone.current; + platformDispatcher.onAccessibilityFeaturesChanged = callback; } /// Change the retained semantics data about this window. /// + /// {@macro dart.ui.window.functionForwardWarning} + /// /// If [semanticsEnabled] is true, the user has requested that this function /// be called whenever the semantic content of this window changes. /// /// In either case, this function disposes the given update, which means the /// semantics update cannot be used further. - void updateSemantics(SemanticsUpdate update) native 'PlatformConfiguration_updateSemantics'; - - /// Set the debug name associated with this window's root isolate. - /// - /// Normally debug names are automatically generated from the Dart port, entry - /// point, and source file. For example: `main.dart$main-1234`. - /// - /// This can be combined with flutter tools `--isolate-filter` flag to debug - /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. - /// Note that this does not rename any child isolates of the root. - void setIsolateDebugName(String name) native 'PlatformConfiguration_setIsolateDebugName'; + void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update); /// Sends a message to a platform-specific plugin. /// + /// {@macro dart.ui.window.functionForwardWarning} + /// /// The `name` parameter determines which plugin receives the message. The /// `data` parameter contains the message payload and is typically UTF-8 /// encoded JSON but can be arbitrary data. If the plugin replies to the @@ -1181,20 +694,16 @@ class Window { /// The framework invokes [callback] in the same zone in which this method /// was called. void sendPlatformMessage(String name, - ByteData? data, - PlatformMessageResponseCallback? callback) { - final String? error = - _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data); - if (error != null) - throw Exception(error); + ByteData? data, + PlatformMessageResponseCallback? callback) { + platformDispatcher.sendPlatformMessage(name, data, callback); } - String? _sendPlatformMessage(String name, - PlatformMessageResponseCallback? callback, - ByteData? data) native 'PlatformConfiguration_sendPlatformMessage'; /// Called whenever this window receives a message from a platform-specific /// plugin. /// + /// {@macro dart.ui.window.accessorForwardWarning} + /// /// The `name` parameter determines which plugin sent the message. The `data` /// parameter is the payload and is typically UTF-8 encoded JSON but can be /// arbitrary data. @@ -1205,43 +714,24 @@ class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; - PlatformMessageCallback? _onPlatformMessage; - Zone _onPlatformMessageZone = Zone.root; + // TODO(ianh): deprecate once framework uses [ChannelBuffers.setListener]. + PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback) { - _onPlatformMessage = callback; - _onPlatformMessageZone = Zone.current; + platformDispatcher.onPlatformMessage = callback; } - /// Called by [_dispatchPlatformMessage]. - void _respondToPlatformMessage(int responseId, ByteData? data) - native 'PlatformConfiguration_respondToPlatformMessage'; - - /// Wraps the given [callback] in another callback that ensures that the - /// original callback is called in the zone it was registered in. - static PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback? callback) { - if (callback == null) - return null; - - // Store the zone in which the callback is being registered. - final Zone registrationZone = Zone.current; - - return (ByteData? data) { - registrationZone.runUnaryGuarded(callback, data); - }; - } - - - /// The embedder can specify data that the isolate can request synchronously - /// on launch. This accessor fetches that data. + /// Set the debug name associated with this platform dispatcher's root + /// isolate. /// - /// This data is persistent for the duration of the Flutter application and is - /// available even after isolate restarts. Because of this lifecycle, the size - /// of this data must be kept to a minimum. + /// {@macro dart.ui.window.accessorForwardWarning} /// - /// For asynchronous communication between the embedder and isolate, a - /// platform channel may be used. - ByteData? getPersistentIsolateData() native 'PlatformConfiguration_getPersistentIsolateData'; + /// Normally debug names are automatically generated from the Dart port, entry + /// point, and source file. For example: `main.dart$main-1234`. + /// + /// This can be combined with flutter tools `--isolate-filter` flag to debug + /// specific root isolates. For example: `flutter attach --isolate-filter=[name]`. + /// Note that this does not rename any child isolates of the root. + void setIsolateDebugName(String name) => PlatformDispatcher.instance.setIsolateDebugName(name); } /// Additional accessibility features that may be enabled by the platform. @@ -1338,12 +828,18 @@ enum Brightness { light, } -/// The [Window] singleton. +/// The [SingletonFlutterWindow] representing the main window for applications +/// where there is only one window, such as applications designed for +/// single-display mobile devices. /// -/// Please try to avoid statically referencing this and instead use a -/// binding for dependency resolution such as `WidgetsBinding.instance.window`. +/// Applications that are designed to use more than one window should interact +/// with the `WidgetsBinding.instance.platformDispatcher` instead. /// -/// Static access of this "window" object means that Flutter has few, if any +/// Consider avoiding static references to this singleton through +/// [PlatformDispatcher.instance] and instead prefer using a binding for +/// dependency resolution such as `WidgetsBinding.instance.window`. +/// +/// Static access of this `window` object means that Flutter has few, if any /// options to fake or mock the given object in tests. Even in cases where Dart /// offers special language constructs to forcefully shadow such properties, /// those mechanisms would only be reasonable for tests and they would not be @@ -1351,6 +847,86 @@ enum Brightness { /// appropriate implementation at runtime. /// /// The only place that `WidgetsBinding.instance.window` is inappropriate is if -/// a `Window` is required before invoking `runApp()`. In that case, it is -/// acceptable (though unfortunate) to use this object statically. -final Window window = Window._(); +/// access to these APIs is required before the binding is initialized by +/// invoking `runApp()` or `WidgetsFlutterBinding.instance.ensureInitialized()`. +/// In that case, it is necessary (though unfortunate) to use the +/// [PlatformDispatcher.instance] object statically. +/// +/// See also: +/// +/// * [PlatformDispatcher.views], contains the current list of Flutter windows +/// belonging to the application, including top level application windows like +/// this one. +final SingletonFlutterWindow window = SingletonFlutterWindow._(0, PlatformDispatcher.instance); + +/// Additional data available on each flutter frame. +class FrameData { + const FrameData._({this.frameNumber = -1}); + + /// The number of the current frame. + /// + /// This number monotonically increases, but doesn't necessarily + /// start at a particular value. + /// + /// If not provided, defaults to -1. + final int frameNumber; +} + +/// Platform specific configuration for gesture behavior, such as touch slop. +/// +/// These settings are provided via [ViewConfiguration] to each window, and should +/// be favored for configuring gesture behavior over the framework constants. +/// +/// A `null` field indicates that the platform or view does not have a preference +/// and the fallback constants should be used instead. +class GestureSettings { + /// Create a new [GestureSettings] value. + /// + /// Consider using [GestureSettings.copyWith] on an existing settings object + /// to ensure that newly added fields are correctly set. + const GestureSettings({ + this.physicalTouchSlop, + this.physicalDoubleTapSlop, + }); + + /// The number of physical pixels a pointer is allowed to drift before it is + /// considered an intentional movement. + /// + /// If `null`, the framework's default touch slop configuration should be used + /// instead. + final double? physicalTouchSlop; + + /// The number of physical pixels that the first and second tap of a double tap + /// can drift apart to still be recognized as a double tap. + /// + /// If `null`, the framework's default double tap slop configuration should be used + /// instead. + final double? physicalDoubleTapSlop; + + /// Create a new [GestureSetting]s object from an existing value, overwriting + /// all of the provided fields. + GestureSettings copyWith({ + double? physicalTouchSlop, + double? physicalDoubleTapSlop, + }) { + return GestureSettings( + physicalTouchSlop: physicalTouchSlop ?? this.physicalTouchSlop, + physicalDoubleTapSlop: physicalDoubleTapSlop ?? this.physicalDoubleTapSlop, + ); + } + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is GestureSettings && + other.physicalTouchSlop == physicalTouchSlop && + other.physicalDoubleTapSlop == physicalDoubleTapSlop; + } + + @override + int get hashCode => hashValues(physicalTouchSlop, physicalDoubleTapSlop); + + @override + String toString() => 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; +} diff --git a/lib/ui/window/key_data.cc b/lib/ui/window/key_data.cc new file mode 100644 index 0000000000000..2dacd8d7bea43 --- /dev/null +++ b/lib/ui/window/key_data.cc @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/key_data.h" + +#include + +namespace flutter { + +static_assert(sizeof(KeyData) == kBytesPerKeyField * kKeyDataFieldCount, + "KeyData has the wrong size"); + +void KeyData::Clear() { + memset(this, 0, sizeof(KeyData)); +} + +} // namespace flutter diff --git a/lib/ui/window/key_data.h b/lib/ui/window/key_data.h new file mode 100644 index 0000000000000..a7785da12507d --- /dev/null +++ b/lib/ui/window/key_data.h @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_ + +#include + +namespace flutter { + +// If this value changes, update the key data unpacking code in hooks.dart. +static constexpr int kKeyDataFieldCount = 5; +static constexpr int kBytesPerKeyField = sizeof(int64_t); + +// The change of the key event, used by KeyData. +// +// Must match the KeyEventType enum in ui/key.dart. +enum class KeyEventType : int64_t { + kDown = 0, + kUp, + kRepeat, +}; + +// The fixed-length sections of a KeyDataPacket. +// +// KeyData does not contain `character`, for variable-length data are stored in +// a different way in KeyDataPacket. +// +// This structure is unpacked by hooks.dart. +struct alignas(8) KeyData { + // Timestamp in microseconds from an arbitrary and consistent start point + uint64_t timestamp; + KeyEventType type; + uint64_t physical; + uint64_t logical; + // True if the event does not correspond to a native event. + // + // The value is 1 for true, and 0 for false. + uint64_t synthesized; + + // Sets all contents of `Keydata` to 0. + void Clear(); +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_H_ diff --git a/lib/ui/window/key_data_packet.cc b/lib/ui/window/key_data_packet.cc new file mode 100644 index 0000000000000..fe24af210cd67 --- /dev/null +++ b/lib/ui/window/key_data_packet.cc @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/window/key_data_packet.h" + +#include + +#include "flutter/fml/logging.h" + +namespace flutter { + +KeyDataPacket::KeyDataPacket(const KeyData& event, const char* character) { + size_t char_size = character == nullptr ? 0 : strlen(character); + uint64_t char_size_64 = char_size; + data_.resize(sizeof(uint64_t) + sizeof(KeyData) + char_size); + memcpy(CharacterSizeStart(), &char_size_64, sizeof(char_size)); + memcpy(KeyDataStart(), &event, sizeof(KeyData)); + if (character != nullptr) { + memcpy(CharacterStart(), character, char_size); + } +} + +KeyDataPacket::~KeyDataPacket() = default; + +} // namespace flutter diff --git a/lib/ui/window/key_data_packet.h b/lib/ui/window/key_data_packet.h new file mode 100644 index 0000000000000..e8efdf17334df --- /dev/null +++ b/lib/ui/window/key_data_packet.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ +#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_MESSAGE_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/key_data.h" + +namespace flutter { + +// A byte stream representing a key event, to be sent to the framework. +class KeyDataPacket { + public: + // Build the key data packet by providing information. + // + // The `character` is a nullable C-string that ends with a '\0'. + KeyDataPacket(const KeyData& event, const char* character); + ~KeyDataPacket(); + + // Prevent copying. + KeyDataPacket(KeyDataPacket const&) = delete; + KeyDataPacket& operator=(KeyDataPacket const&) = delete; + + const std::vector& data() const { return data_; } + + private: + // Packet structure: + // | CharDataSize | (1 field) + // | Key Data | (kKeyDataFieldCount fields) + // | CharData | (CharDataSize bits) + + uint8_t* CharacterSizeStart() { return data_.data(); } + uint8_t* KeyDataStart() { return CharacterSizeStart() + sizeof(uint64_t); } + uint8_t* CharacterStart() { return KeyDataStart() + sizeof(KeyData); } + + std::vector data_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_MESSAGE_H_ diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index e470e1dab79e6..47d6331e4064c 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -9,6 +9,7 @@ #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/platform_message_response_dart.h" +#include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" @@ -123,13 +124,13 @@ Dart_Handle SendPlatformMessage(Dart_Handle window, } if (Dart_IsNull(data_handle)) { dart_state->platform_configuration()->client()->HandlePlatformMessage( - fml::MakeRefCounted(name, response)); + std::make_unique(name, response)); } else { tonic::DartByteData data(data_handle); const uint8_t* buffer = static_cast(data.data()); dart_state->platform_configuration()->client()->HandlePlatformMessage( - fml::MakeRefCounted( - name, std::vector(buffer, buffer + data.length_in_bytes()), + std::make_unique( + name, fml::MallocMapping::Copy(buffer, data.length_in_bytes()), response)); } @@ -180,8 +181,17 @@ void GetPersistentIsolateData(Dart_NativeArguments args) { persistent_isolate_data->GetSize())); } -Dart_Handle ToByteData(const std::vector& buffer) { - return tonic::DartByteData::Create(buffer.data(), buffer.size()); +void RespondToKeyData(Dart_Handle window, int response_id, bool handled) { + UIDartState::Current()->platform_configuration()->CompleteKeyDataResponse( + response_id, handled); +} + +void _RespondToKeyData(Dart_NativeArguments args) { + tonic::DartCallStatic(&RespondToKeyData, args); +} + +Dart_Handle ToByteData(const fml::Mapping& buffer) { + return tonic::DartByteData::Create(buffer.GetMapping(), buffer.GetSize()); } } // namespace @@ -195,79 +205,110 @@ PlatformConfiguration::PlatformConfiguration( PlatformConfiguration::~PlatformConfiguration() {} void PlatformConfiguration::DidCreateIsolate() { - library_.Set(tonic::DartState::Current(), - Dart_LookupLibrary(tonic::ToDart("dart:ui"))); - window_.reset(new Window({1.0, 0.0, 0.0})); + Dart_Handle library = Dart_LookupLibrary(tonic::ToDart("dart:ui")); + update_locales_.Set(tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_updateLocales"))); + update_user_settings_data_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_updateUserSettingsData"))); + update_lifecycle_state_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_updateLifecycleState"))); + update_semantics_enabled_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_updateSemanticsEnabled"))); + update_accessibility_features_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_updateAccessibilityFeatures"))); + dispatch_platform_message_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_dispatchPlatformMessage"))); + dispatch_semantics_action_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_dispatchSemanticsAction"))); + begin_frame_.Set(tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_beginFrame"))); + draw_frame_.Set(tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_drawFrame"))); + report_timings_.Set(tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_reportTimings"))); + windows_.insert( + std::make_pair(0, std::unique_ptr(new Window{ + 0, ViewportMetrics{1.0, 0.0, 0.0, -1}}))); } void PlatformConfiguration::UpdateLocales( const std::vector& locales) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::shared_ptr dart_state = + update_locales_.dart_state().lock(); if (!dart_state) { return; } + tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_updateLocales", - { - tonic::ToDart>(locales), - })); + tonic::LogIfError( + tonic::DartInvoke(update_locales_.Get(), + { + tonic::ToDart>(locales), + })); } void PlatformConfiguration::UpdateUserSettingsData(const std::string& data) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::shared_ptr dart_state = + update_user_settings_data_.dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), - "_updateUserSettingsData", - { - tonic::StdStringToDart(data), - })); + tonic::LogIfError(tonic::DartInvoke(update_user_settings_data_.Get(), + { + tonic::StdStringToDart(data), + })); } void PlatformConfiguration::UpdateLifecycleState(const std::string& data) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::shared_ptr dart_state = + update_lifecycle_state_.dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), - "_updateLifecycleState", - { - tonic::StdStringToDart(data), - })); + tonic::LogIfError(tonic::DartInvoke(update_lifecycle_state_.Get(), + { + tonic::StdStringToDart(data), + })); } void PlatformConfiguration::UpdateSemanticsEnabled(bool enabled) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::shared_ptr dart_state = + update_semantics_enabled_.dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); UIDartState::ThrowIfUIOperationsProhibited(); - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_updateSemanticsEnabled", {tonic::ToDart(enabled)})); + tonic::LogIfError(tonic::DartInvoke(update_semantics_enabled_.Get(), + {tonic::ToDart(enabled)})); } void PlatformConfiguration::UpdateAccessibilityFeatures(int32_t values) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::shared_ptr dart_state = + update_accessibility_features_.dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), - "_updateAccessibilityFeatures", - {tonic::ToDart(values)})); + tonic::LogIfError(tonic::DartInvoke(update_accessibility_features_.Get(), + {tonic::ToDart(values)})); } void PlatformConfiguration::DispatchPlatformMessage( - fml::RefPtr message) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::unique_ptr message) { + std::shared_ptr dart_state = + dispatch_platform_message_.dart_state().lock(); if (!dart_state) { FML_DLOG(WARNING) << "Dropping platform message for lack of DartState on channel: " @@ -291,34 +332,45 @@ void PlatformConfiguration::DispatchPlatformMessage( } tonic::LogIfError( - tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage", - {tonic::ToDart(message->channel()), data_handle, - tonic::ToDart(response_id)})); + tonic::DartInvoke(dispatch_platform_message_.Get(), + {tonic::ToDart(message->channel()), data_handle, + tonic::ToDart(response_id)})); } void PlatformConfiguration::DispatchSemanticsAction(int32_t id, SemanticsAction action, - std::vector args) { - std::shared_ptr dart_state = library_.dart_state().lock(); + fml::MallocMapping args) { + std::shared_ptr dart_state = + dispatch_semantics_action_.dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); - Dart_Handle args_handle = (args.empty()) ? Dart_Null() : ToByteData(args); + Dart_Handle args_handle = + (args.GetSize() <= 0) ? Dart_Null() : ToByteData(args); if (Dart_IsError(args_handle)) { return; } - tonic::LogIfError(tonic::DartInvokeField( - library_.value(), "_dispatchSemanticsAction", + tonic::LogIfError(tonic::DartInvoke( + dispatch_semantics_action_.Get(), {tonic::ToDart(id), tonic::ToDart(static_cast(action)), args_handle})); } -void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { - std::shared_ptr dart_state = library_.dart_state().lock(); +uint64_t PlatformConfiguration::RegisterKeyDataResponse( + KeyDataResponse callback) { + uint64_t response_id = next_key_response_id_++; + pending_key_responses_[response_id] = std::move(callback); + return response_id; +} + +void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, + uint64_t frame_number) { + std::shared_ptr dart_state = + begin_frame_.dart_state().lock(); if (!dart_state) { return; } @@ -326,18 +378,20 @@ void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame", - { - Dart_NewInteger(microseconds), - })); + tonic::LogIfError( + tonic::DartInvoke(begin_frame_.Get(), { + Dart_NewInteger(microseconds), + Dart_NewInteger(frame_number), + })); UIDartState::Current()->FlushMicrotasksNow(); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {})); + tonic::LogIfError(tonic::DartInvokeVoid(draw_frame_.Get())); } void PlatformConfiguration::ReportTimings(std::vector timings) { - std::shared_ptr dart_state = library_.dart_state().lock(); + std::shared_ptr dart_state = + report_timings_.dart_state().lock(); if (!dart_state) { return; } @@ -356,10 +410,9 @@ void PlatformConfiguration::ReportTimings(std::vector timings) { memcpy(data, timings.data(), sizeof(int64_t) * timings.size()); FML_CHECK(Dart_TypedDataReleaseData(data_handle)); - tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_reportTimings", - { - data_handle, - })); + tonic::LogIfError(tonic::DartInvoke(report_timings_.Get(), { + data_handle, + })); } void PlatformConfiguration::CompletePlatformMessageEmptyResponse( @@ -391,6 +444,27 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } +void PlatformConfiguration::CompleteKeyDataResponse(uint64_t response_id, + bool handled) { + if (response_id == 0) { + return; + } + auto it = pending_key_responses_.find(response_id); + FML_DCHECK(it != pending_key_responses_.end()); + if (it == pending_key_responses_.end()) { + return; + } + KeyDataResponse callback = std::move(it->second); + pending_key_responses_.erase(it); + // The key result callback must be called on the platform thread. This is + // mainly because iOS needs to block the platform message loop with a nested + // loop to respond to key events synchronously, and if the callback is called + // from another thread, it won't stop the nested message loop, causing a + // deadlock. + UIDartState::Current()->GetTaskRunners().GetPlatformTaskRunner()->PostTask( + [handled, callback]() { callback(handled); }); +} + Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { std::vector supportedLocales = tonic::DartConverter>::FromDart( @@ -421,7 +495,8 @@ void PlatformConfiguration::RegisterNatives( true}, {"PlatformConfiguration_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, - {"PlatformConfiguration_render", Render, 2, true}, + {"PlatformConfiguration_respondToKeyData", _RespondToKeyData, 3, true}, + {"PlatformConfiguration_render", Render, 3, true}, {"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true}, {"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2, true}, diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 4652c7c5dcdbb..e5ec3070a1fc0 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -5,39 +5,26 @@ #ifndef FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ #define FLUTTER_LIB_UI_WINDOW_PLATFORM_CONFIGURATION_H_ +#include #include -#include #include #include #include #include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/semantics/semantics_update.h" -#include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" #include "third_party/tonic/dart_persistent_value.h" -namespace tonic { -class DartLibraryNatives; - -// So tonic::ToDart> returns List instead of -// List. -template <> -struct DartListFactory { - static Dart_Handle NewList(intptr_t length) { - return Dart_NewListOf(Dart_CoreType_Int, length); - } -}; - -} // namespace tonic - namespace flutter { class FontCollection; class PlatformMessage; class Scene; +typedef std::function KeyDataResponse; + //-------------------------------------------------------------------------- /// @brief An enum for defining the different kinds of accessibility features /// that can be enabled by the platform. @@ -100,7 +87,8 @@ class PlatformConfigurationClient { /// @param[in] message The message from the Flutter application to send to /// the underlying platform. /// - virtual void HandlePlatformMessage(fml::RefPtr message) = 0; + virtual void HandlePlatformMessage( + std::unique_ptr message) = 0; //-------------------------------------------------------------------------- /// @brief Returns the current collection of fonts available on the @@ -190,6 +178,27 @@ class PlatformConfigurationClient { ComputePlatformResolvedLocale( const std::vector& supported_locale_data) = 0; + //-------------------------------------------------------------------------- + /// @brief Invoked when the Dart VM requests that a deferred library + /// be loaded. Notifies the engine that the deferred library + /// identified by the specified loading unit id should be + /// downloaded and loaded into the Dart VM via + /// `LoadDartDeferredLibrary` + /// + /// Upon encountering errors or otherwise failing to load a + /// loading unit with the specified id, the failure should be + /// directly reported to dart by calling + /// `LoadDartDeferredLibraryFailure` to ensure the waiting dart + /// future completes with an error. + /// + /// @param[in] loading_unit_id The unique id of the deferred library's + /// loading unit. This id is to be passed + /// back into LoadDartDeferredLibrary + /// in order to identify which deferred + /// library to load. + /// + virtual void RequestDartDeferredLibrary(intptr_t loading_unit_id) = 0; + protected: virtual ~PlatformConfigurationClient(); }; @@ -301,7 +310,7 @@ class PlatformConfiguration final { /// @param[in] message The message sent from the embedder to the Dart /// application. /// - void DispatchPlatformMessage(fml::RefPtr message); + void DispatchPlatformMessage(std::unique_ptr message); //---------------------------------------------------------------------------- /// @brief Notifies the framework that the embedder encountered an @@ -316,21 +325,38 @@ class PlatformConfiguration final { /// void DispatchSemanticsAction(int32_t id, SemanticsAction action, - std::vector args); + fml::MallocMapping args); + + //---------------------------------------------------------------------------- + /// @brief Registers a callback to be invoked when the framework has + /// decided whether to handle an event. This callback originates + /// in the platform view and has been forwarded through the engine + /// to here. + /// + /// This method will move and store the `callback`, associate it + /// with a self-incrementing identifier, the response ID, then + /// return the ID, which is typically used by + /// Window::DispatchKeyDataPacket. + /// + /// @param[in] callback The callback to be registered. + /// + /// @return The response ID to be associated with the callback. Using this + /// ID in CompleteKeyDataResponse will invoke the callback. + /// + uint64_t RegisterKeyDataResponse(KeyDataResponse callback); //---------------------------------------------------------------------------- /// @brief Notifies the framework that it is time to begin working on a - /// new - /// frame previously scheduled via a call to + /// new frame previously scheduled via a call to /// `PlatformConfigurationClient::ScheduleFrame`. This call /// originates in the animator. /// /// The frame time given as the argument indicates the point at /// which the current frame interval began. It is very slightly /// (because of scheduling overhead) in the past. If a new layer - /// tree is not produced and given to the GPU task runner within - /// one frame interval from this point, the Flutter application - /// will jank. + /// tree is not produced and given to the raster task runner + /// within one frame interval from this point, the Flutter + /// application will jank. /// /// This method calls the `::_beginFrame` method in `hooks.dart`. /// @@ -338,19 +364,24 @@ class PlatformConfiguration final { /// began. May be used by animation interpolators, /// physics simulations, etc.. /// - void BeginFrame(fml::TimePoint frame_time); + /// @param[in] frame_number The frame number recorded by the animator. Used + /// by the framework to associate frame specific + /// debug information with frame timings and timeline + /// events. + /// + void BeginFrame(fml::TimePoint frame_time, uint64_t frame_number); //---------------------------------------------------------------------------- /// @brief Dart code cannot fully measure the time it takes for a /// specific frame to be rendered. This is because Dart code only /// runs on the UI task runner. That is only a small part of the - /// overall frame workload. The GPU task runner frame workload is - /// executed on a thread where Dart code cannot run (and hence + /// overall frame workload. The raster task runner frame workload + /// is executed on a thread where Dart code cannot run (and hence /// instrument). Besides, due to the pipelined nature of rendering /// in Flutter, there may be multiple frame workloads being /// processed at any given time. However, for non-Timeline based /// profiling, it is useful for trace collection and processing to - /// happen in Dart. To do this, the GPU task runner frame + /// happen in Dart. To do this, the raster task runner frame /// workloads need to be instrumented separately. After a set /// number of these profiles have been gathered, they need to be /// reported back to Dart code. The engine reports this extra @@ -378,11 +409,14 @@ class PlatformConfiguration final { static void RegisterNatives(tonic::DartLibraryNatives* natives); //---------------------------------------------------------------------------- - /// @brief Retrieves the Window managed by the PlatformConfiguration. + /// @brief Retrieves the Window with the given ID managed by the + /// `PlatformConfiguration`. + /// + /// @param[in] window_id The id of the window to find and return. /// /// @return a pointer to the Window. /// - Window* window() const { return window_.get(); } + Window* get_window(int window_id) { return windows_[window_id].get(); } //---------------------------------------------------------------------------- /// @brief Responds to a previous platform message to the engine from the @@ -404,16 +438,45 @@ class PlatformConfiguration final { /// void CompletePlatformMessageEmptyResponse(int response_id); + //---------------------------------------------------------------------------- + /// @brief Responds to a previously registered key data message from the + /// framework to the engine. + /// + /// For each response_id, this method should be called exactly + /// once. Responding to a response_id that has not been registered + /// or has been invoked will lead to a fatal error. + /// + /// @param[in] response_id The unique id that identifies the original platform + /// message to respond to, created by + /// RegisterKeyDataResponse. + /// @param[in] handled Whether the key data is handled. + /// + void CompleteKeyDataResponse(uint64_t response_id, bool handled); + private: PlatformConfigurationClient* client_; - tonic::DartPersistentValue library_; - - std::unique_ptr window_; - - // We use id 0 to mean that no response is expected. + tonic::DartPersistentValue update_locales_; + tonic::DartPersistentValue update_user_settings_data_; + tonic::DartPersistentValue update_lifecycle_state_; + tonic::DartPersistentValue update_semantics_enabled_; + tonic::DartPersistentValue update_accessibility_features_; + tonic::DartPersistentValue dispatch_platform_message_; + tonic::DartPersistentValue dispatch_key_message_; + tonic::DartPersistentValue dispatch_semantics_action_; + tonic::DartPersistentValue begin_frame_; + tonic::DartPersistentValue draw_frame_; + tonic::DartPersistentValue report_timings_; + + std::unordered_map> windows_; + + // ID starts at 1 because an ID of 0 indicates that no response is expected. int next_response_id_ = 1; std::unordered_map> pending_responses_; + + // ID starts at 1 because an ID of 0 indicates that no response is expected. + uint64_t next_key_response_id_ = 1; + std::unordered_map pending_key_responses_; }; } // namespace flutter diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index 1ff3ce01de52d..a19dc476bd399 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -29,7 +29,8 @@ class DummyPlatformConfigurationClient : public PlatformConfigurationClient { void ScheduleFrame() override {} void Render(Scene* scene) override {} void UpdateSemantics(SemanticsUpdate* update) override {} - void HandlePlatformMessage(fml::RefPtr message) override {} + void HandlePlatformMessage( + std::unique_ptr message) override {} FontCollection& GetFontCollection() override { return font_collection_; } void UpdateIsolateDescription(const std::string isolate_name, int64_t isolate_port) override {} @@ -54,11 +55,14 @@ TEST_F(ShellTest, PlatformConfigurationInitialization) { Dart_NativeArguments args) { PlatformConfiguration* configuration = UIDartState::Current()->platform_configuration(); - ASSERT_NE(configuration->window(), nullptr); - ASSERT_EQ(configuration->window()->viewport_metrics().device_pixel_ratio, - 1.0); - ASSERT_EQ(configuration->window()->viewport_metrics().physical_width, 0.0); - ASSERT_EQ(configuration->window()->viewport_metrics().physical_height, 0.0); + ASSERT_NE(configuration->get_window(0), nullptr); + ASSERT_EQ( + configuration->get_window(0)->viewport_metrics().device_pixel_ratio, + 1.0); + ASSERT_EQ(configuration->get_window(0)->viewport_metrics().physical_width, + 0.0); + ASSERT_EQ(configuration->get_window(0)->viewport_metrics().physical_height, + 0.0); message_latch->Signal(); }; @@ -97,14 +101,19 @@ TEST_F(ShellTest, PlatformConfigurationWindowMetricsUpdate) { PlatformConfiguration* configuration = UIDartState::Current()->platform_configuration(); - ASSERT_NE(configuration->window(), nullptr); - configuration->window()->UpdateWindowMetrics( - ViewportMetrics{2.0, 10.0, 20.0}); - ASSERT_EQ(configuration->window()->viewport_metrics().device_pixel_ratio, - 2.0); - ASSERT_EQ(configuration->window()->viewport_metrics().physical_width, 10.0); - ASSERT_EQ(configuration->window()->viewport_metrics().physical_height, + ASSERT_NE(configuration->get_window(0), nullptr); + configuration->get_window(0)->UpdateWindowMetrics( + ViewportMetrics{2.0, 10.0, 20.0, 22}); + ASSERT_EQ( + configuration->get_window(0)->viewport_metrics().device_pixel_ratio, + 2.0); + ASSERT_EQ(configuration->get_window(0)->viewport_metrics().physical_width, + 10.0); + ASSERT_EQ(configuration->get_window(0)->viewport_metrics().physical_height, 20.0); + ASSERT_EQ( + configuration->get_window(0)->viewport_metrics().physical_touch_slop, + 22); message_latch->Signal(); }; diff --git a/lib/ui/window/platform_message.cc b/lib/ui/window/platform_message.cc index 8423da599ed29..14cd7bb21c9d1 100644 --- a/lib/ui/window/platform_message.cc +++ b/lib/ui/window/platform_message.cc @@ -9,7 +9,7 @@ namespace flutter { PlatformMessage::PlatformMessage(std::string channel, - std::vector data, + fml::MallocMapping data, fml::RefPtr response) : channel_(std::move(channel)), data_(std::move(data)), diff --git a/lib/ui/window/platform_message.h b/lib/ui/window/platform_message.h index 58adac825169e..7fe09cdcac1c7 100644 --- a/lib/ui/window/platform_message.h +++ b/lib/ui/window/platform_message.h @@ -14,29 +14,28 @@ namespace flutter { -class PlatformMessage : public fml::RefCountedThreadSafe { - FML_FRIEND_REF_COUNTED_THREAD_SAFE(PlatformMessage); - FML_FRIEND_MAKE_REF_COUNTED(PlatformMessage); - +class PlatformMessage { public: + PlatformMessage(std::string channel, + fml::MallocMapping data, + fml::RefPtr response); + PlatformMessage(std::string channel, + fml::RefPtr response); + ~PlatformMessage(); + const std::string& channel() const { return channel_; } - const std::vector& data() const { return data_; } + const fml::MallocMapping& data() const { return data_; } bool hasData() { return hasData_; } const fml::RefPtr& response() const { return response_; } - private: - PlatformMessage(std::string channel, - std::vector data, - fml::RefPtr response); - PlatformMessage(std::string channel, - fml::RefPtr response); - ~PlatformMessage(); + fml::MallocMapping releaseData() { return std::move(data_); } + private: std::string channel_; - std::vector data_; + fml::MallocMapping data_; bool hasData_; fml::RefPtr response_; }; diff --git a/lib/ui/window/pointer_data_packet_converter.cc b/lib/ui/window/pointer_data_packet_converter.cc index c9cf2b9ece683..8f90023d5b939 100644 --- a/lib/ui/window/pointer_data_packet_converter.cc +++ b/lib/ui/window/pointer_data_packet_converter.cc @@ -54,7 +54,7 @@ void PointerDataPacketConverter::ConvertPointerData( auto iter = states_.find(pointer_data.device); if (iter != states_.end()) { PointerState state = iter->second; - FML_DCHECK(state.isDown); + FML_DCHECK(state.is_down); UpdatePointerIdentifier(pointer_data, state, false); if (LocationNeedsUpdate(pointer_data, state)) { @@ -67,7 +67,7 @@ void PointerDataPacketConverter::ConvertPointerData( converted_pointers.push_back(synthesized_move_event); } - state.isDown = false; + state.is_down = false; states_[pointer_data.device] = state; converted_pointers.push_back(pointer_data); } @@ -85,14 +85,14 @@ void PointerDataPacketConverter::ConvertPointerData( FML_DCHECK(iter != states_.end()); PointerState state = iter->second; - if (state.isDown) { + if (state.is_down) { // Synthesizes cancel event if the pointer is down. PointerData synthesized_cancel_event = pointer_data; synthesized_cancel_event.change = PointerData::Change::kCancel; synthesized_cancel_event.synthesized = 1; UpdatePointerIdentifier(synthesized_cancel_event, state, false); - state.isDown = false; + state.is_down = false; states_[synthesized_cancel_event.device] = state; converted_pointers.push_back(synthesized_cancel_event); } @@ -119,13 +119,15 @@ void PointerDataPacketConverter::ConvertPointerData( PointerData synthesized_add_event = pointer_data; synthesized_add_event.change = PointerData::Change::kAdd; synthesized_add_event.synthesized = 1; + synthesized_add_event.buttons = 0; state = EnsurePointerState(synthesized_add_event); converted_pointers.push_back(synthesized_add_event); } else { state = iter->second; } - FML_DCHECK(!state.isDown); + FML_DCHECK(!state.is_down); + state.buttons = pointer_data.buttons; if (LocationNeedsUpdate(pointer_data, state)) { UpdateDeltaAndState(pointer_data, state); converted_pointers.push_back(pointer_data); @@ -140,25 +142,28 @@ void PointerDataPacketConverter::ConvertPointerData( PointerData synthesized_add_event = pointer_data; synthesized_add_event.change = PointerData::Change::kAdd; synthesized_add_event.synthesized = 1; + synthesized_add_event.buttons = 0; state = EnsurePointerState(synthesized_add_event); converted_pointers.push_back(synthesized_add_event); } else { state = iter->second; } - FML_DCHECK(!state.isDown); + FML_DCHECK(!state.is_down); if (LocationNeedsUpdate(pointer_data, state)) { // Synthesizes a hover event if the location does not match. PointerData synthesized_hover_event = pointer_data; synthesized_hover_event.change = PointerData::Change::kHover; synthesized_hover_event.synthesized = 1; + synthesized_hover_event.buttons = 0; UpdateDeltaAndState(synthesized_hover_event, state); converted_pointers.push_back(synthesized_hover_event); } UpdatePointerIdentifier(pointer_data, state, true); - state.isDown = true; + state.is_down = true; + state.buttons = pointer_data.buttons; states_[pointer_data.device] = state; converted_pointers.push_back(pointer_data); break; @@ -168,10 +173,11 @@ void PointerDataPacketConverter::ConvertPointerData( auto iter = states_.find(pointer_data.device); FML_DCHECK(iter != states_.end()); PointerState state = iter->second; - FML_DCHECK(state.isDown); + FML_DCHECK(state.is_down); UpdatePointerIdentifier(pointer_data, state, false); UpdateDeltaAndState(pointer_data, state); + state.buttons = pointer_data.buttons; converted_pointers.push_back(pointer_data); break; } @@ -180,7 +186,7 @@ void PointerDataPacketConverter::ConvertPointerData( auto iter = states_.find(pointer_data.device); FML_DCHECK(iter != states_.end()); PointerState state = iter->second; - FML_DCHECK(state.isDown); + FML_DCHECK(state.is_down); UpdatePointerIdentifier(pointer_data, state, false); @@ -188,13 +194,15 @@ void PointerDataPacketConverter::ConvertPointerData( // Synthesizes a move event if the location does not match. PointerData synthesized_move_event = pointer_data; synthesized_move_event.change = PointerData::Change::kMove; + synthesized_move_event.buttons = state.buttons; synthesized_move_event.synthesized = 1; UpdateDeltaAndState(synthesized_move_event, state); converted_pointers.push_back(synthesized_move_event); } - state.isDown = false; + state.is_down = false; + state.buttons = pointer_data.buttons; states_[pointer_data.device] = state; converted_pointers.push_back(pointer_data); break; @@ -213,11 +221,12 @@ void PointerDataPacketConverter::ConvertPointerData( PointerState state = iter->second; if (LocationNeedsUpdate(pointer_data, state)) { - if (state.isDown) { + if (state.is_down) { // Synthesizes a move event if the pointer is down. PointerData synthesized_move_event = pointer_data; synthesized_move_event.signal_kind = PointerData::SignalKind::kNone; synthesized_move_event.change = PointerData::Change::kMove; + synthesized_move_event.buttons = state.buttons; synthesized_move_event.synthesized = 1; UpdateDeltaAndState(synthesized_move_event, state); @@ -228,6 +237,7 @@ void PointerDataPacketConverter::ConvertPointerData( synthesized_hover_event.signal_kind = PointerData::SignalKind::kNone; synthesized_hover_event.change = PointerData::Change::kHover; + synthesized_hover_event.buttons = 0; synthesized_hover_event.synthesized = 1; UpdateDeltaAndState(synthesized_hover_event, state); @@ -250,7 +260,7 @@ PointerState PointerDataPacketConverter::EnsurePointerState( PointerData pointer_data) { PointerState state; state.pointer_identifier = 0; - state.isDown = false; + state.is_down = false; state.physical_x = pointer_data.physical_x; state.physical_y = pointer_data.physical_y; states_[pointer_data.device] = state; diff --git a/lib/ui/window/pointer_data_packet_converter.h b/lib/ui/window/pointer_data_packet_converter.h index 67b19d164ae54..c5871d5d39915 100644 --- a/lib/ui/window/pointer_data_packet_converter.h +++ b/lib/ui/window/pointer_data_packet_converter.h @@ -16,15 +16,23 @@ namespace flutter { //------------------------------------------------------------------------------ -/// The current information about a pointer. This struct is used by -/// PointerDataPacketConverter to fill in necesarry information for raw pointer -/// packet sent from embedding. +/// The current information about a pointer. +/// +/// This struct is used by PointerDataPacketConverter to fill in necessary +/// information for the raw pointer packet sent from embedding. This struct also +/// stores the button state of the last pointer down, up, move, or hover event. +/// When an embedder issues a pointer up or down event where the pointer's +/// position has changed since the last move or hover event, +/// PointerDataPacketConverter generates a synthetic move or hover to notify the +/// framework. In these cases, these events must be issued with the button state +/// prior to the pointer up or down. /// struct PointerState { int64_t pointer_identifier; - bool isDown; + bool is_down; double physical_x; double physical_y; + int64_t buttons; }; //------------------------------------------------------------------------------ diff --git a/lib/ui/window/pointer_data_packet_converter_unittests.cc b/lib/ui/window/pointer_data_packet_converter_unittests.cc index 806dc4679b38d..deee0926738bf 100644 --- a/lib/ui/window/pointer_data_packet_converter_unittests.cc +++ b/lib/ui/window/pointer_data_packet_converter_unittests.cc @@ -15,7 +15,8 @@ void CreateSimulatedPointerData(PointerData& data, // NOLINT PointerData::Change change, int64_t device, double dx, - double dy) { + double dy, + int64_t buttons) { data.time_stamp = 0; data.change = change; data.kind = PointerData::DeviceKind::kTouch; @@ -26,7 +27,7 @@ void CreateSimulatedPointerData(PointerData& data, // NOLINT data.physical_y = dy; data.physical_delta_x = 0.0; data.physical_delta_y = 0.0; - data.buttons = 0; + data.buttons = buttons; data.obscured = 0; data.synthesized = 0; data.pressure = 0.0; @@ -53,7 +54,8 @@ void CreateSimulatedMousePointerData(PointerData& data, // NOLINT double dx, double dy, double scroll_delta_x, - double scroll_delta_y) { + double scroll_delta_y, + int64_t buttons) { data.time_stamp = 0; data.change = change; data.kind = PointerData::DeviceKind::kMouse; @@ -64,7 +66,7 @@ void CreateSimulatedMousePointerData(PointerData& data, // NOLINT data.physical_y = dy; data.physical_delta_x = 0.0; data.physical_delta_y = 0.0; - data.buttons = 0; + data.buttons = buttons; data.obscured = 0; data.synthesized = 0; data.pressure = 0.0; @@ -103,17 +105,18 @@ TEST(PointerDataPacketConverterTest, CanConvetPointerDataPacket) { PointerDataPacketConverter converter; auto packet = std::make_unique(6); PointerData data; - CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kHover, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kHover, 0, 3.0, 0.0, 0); packet->SetPointerData(1, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0, 1); packet->SetPointerData(2, data); - CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 4.0, 1); packet->SetPointerData(3, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0, 0); packet->SetPointerData(4, data); - CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0, + 0); packet->SetPointerData(5, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -151,13 +154,14 @@ TEST(PointerDataPacketConverterTest, CanSynthesizeDownAndUp) { PointerDataPacketConverter converter; auto packet = std::make_unique(4); PointerData data; - CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 3.0, 0.0, 1); packet->SetPointerData(1, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 4.0, 0); packet->SetPointerData(2, data); - CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 4.0, + 0); packet->SetPointerData(3, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -173,10 +177,12 @@ TEST(PointerDataPacketConverterTest, CanSynthesizeDownAndUp) { ASSERT_EQ(result[1].synthesized, 1); ASSERT_EQ(result[1].physical_delta_x, 3.0); ASSERT_EQ(result[1].physical_delta_y, 0.0); + ASSERT_EQ(result[1].buttons, 0); ASSERT_EQ(result[2].change, PointerData::Change::kDown); ASSERT_EQ(result[2].pointer_identifier, 1); ASSERT_EQ(result[2].synthesized, 0); + ASSERT_EQ(result[2].buttons, 1); // A move should be synthesized. ASSERT_EQ(result[3].change, PointerData::Change::kMove); @@ -184,10 +190,12 @@ TEST(PointerDataPacketConverterTest, CanSynthesizeDownAndUp) { ASSERT_EQ(result[3].synthesized, 1); ASSERT_EQ(result[3].physical_delta_x, 0.0); ASSERT_EQ(result[3].physical_delta_y, 4.0); + ASSERT_EQ(result[3].buttons, 1); ASSERT_EQ(result[4].change, PointerData::Change::kUp); ASSERT_EQ(result[4].pointer_identifier, 1); ASSERT_EQ(result[4].synthesized, 0); + ASSERT_EQ(result[4].buttons, 0); ASSERT_EQ(result[5].change, PointerData::Change::kRemove); ASSERT_EQ(result[5].synthesized, 0); @@ -197,19 +205,20 @@ TEST(PointerDataPacketConverterTest, CanUpdatePointerIdentifier) { PointerDataPacketConverter converter; auto packet = std::make_unique(7); PointerData data; - CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1); packet->SetPointerData(1, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0); packet->SetPointerData(2, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1); packet->SetPointerData(3, data); - CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0, 1); packet->SetPointerData(4, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0, 0); packet->SetPointerData(5, data); - CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0, + 0); packet->SetPointerData(6, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -251,14 +260,14 @@ TEST(PointerDataPacketConverterTest, AlwaysForwardMoveEvent) { PointerDataPacketConverter converter; auto packet = std::make_unique(4); PointerData data; - CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1); packet->SetPointerData(1, data); // Creates a move event without a location change. - CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 0.0, 0.0, 1); packet->SetPointerData(2, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0); packet->SetPointerData(3, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -288,29 +297,31 @@ TEST(PointerDataPacketConverterTest, CanWorkWithDifferentDevices) { PointerDataPacketConverter converter; auto packet = std::make_unique(12); PointerData data; - CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0, 0); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1); packet->SetPointerData(1, data); - CreateSimulatedPointerData(data, PointerData::Change::kAdd, 1, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 1, 0.0, 0.0, 0); packet->SetPointerData(2, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 0.0, 0.0, 1); packet->SetPointerData(3, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0); packet->SetPointerData(4, data); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1); packet->SetPointerData(5, data); - CreateSimulatedPointerData(data, PointerData::Change::kMove, 1, 0.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 1, 0.0, 4.0, 1); packet->SetPointerData(6, data); - CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 3.0, 0.0, 1); packet->SetPointerData(7, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 1, 0.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 1, 0.0, 4.0, 0); packet->SetPointerData(8, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 3.0, 0.0, 0); packet->SetPointerData(9, data); - CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 0, 3.0, 0.0, + 0); packet->SetPointerData(10, data); - CreateSimulatedPointerData(data, PointerData::Change::kRemove, 1, 0.0, 4.0); + CreateSimulatedPointerData(data, PointerData::Change::kRemove, 1, 0.0, 4.0, + 0); packet->SetPointerData(11, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -383,9 +394,10 @@ TEST(PointerDataPacketConverterTest, CanSynthesizeAdd) { PointerDataPacketConverter converter; auto packet = std::make_unique(2); PointerData data; - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 330.0, 450.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 330.0, 450.0, + 1); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0, 0); packet->SetPointerData(1, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -398,11 +410,13 @@ TEST(PointerDataPacketConverterTest, CanSynthesizeAdd) { ASSERT_EQ(result[0].physical_x, 330.0); ASSERT_EQ(result[0].physical_y, 450.0); ASSERT_EQ(result[0].synthesized, 1); + ASSERT_EQ(result[0].buttons, 0); ASSERT_EQ(result[1].change, PointerData::Change::kDown); ASSERT_EQ(result[1].physical_x, 330.0); ASSERT_EQ(result[1].physical_y, 450.0); ASSERT_EQ(result[1].synthesized, 0); + ASSERT_EQ(result[1].buttons, 1); // A move should be synthesized. ASSERT_EQ(result[2].change, PointerData::Change::kMove); @@ -411,11 +425,13 @@ TEST(PointerDataPacketConverterTest, CanSynthesizeAdd) { ASSERT_EQ(result[2].physical_x, 0.0); ASSERT_EQ(result[2].physical_y, 0.0); ASSERT_EQ(result[2].synthesized, 1); + ASSERT_EQ(result[2].buttons, 1); ASSERT_EQ(result[3].change, PointerData::Change::kUp); ASSERT_EQ(result[3].physical_x, 0.0); ASSERT_EQ(result[3].physical_y, 0.0); ASSERT_EQ(result[3].synthesized, 0); + ASSERT_EQ(result[3].buttons, 0); } TEST(PointerDataPacketConverterTest, CanHandleThreeFingerGesture) { @@ -425,23 +441,27 @@ TEST(PointerDataPacketConverterTest, CanHandleThreeFingerGesture) { std::vector result; // First finger down. auto packet = std::make_unique(1); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0, 1); packet->SetPointerData(0, data); auto converted_packet = converter.Convert(std::move(packet)); UnpackPointerPacket(result, std::move(converted_packet)); // Second finger down. packet = std::make_unique(1); - CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 33.0, 44.0); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 1, 33.0, 44.0, + 1); packet->SetPointerData(0, data); converted_packet = converter.Convert(std::move(packet)); UnpackPointerPacket(result, std::move(converted_packet)); // Triggers three cancels. packet = std::make_unique(3); - CreateSimulatedPointerData(data, PointerData::Change::kCancel, 1, 33.0, 44.0); + CreateSimulatedPointerData(data, PointerData::Change::kCancel, 1, 33.0, 44.0, + 0); packet->SetPointerData(0, data); - CreateSimulatedPointerData(data, PointerData::Change::kCancel, 0, 0.0, 0.0); + CreateSimulatedPointerData(data, PointerData::Change::kCancel, 0, 0.0, 0.0, + 0); packet->SetPointerData(1, data); - CreateSimulatedPointerData(data, PointerData::Change::kCancel, 2, 40.0, 50.0); + CreateSimulatedPointerData(data, PointerData::Change::kCancel, 2, 40.0, 50.0, + 0); packet->SetPointerData(2, data); converted_packet = converter.Convert(std::move(packet)); UnpackPointerPacket(result, std::move(converted_packet)); @@ -452,24 +472,28 @@ TEST(PointerDataPacketConverterTest, CanHandleThreeFingerGesture) { ASSERT_EQ(result[0].physical_x, 0.0); ASSERT_EQ(result[0].physical_y, 0.0); ASSERT_EQ(result[0].synthesized, 1); + ASSERT_EQ(result[0].buttons, 0); ASSERT_EQ(result[1].change, PointerData::Change::kDown); ASSERT_EQ(result[1].device, 0); ASSERT_EQ(result[1].physical_x, 0.0); ASSERT_EQ(result[1].physical_y, 0.0); ASSERT_EQ(result[1].synthesized, 0); + ASSERT_EQ(result[1].buttons, 1); ASSERT_EQ(result[2].change, PointerData::Change::kAdd); ASSERT_EQ(result[2].device, 1); ASSERT_EQ(result[2].physical_x, 33.0); ASSERT_EQ(result[2].physical_y, 44.0); ASSERT_EQ(result[2].synthesized, 1); + ASSERT_EQ(result[2].buttons, 0); ASSERT_EQ(result[3].change, PointerData::Change::kDown); ASSERT_EQ(result[3].device, 1); ASSERT_EQ(result[3].physical_x, 33.0); ASSERT_EQ(result[3].physical_y, 44.0); ASSERT_EQ(result[3].synthesized, 0); + ASSERT_EQ(result[3].buttons, 1); ASSERT_EQ(result[4].change, PointerData::Change::kCancel); ASSERT_EQ(result[4].device, 1); @@ -491,23 +515,23 @@ TEST(PointerDataPacketConverterTest, CanConvetScroll) { PointerData data; CreateSimulatedMousePointerData(data, PointerData::Change::kAdd, PointerData::SignalKind::kNone, 0, 0.0, 0.0, - 0.0, 0.0); + 0.0, 0.0, 0); packet->SetPointerData(0, data); CreateSimulatedMousePointerData(data, PointerData::Change::kAdd, PointerData::SignalKind::kNone, 1, 0.0, 0.0, - 0.0, 0.0); + 0.0, 0.0, 0); packet->SetPointerData(1, data); CreateSimulatedMousePointerData(data, PointerData::Change::kDown, PointerData::SignalKind::kNone, 1, 0.0, 0.0, - 0.0, 0.0); + 0.0, 0.0, 1); packet->SetPointerData(2, data); CreateSimulatedMousePointerData(data, PointerData::Change::kHover, PointerData::SignalKind::kScroll, 0, 34.0, - 34.0, 30.0, 0.0); + 34.0, 30.0, 0.0, 0); packet->SetPointerData(3, data); CreateSimulatedMousePointerData(data, PointerData::Change::kHover, PointerData::SignalKind::kScroll, 1, 49.0, - 49.0, 50.0, 0.0); + 49.0, 50.0, 0.0, 0); packet->SetPointerData(4, data); auto converted_packet = converter.Convert(std::move(packet)); @@ -536,7 +560,7 @@ TEST(PointerDataPacketConverterTest, CanConvetScroll) { ASSERT_EQ(result[2].physical_y, 0.0); ASSERT_EQ(result[2].synthesized, 0); - // Converter will synthesize a hover to position. + // Converter will synthesize a hover to position for device 0. ASSERT_EQ(result[3].change, PointerData::Change::kHover); ASSERT_EQ(result[3].signal_kind, PointerData::SignalKind::kNone); ASSERT_EQ(result[3].device, 0); @@ -544,6 +568,7 @@ TEST(PointerDataPacketConverterTest, CanConvetScroll) { ASSERT_EQ(result[3].physical_y, 34.0); ASSERT_EQ(result[3].physical_delta_x, 34.0); ASSERT_EQ(result[3].physical_delta_y, 34.0); + ASSERT_EQ(result[3].buttons, 0); ASSERT_EQ(result[3].synthesized, 1); ASSERT_EQ(result[4].change, PointerData::Change::kHover); @@ -554,7 +579,7 @@ TEST(PointerDataPacketConverterTest, CanConvetScroll) { ASSERT_EQ(result[4].scroll_delta_x, 30.0); ASSERT_EQ(result[4].scroll_delta_y, 0.0); - // Converter will synthesize a move to position. + // Converter will synthesize a move to position for device 1. ASSERT_EQ(result[5].change, PointerData::Change::kMove); ASSERT_EQ(result[5].signal_kind, PointerData::SignalKind::kNone); ASSERT_EQ(result[5].device, 1); @@ -562,6 +587,7 @@ TEST(PointerDataPacketConverterTest, CanConvetScroll) { ASSERT_EQ(result[5].physical_y, 49.0); ASSERT_EQ(result[5].physical_delta_x, 49.0); ASSERT_EQ(result[5].physical_delta_y, 49.0); + ASSERT_EQ(result[5].buttons, 1); ASSERT_EQ(result[5].synthesized, 1); ASSERT_EQ(result[6].change, PointerData::Change::kHover); diff --git a/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc index 8db0996ab81eb..961e29dd4cf2e 100644 --- a/lib/ui/window/viewport_metrics.cc +++ b/lib/ui/window/viewport_metrics.cc @@ -10,6 +10,15 @@ namespace flutter { ViewportMetrics::ViewportMetrics() = default; +ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height, + double p_physical_touch_slop) + : device_pixel_ratio(p_device_pixel_ratio), + physical_width(p_physical_width), + physical_height(p_physical_height), + physical_touch_slop(p_physical_touch_slop) {} + ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, @@ -24,7 +33,8 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_system_gesture_inset_top, double p_physical_system_gesture_inset_right, double p_physical_system_gesture_inset_bottom, - double p_physical_system_gesture_inset_left) + double p_physical_system_gesture_inset_left, + double p_physical_touch_slop) : device_pixel_ratio(p_device_pixel_ratio), physical_width(p_physical_width), physical_height(p_physical_height), @@ -41,14 +51,46 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, p_physical_system_gesture_inset_right), physical_system_gesture_inset_bottom( p_physical_system_gesture_inset_bottom), - physical_system_gesture_inset_left(p_physical_system_gesture_inset_left) { + physical_system_gesture_inset_left(p_physical_system_gesture_inset_left), + physical_touch_slop(p_physical_touch_slop) {} + +bool operator==(const ViewportMetrics& a, const ViewportMetrics& b) { + return a.device_pixel_ratio == b.device_pixel_ratio && + a.physical_width == b.physical_width && + a.physical_height == b.physical_height && + a.physical_padding_top == b.physical_padding_top && + a.physical_padding_right == b.physical_padding_right && + a.physical_padding_bottom == b.physical_padding_bottom && + a.physical_padding_left == b.physical_padding_left && + a.physical_view_inset_top == b.physical_view_inset_top && + a.physical_view_inset_right == b.physical_view_inset_right && + a.physical_view_inset_bottom == b.physical_view_inset_bottom && + a.physical_view_inset_left == b.physical_view_inset_left && + a.physical_system_gesture_inset_top == + b.physical_system_gesture_inset_top && + a.physical_system_gesture_inset_right == + b.physical_system_gesture_inset_right && + a.physical_system_gesture_inset_bottom == + b.physical_system_gesture_inset_bottom && + a.physical_system_gesture_inset_left == + b.physical_system_gesture_inset_left && + a.physical_touch_slop == b.physical_touch_slop; } -ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, - double p_physical_width, - double p_physical_height) - : device_pixel_ratio(p_device_pixel_ratio), - physical_width(p_physical_width), - physical_height(p_physical_height) {} +std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a) { + os << "DPR: " << a.device_pixel_ratio << " " + << "Size: [" << a.physical_width << "W " << a.physical_height << "H] " + << "Padding: [" << a.physical_padding_top << "T " + << a.physical_padding_right << "R " << a.physical_padding_bottom << "B " + << a.physical_padding_left << "L] " + << "Insets: [" << a.physical_view_inset_top << "T " + << a.physical_view_inset_right << "R " << a.physical_view_inset_bottom + << "B " << a.physical_view_inset_left << "L] " + << "Gesture Insets: [" << a.physical_system_gesture_inset_top << "T " + << a.physical_system_gesture_inset_right << "R " + << a.physical_system_gesture_inset_bottom << "B " + << a.physical_system_gesture_inset_left << "L]"; + return os; +} } // namespace flutter diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index 01081e3f345f1..3224b2930609a 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -5,10 +5,16 @@ #ifndef FLUTTER_LIB_UI_WINDOW_VIEWPORT_METRICS_H_ #define FLUTTER_LIB_UI_WINDOW_VIEWPORT_METRICS_H_ +#include + namespace flutter { struct ViewportMetrics { ViewportMetrics(); + ViewportMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height, + double p_physical_touch_slop); ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, @@ -23,13 +29,8 @@ struct ViewportMetrics { double p_physical_system_gesture_inset_top, double p_physical_system_gesture_inset_right, double p_physical_system_gesture_inset_bottom, - double p_physical_system_gesture_inset_left); - - // Create a ViewportMetrics instance that doesn't include depth, padding, or - // insets. - ViewportMetrics(double p_device_pixel_ratio, - double p_physical_width, - double p_physical_height); + double p_physical_system_gesture_inset_left, + double p_physical_touch_slop); double device_pixel_ratio = 1.0; double physical_width = 0; @@ -46,26 +47,11 @@ struct ViewportMetrics { double physical_system_gesture_inset_right = 0; double physical_system_gesture_inset_bottom = 0; double physical_system_gesture_inset_left = 0; + double physical_touch_slop = -1.0; }; -struct LogicalSize { - double width = 0.0; - double height = 0.0; -}; - -struct LogicalInset { - double left = 0.0; - double top = 0.0; - double right = 0.0; - double bottom = 0.0; -}; - -struct LogicalMetrics { - LogicalSize size; - double scale = 1.0; - LogicalInset padding; - LogicalInset view_inset; -}; +bool operator==(const ViewportMetrics& a, const ViewportMetrics& b); +std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a); } // namespace flutter diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 1779998945554..66f5e2e7dbef0 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -11,7 +11,8 @@ namespace flutter { -Window::Window(ViewportMetrics metrics) : viewport_metrics_(metrics) { +Window::Window(int64_t window_id, ViewportMetrics metrics) + : window_id_(window_id), viewport_metrics_(metrics) { library_.Set(tonic::DartState::Current(), Dart_LookupLibrary(tonic::ToDart("dart:ui"))); } @@ -35,6 +36,24 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { library_.value(), "_dispatchPointerDataPacket", {data_handle})); } +void Window::DispatchKeyDataPacket(const KeyDataPacket& packet, + uint64_t response_id) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + + const std::vector& buffer = packet.data(); + Dart_Handle data_handle = + tonic::DartByteData::Create(buffer.data(), buffer.size()); + if (Dart_IsError(data_handle)) { + return; + } + tonic::LogIfError( + tonic::DartInvokeField(library_.value(), "_dispatchKeyData", + {data_handle, tonic::ToDart(response_id)})); +} + void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { viewport_metrics_ = metrics; @@ -45,23 +64,22 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { tonic::DartState::Scope scope(dart_state); tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_updateWindowMetrics", - { - tonic::ToDart(metrics.device_pixel_ratio), - tonic::ToDart(metrics.physical_width), - tonic::ToDart(metrics.physical_height), - tonic::ToDart(metrics.physical_padding_top), - tonic::ToDart(metrics.physical_padding_right), - tonic::ToDart(metrics.physical_padding_bottom), - tonic::ToDart(metrics.physical_padding_left), - tonic::ToDart(metrics.physical_view_inset_top), - tonic::ToDart(metrics.physical_view_inset_right), - tonic::ToDart(metrics.physical_view_inset_bottom), - tonic::ToDart(metrics.physical_view_inset_left), - tonic::ToDart(metrics.physical_system_gesture_inset_top), - tonic::ToDart(metrics.physical_system_gesture_inset_right), - tonic::ToDart(metrics.physical_system_gesture_inset_bottom), - tonic::ToDart(metrics.physical_system_gesture_inset_left), - })); + {tonic::ToDart(window_id_), tonic::ToDart(metrics.device_pixel_ratio), + tonic::ToDart(metrics.physical_width), + tonic::ToDart(metrics.physical_height), + tonic::ToDart(metrics.physical_padding_top), + tonic::ToDart(metrics.physical_padding_right), + tonic::ToDart(metrics.physical_padding_bottom), + tonic::ToDart(metrics.physical_padding_left), + tonic::ToDart(metrics.physical_view_inset_top), + tonic::ToDart(metrics.physical_view_inset_right), + tonic::ToDart(metrics.physical_view_inset_bottom), + tonic::ToDart(metrics.physical_view_inset_left), + tonic::ToDart(metrics.physical_system_gesture_inset_top), + tonic::ToDart(metrics.physical_system_gesture_inset_right), + tonic::ToDart(metrics.physical_system_gesture_inset_bottom), + tonic::ToDart(metrics.physical_system_gesture_inset_left), + tonic::ToDart(metrics.physical_touch_slop)})); } } // namespace flutter diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index 172cf6b8c2ae5..9a6847d3c46a6 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -5,10 +5,12 @@ #ifndef FLUTTER_LIB_UI_WINDOW_WINDOW_H_ #define FLUTTER_LIB_UI_WINDOW_WINDOW_H_ +#include #include #include #include +#include "flutter/lib/ui/window/key_data_packet.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -18,17 +20,30 @@ namespace flutter { class Window final { public: - explicit Window(ViewportMetrics metrics); + Window(int64_t window_id, ViewportMetrics metrics); ~Window(); + int window_id() const { return window_id_; } + const ViewportMetrics& viewport_metrics() const { return viewport_metrics_; } + // Dispatch a packet to the framework that indicates one or a few pointer + // events. void DispatchPointerDataPacket(const PointerDataPacket& packet); + // Dispatch a packet to the framework that indicates a key event. + // + // The `response_id` is used to label the response of whether the key event + // is handled by the framework, typically the return value of + // PlatformConfiguration::RegisterKeyDataResponse. + // It should be used later in + // PlatformConfiguration::CompleteKeyDataResponse. + void DispatchKeyDataPacket(const KeyDataPacket& packet, uint64_t response_id); void UpdateWindowMetrics(const ViewportMetrics& metrics); private: tonic::DartPersistentValue library_; + int64_t window_id_; ViewportMetrics viewport_metrics_; }; diff --git a/lib/web_ui/README.md b/lib/web_ui/README.md new file mode 100644 index 0000000000000..cf9f1931032a3 --- /dev/null +++ b/lib/web_ui/README.md @@ -0,0 +1,29 @@ +# Flutter Web Engine + +This directory contains the source code for the Web Engine. The easiest way to +hack on the Web Engine is using the `felt` tool. See dev/README.md for details. + +## Rolling CanvasKit + +CanvasKit is versioned separately from Skia and rolled manually. Flutter +consumes a pre-built CanvasKit provided by the Skia team, currently hosted on +unpkg.com. When a new version of CanvasKit is available (check +https://www.npmjs.com/package/canvaskit-wasm or consult the Skia team +directly), follow these steps to roll to the new version: + +- Make sure you have `depot_tools` installed (if you are regularly hacking on + the engine code, you probably do). +- If not already authenticated with CIPD, run `cipd auth-login` and follow + instructions (this step requires sufficient privileges; contact + #hackers-infra-🌡 on Flutter's Discord server). +- Edit `dev/canvaskit_lock.yaml` and update the value of `canvaskit_version` + to the new version. +- Run `dart dev/canvaskit_roller.dart` and make sure it completes successfully. + The script uploads the new version of CanvasKit to the + `flutter/web/canvaskit_bundle` CIPD package, and writes the CIPD package + instance ID to the DEPS file. +- Send a pull request containing the above file changes. If the new version + contains breaking changes, the PR must also contain corresponding fixes. + +If you have questions, contact the Flutter Web team on Flutter Discord on the +#hackers-web-🌍 channel. diff --git a/lib/web_ui/analysis_options.yaml b/lib/web_ui/analysis_options.yaml index 869fffe973553..60918b30bb210 100644 --- a/lib/web_ui/analysis_options.yaml +++ b/lib/web_ui/analysis_options.yaml @@ -1,89 +1,11 @@ -# This is copy of the root analysis_options.yaml. As we clean up the Web code, -# we'll be uncommenting rules and gradually fix the code. When all rules are -# uncommented, we'll delete this file and simply inherit the root options. +# Web-specific analysis options. +# +# As of today the web code contains quite a few deviations from the repo-wide +# analysis options due to having been migrated from google3. The ultimate goal +# is to clean up our code and delete this file. -analyzer: - enable-experiment: - - non-nullable - strong-mode: - # TODO(uncomment) implicit-casts: false - implicit-dynamic: false - errors: - missing_required_param: warning - missing_return: warning - native_function_body_in_non_sdk_code: ignore - todo: ignore +include: ../../analysis_options.yaml linter: rules: - - always_declare_return_types - - always_put_control_body_on_new_line - # TODO(uncomment) - always_specify_types - # TODO(uncomment) - annotate_overrides - # TODO(uncomment) - avoid_classes_with_only_static_members - # TODO(uncomment) - avoid_empty_else - # TODO(uncomment) - avoid_function_literals_in_foreach_calls - # TODO(uncomment) - avoid_init_to_null - # TODO(uncomment) - avoid_null_checks_in_equality_operators - # TODO(uncomment) - avoid_relative_lib_imports - # TODO(uncomment) - avoid_renaming_method_parameters - # TODO(uncomment) - avoid_return_types_on_setters - # TODO(uncomment) - avoid_slow_async_io - # TODO(uncomment) - await_only_futures - # TODO(uncomment) - camel_case_types - # TODO(uncomment) - cancel_subscriptions - # TODO(uncomment) - control_flow_in_finally - # TODO(uncomment) - directives_ordering - # TODO(uncomment) - empty_catches - # TODO(uncomment) - empty_constructor_bodies - # TODO(uncomment) - empty_statements - # TODO(uncomment) - hash_and_equals - # TODO(uncomment) - implementation_imports - # TODO(uncomment) - iterable_contains_unrelated_type - # TODO(uncomment) - library_names - # TODO(uncomment) - library_prefixes - # TODO(uncomment) - list_remove_unrelated_type - # TODO(uncomment) - no_adjacent_strings_in_list - # TODO(uncomment) - no_duplicate_case_values - # TODO(uncomment) - non_constant_identifier_names - # TODO(uncomment) - overridden_fields - # TODO(uncomment) - package_api_docs - # TODO(uncomment) - package_names - # TODO(uncomment) - package_prefixed_library_names - # TODO(uncomment) - prefer_adjacent_string_concatenation - # TODO(uncomment) - prefer_asserts_in_initializer_lists - # TODO(uncomment) - prefer_collection_literals - # TODO(uncomment) - prefer_conditional_assignment - # TODO(uncomment) - prefer_const_constructors - # TODO(uncomment) - prefer_const_constructors_in_immutables - # TODO(uncomment) - prefer_const_declarations - # TODO(uncomment) - prefer_const_literals_to_create_immutables - # TODO(uncomment) - prefer_contains - # TODO(uncomment) - prefer_equal_for_default_values - # TODO(uncomment) - prefer_final_locals - # TODO(uncomment) - prefer_foreach - # TODO(uncomment) - prefer_generic_function_type_aliases - # TODO(uncomment) - prefer_initializing_formals - # TODO(uncomment) - prefer_is_empty - # TODO(uncomment) - prefer_is_not_empty - # TODO(uncomment) - prefer_single_quotes - # TODO(uncomment) - prefer_typing_uninitialized_variables - # TODO(uncomment) - public_member_api_docs - # TODO(uncomment) - recursive_getters - # TODO(uncomment) - slash_for_doc_comments - # TODO(uncomment) - sort_unnamed_constructors_first - # TODO(uncomment) - test_types_in_equals - # TODO(uncomment) - throw_in_finally - # TODO(uncomment) - type_init_formals - # TODO(uncomment) - unnecessary_brace_in_string_interps - # TODO(uncomment) - unnecessary_const - # TODO(uncomment) - unnecessary_getters_setters - # TODO(uncomment) - unnecessary_new - # TODO(uncomment) - unnecessary_null_aware_assignments - # TODO(uncomment) - unnecessary_null_in_if_null_operators - # TODO(uncomment) - unnecessary_overrides - # TODO(uncomment) - unnecessary_parenthesis - # TODO(uncomment) - unnecessary_this - # TODO(uncomment) - unrelated_type_equality_checks - # TODO(uncomment) - use_rethrow_when_possible - # TODO(uncomment) - valid_regexps + public_member_api_docs: false \ No newline at end of file diff --git a/lib/web_ui/build.canvaskit.yaml b/lib/web_ui/build.canvaskit.yaml deleted file mode 100644 index 8be2596d000a7..0000000000000 --- a/lib/web_ui/build.canvaskit.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Build configuration used by CanvasKit tests. When building for CanvasKit -# we must pass FLUTTER_WEB_USE_SKIA=true to dart2js. -# -# See also `build.html.yaml`. -targets: - $default: - builders: - build_web_compilers|entrypoint: - options: - compiler: dart2js - dart2js_args: - - --no-minify - - --disable-inlining - - --enable-asserts - - --enable-experiment=non-nullable - - --no-sound-null-safety - - -DFLUTTER_WEB_USE_SKIA=true - generate_for: - include: - - test/canvaskit/**.dart diff --git a/lib/web_ui/build.html.yaml b/lib/web_ui/build.html.yaml deleted file mode 100644 index 36ea478d17df4..0000000000000 --- a/lib/web_ui/build.html.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Build configuration used by HTML (non-CanvasKit) tests. -# -# See also `build.canvaskit.yaml`. -targets: - $default: - builders: - build_web_compilers|entrypoint: - options: - compiler: dart2js - dart2js_args: - - --no-minify - - --disable-inlining - - --enable-asserts - - --enable-experiment=non-nullable - - --no-sound-null-safety - generate_for: - include: - - test/**.dart - exclude: - - test/**vm_test.dart - - test/canvaskit/**.dart diff --git a/lib/web_ui/dev/README.md b/lib/web_ui/dev/README.md index d530e62053353..940ef99917214 100644 --- a/lib/web_ui/dev/README.md +++ b/lib/web_ui/dev/README.md @@ -7,7 +7,7 @@ `felt` supports multiple commands as follows: 1. **`felt check-licenses`**: Checks that all Dart and JS source code files contain the correct license headers. -2. **`felt test`**: Runs all or some tests depending on the passed arguments. +2. **`felt test`**: Runs all or some tests depending on the passed arguments. It supports a watch mode for convenience. 3. **`felt build`**: Builds the engine locally so it can be used by Flutter apps. It also supports a watch mode for more convenience. You could also run `felt help` or `felt help ` to get more information about the available commands and arguments. @@ -31,9 +31,16 @@ If you don't want to add `felt` to your path, you can still invoke it using a re ## Speeding up your builds and tests -If you are a Google employee, you can use an internal instance of Goma to parallelize your builds. Because Goma compiles code on remote servers, this option is effective even on low-powered laptops. +If you are a Google employee, you can use an internal instance of Goma (go/ma) +to parallelize your builds. Because Goma compiles code on remote servers, this +option is particularly effective for building on low-powered laptops. -By default, when compiling Dart code to JavaScript, we use 8 `dart2js` workers. +Concurrency of various build steps can be configured via environment variables: + +- `FELT_DART2JS_CONCURRENCY` specifies the number of concurrent `dart2js` + processes used to compile tests. Default value is 8. +- `FELT_TEST_CONCURRENCY` specifies the number of tests run concurrently. + Default value is 10. ## Running web engine tests @@ -43,6 +50,18 @@ To run all tests on Chrome. This will run both integration tests and the unit te felt test ``` +To run a specific test: + +``` +felt test test/engine/util_test.dart +``` + +To enable watch mode so that the test re-runs on every change: + +``` +felt test --watch test/engine/util_test.dart +``` + To run unit tests only: ``` @@ -135,7 +154,7 @@ the new revision. If you are making changes in the `felt` tool itself, you need to be aware of Dart snapshots. We create a Dart snapshot of the `felt` tool to make the startup faster. -To make sure you are running the `felt` tool with your changes included, you would need to stop using the snapshot. This can be achived through the environment variable `FELT_USE_SNAPSHOT`: +To make sure you are running the `felt` tool with your changes included, you would need to stop using the snapshot. This can be achieved through the environment variable `FELT_USE_SNAPSHOT`: ``` FELT_USE_SNAPSHOT=false felt @@ -151,19 +170,33 @@ _**Note**: if `FELT_USE_SNAPSHOT` is omitted or has any value other than "false" ## Upgrade Browser Version -Since the engine code and infra recipes do not live in the same repository there are few steps to follow in order to upgrade a browser's version. For now these instructins are most relevant to Chrome. +Since the engine code and infra recipes do not live in the same repository +there are few steps to follow in order to upgrade a browser's version. For +now these instructins are most relevant to Chrome. 1. Dowload the binaries for the new browser/driver for each operaing system (macOS, linux, windows). 2. Create CIPD packages for these packages. (More documentation is available for Googlers. go/cipd-flutter-web) -3. Add the new browser version to the recipe. Do not remove the old one. This recipe will apply to all PRs as soon as it is merged. However, not all PRs will have the up to date code for a while. -4. Update the version in this repo. Do this by changing the related fields in `browser_lock.yaml` file. -5. After a few days don't forget to remove the old version from the LUCI recipe. +3. Update the version in this repo. Do this by changing the related fields in `browser_lock.yaml` file. -Note that for LUCI builders, both unit and integration tests are using the same browser. +Note that for LUCI builders, for Chrome both unit and integration tests are using the same browser. (For Firefox [issue](https://github.com/flutter/flutter/issues/71617) Some useful links: 1. For Chrome downloads [link](https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html) -2. Browser and driver CIPD [packages](https://chrome-infra-packages.appspot.com/p/flutter_internal) (Note: Access rights are restricted for these packages.) +2. Browser and driver CIPD [packages](https://chrome-infra-packages.appspot.com/p/flutter_internal) (note: access is restricted for these packages) 3. LUCI web [recipe](https://flutter.googlesource.com/recipes/+/refs/heads/master/recipes/web_engine.py) 4. More general reading on CIPD packages [link](https://chromium.googlesource.com/chromium/src.git/+/master/docs/cipd.md#What-is-CIPD) + +## Troubleshooting + +### Can't load Kernel binary: Invalid kernel binary format version. + +Some times `.dart_tool` cache invalidation fails, and you'll end up with a cached version of `felt` that is not compatible with the Dart SDK that you're using. + +In that case, any invocation to `felt` will fail with: + +`Can't load Kernel binary: Invalid kernel binary format version.` + +The solution is to delete the cached `felt.snapshot` files within `engine/src/flutter/lib/web_ui`: + +**`rm .dart_tool/felt.snapshot*`** diff --git a/lib/web_ui/dev/analysis_options.yaml b/lib/web_ui/dev/analysis_options.yaml deleted file mode 100644 index ac1dc7ce40a6a..0000000000000 --- a/lib/web_ui/dev/analysis_options.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# This is a temporary file used to clean up dev/ before other directories -include: ../analysis_options.yaml - -analyzer: - strong-mode: - implicit-casts: false - implicit-dynamic: false diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index a63aa9bfc999b..0b8c79255c6d4 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -2,18 +2,52 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:math' as math; +import 'package:image/image.dart'; import 'package:pedantic/pedantic.dart'; import 'package:stack_trace/stack_trace.dart'; +import 'package:test_api/src/backend/runtime.dart'; import 'package:typed_data/typed_buffers.dart'; -import 'package:test_api/src/utils.dart' show getErrorMessage; +/// Provides the environment for a specific web browser. +abstract class BrowserEnvironment { + /// The [Runtime] used by `package:test` to identify this browser type. + Runtime get packageTestRuntime; -import 'common.dart'; // ignore: unused_import + /// The name of the configuration YAML file used to configure `package:test`. + /// + /// The configuration file is expected to be a direct child of the `web_ui` + /// directory. + String get packageTestConfigurationYamlFile; + + /// Prepares the OS environment to run tests for this browser. + /// + /// This may include things like staring web drivers, iOS Simulators, and/or + /// Android emulators. + /// + /// Typically the browser environment is prepared once and supports multiple + /// browser instances. + Future prepareEnvironment(); + + /// Launches a browser instance. + /// + /// The browser will be directed to open the provided [url]. + /// + /// If [debug] is true and the browser supports debugging, launches the + /// browser in debug mode by pausing test execution after the code is loaded + /// but before calling the `main()` function of the test, giving the + /// developer a chance to set breakpoints. + Browser launchBrowserInstance(Uri url, {bool debug = false}); + + /// Returns the screenshot manager used by this browser. + /// + /// If the browser does not support screenshots, returns null. + ScreenshotManager? getScreenshotManager(); +} /// An interface for running browser instances. /// @@ -28,34 +62,34 @@ abstract class Browser { /// The Observatory URL for this browser. /// - /// This will return `null` for browsers that aren't running the Dart VM, or + /// Returns `null` for browsers that aren't running the Dart VM, or /// if the Observatory URL can't be found. - Future get observatoryUrl => null; + Future? get observatoryUrl => null; /// The remote debugger URL for this browser. /// - /// This will return `null` for browsers that don't support remote debugging, + /// Returns `null` for browsers that don't support remote debugging, /// or if the remote debugging URL can't be found. - Future get remoteDebuggerUrl => null; + Future? get remoteDebuggerUrl => null; /// The underlying process. /// /// This will fire once the process has started successfully. Future get _process => _processCompleter.future; - final _processCompleter = Completer(); + final Completer _processCompleter = Completer(); /// Whether [close] has been called. - var _closed = false; + bool _closed = false; /// A future that completes when the browser exits. /// /// If there's a problem starting or running the browser, this will complete /// with an error. Future get onExit => _onExitCompleter.future; - final _onExitCompleter = Completer(); + final Completer _onExitCompleter = Completer(); /// Standard IO streams for the underlying browser process. - final _ioSubscriptions = []; + final List> _ioSubscriptions = >[]; /// Creates a new browser. /// @@ -63,15 +97,15 @@ abstract class Browser { /// which asynchronously returns the browser process. Any errors in /// [startBrowser] (even those raised asynchronously after it returns) are /// piped to [onExit] and will cause the browser to be killed. - Browser(Future startBrowser()) { + Browser(Future Function() startBrowser) { // Don't return a Future here because there's no need for the caller to wait // for the process to actually start. They should just wait for the HTTP // request instead. - runZoned(() async { - var process = await startBrowser(); + runZonedGuarded(() async { + final Process process = await startBrowser(); _processCompleter.complete(process); - var output = Uint8Buffer(); + final Uint8Buffer output = Uint8Buffer(); void drainOutput(Stream> stream) { try { _ioSubscriptions @@ -83,7 +117,7 @@ abstract class Browser { drainOutput(process.stdout); drainOutput(process.stderr); - var exitCode = await process.exitCode; + final int exitCode = await process.exitCode; // This hack dodges an otherwise intractable race condition. When the user // presses Control-C, the signal is sent to the browser and the test @@ -96,12 +130,12 @@ abstract class Browser { // resolve the ambiguity is to wait a brief amount of time and see if this // browser is actually closed. if (!_closed && exitCode < 0) { - await Future.delayed(Duration(milliseconds: 200)); + await Future.delayed(const Duration(milliseconds: 200)); } if (!_closed && exitCode != 0) { - var outputString = utf8.decode(output); - var message = '$name failed with exit code $exitCode.'; + final String outputString = utf8.decode(output); + String message = '$name failed with exit code $exitCode.'; if (outputString.isNotEmpty) { message += '\nStandard output:\n$outputString'; } @@ -110,24 +144,24 @@ abstract class Browser { } _onExitCompleter.complete(); - }, onError: (dynamic error, StackTrace stackTrace) { + }, (dynamic error, StackTrace? stackTrace) { // Ignore any errors after the browser has been closed. if (_closed) { return; } // Make sure the process dies even if the error wasn't fatal. - _process.then((process) => process.kill()); + _process.then((Process process) => process.kill()); + + stackTrace ??= Trace.current(); - if (stackTrace == null) { - stackTrace = Trace.current(); - } if (_onExitCompleter.isCompleted) { return; } _onExitCompleter.completeError( - Exception('Failed to run $name: ${getErrorMessage(error)}.'), - stackTrace); + Exception('Failed to run $name: $error.'), + stackTrace, + ); }); } @@ -135,13 +169,13 @@ abstract class Browser { /// /// Returns the same [Future] as [onExit], except that it won't emit /// exceptions. - Future close() async { + Future close() async { _closed = true; // If we don't manually close the stream the test runner can hang. // For example this happens with Chrome Headless. // See SDK issue: https://github.com/dart-lang/sdk/issues/31264 - for (var stream in _ioSubscriptions) { + for (final StreamSubscription stream in _ioSubscriptions) { unawaited(stream.cancel()); } @@ -151,3 +185,18 @@ abstract class Browser { return onExit.catchError((dynamic _) {}); } } + +/// Interface for capturing screenshots from a browser. +abstract class ScreenshotManager { + /// Capture a screenshot. + /// + /// Please read the details for the implementing classes. + Future capture(math.Rectangle region); + + /// Suffix to be added to the end of the filename. + /// + /// Example file names: + /// - Chrome, no-suffix: backdrop_filter_clip_moved.actual.png + /// - iOS Safari: backdrop_filter_clip_moved.iOS_Safari.actual.png + String get filenameSuffix; +} diff --git a/lib/web_ui/dev/browser_lock.dart b/lib/web_ui/dev/browser_lock.dart new file mode 100644 index 0000000000000..f32815abd9827 --- /dev/null +++ b/lib/web_ui/dev/browser_lock.dart @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:path/path.dart' as path; +import 'package:yaml/yaml.dart'; + +import 'common.dart'; +import 'environment.dart'; + +/// Returns the browser configuration based on the `browser_lock.yaml` file in +/// the current engine workspace. +final BrowserLock browserLock = BrowserLock(); + +/// Provides access to the contents of the `browser_lock.yaml` file. +class BrowserLock { + factory BrowserLock() { + final io.File lockFile = io.File( + path.join(environment.webUiRootDir.path, 'dev', 'browser_lock.yaml'), + ); + final YamlMap yaml = loadYaml(lockFile.readAsStringSync()) as YamlMap; + return BrowserLock._fromYaml(yaml); + } + + BrowserLock._fromYaml(YamlMap yaml) : + chromeLock = ChromeLock._fromYaml(yaml['chrome'] as YamlMap), + firefoxLock = FirefoxLock._fromYaml(yaml['firefox'] as YamlMap), + edgeLock = EdgeLock._fromYaml(yaml['edge'] as YamlMap), + safariIosLock = SafariIosLock._fromYaml(yaml['safari_ios'] as YamlMap); + + final ChromeLock chromeLock; + final FirefoxLock firefoxLock; + final EdgeLock edgeLock; + final SafariIosLock safariIosLock; +} + +class ChromeLock { + ChromeLock._fromYaml(YamlMap yaml) : + linux = (yaml['Linux'] as int).toString(), + mac = (yaml['Mac'] as int).toString(), + windows = (yaml['Win'] as int).toString(); + + final String linux; + final String mac; + final String windows; + + /// Return the version of Chromium to use for the current operating system. + String get versionForCurrentPlatform { + return PlatformBinding.instance.getChromeBuild(this); + } +} + +class FirefoxLock { + FirefoxLock._fromYaml(YamlMap yaml) : + version = yaml['version'] as String; + + final String version; +} + +class EdgeLock { + EdgeLock._fromYaml(YamlMap yaml) : + launcherVersion = yaml['launcher_version'] as String; + + final String launcherVersion; +} + +class SafariIosLock { + SafariIosLock._fromYaml(YamlMap yaml) : + majorVersion = yaml['major_version'] as int, + minorVersion = yaml['minor_version'] as int, + device = yaml['device'] as String, + heightOfHeader = yaml['height_of_header'] as int, + heightOfFooter = yaml['height_of_footer'] as int, + scaleFactor = yaml['scale_factor'] as double; + + final int majorVersion; + final int minorVersion; + final String device; + final int heightOfHeader; + final int heightOfFooter; + final double scaleFactor; + + String get simulatorDescription => '$device with iOS $majorVersion.$minorVersion'; +} diff --git a/lib/web_ui/dev/browser_lock.yaml b/lib/web_ui/dev/browser_lock.yaml index b1a83fe5869a0..f39818799fde7 100644 --- a/lib/web_ui/dev/browser_lock.yaml +++ b/lib/web_ui/dev/browser_lock.yaml @@ -1,31 +1,31 @@ -## Driver version in use. -## For an integration test to run, the browser's major version and the driver's -## major version should be equal. Please make sure the major version of -## the binary for `chrome` is the same with `required_driver_version`. -## (Major version meaning: For a browser that has version 13.0.5, the major -## version is 13.) -## Please refer to README's `Upgrade Browser Version` section for more details -## on how to update the version number. -required_driver_version: - ## Make sure the major version of the binary in `browser_lock.yaml` is the - ## same for Chrome. - chrome: '84' +# Please refer to the "Upgrade Browser Version" section in the README.md for +# more details on how to update browser version numbers. chrome: # It seems Chrome can't always release from the same build for all operating # systems, so we specify per-OS build number. + # + # The OS names here must match what recipe Python expression + # `self.m.platform.name.capitalize()` evaluates to. See: + # + # recipe_modules/web_util/api.py Linux: 768968 # Major version 84 Mac: 768985 # Major version 84 Win: 768975 # Major version 84 + +## Firefox does not use CIPD. To update the version, simply update it in this +## file. firefox: - version: '72.0' + version: '83.0' + edge: launcher_version: '1.2.0.0' -ios-safari: + +safari_ios: # Make sure this version is the same version supported by LUCI macOS bots. # XCode on these bots will be updated once a year, do not forget to update # `heightOfHeader` during this time. - majorVersion: 13 - minorVersion: 0 + major_version: 13 + minor_version: 0 device: 'iPhone 11' # `xcrun simctl` command is used to take screenshots. It takes the screenshot # of the entire simulator. Therefore we need to crop all the parts other than @@ -34,10 +34,10 @@ ios-safari: # `heightOfHeader` is the number of pixels taken by the phone's header menu # and the browsers address bar. # TODO: https://github.com/flutter/flutter/issues/65672 - heightOfHeader: 189 + height_of_header: 189 # `heightOfFooter` is the number of pixels taken by the phone's navigation # menu. - heightOfFooter: 250 + height_of_footer: 250 # Most of the time tests use a portion of the screen to compare goldens # instead of the entire screen. This area is reprented by a rectangle # when taking screenshots. However the rectangle dimensions are in logical @@ -46,9 +46,4 @@ ios-safari: # transformation by a scale_factor (a.k.a. we perform isotropic scaling). # This value will be differ depending on the phone. # For iOS 13.0 iPhone 11 Pro, this number is 1.15. - scaleFactor: 1.00 - -## geckodriver is used for testing Firefox Browser. It works with multiple -## Firefox Browser versions. -## See: https://github.com/mozilla/geckodriver/releases -geckodriver: 'v0.26.0' + scale_factor: 1.00 diff --git a/lib/web_ui/dev/build.dart b/lib/web_ui/dev/build.dart index 5a23dd45f86cf..459dc19582276 100644 --- a/lib/web_ui/dev/build.dart +++ b/lib/web_ui/dev/build.dart @@ -2,25 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; +import 'dart:io' show Platform; import 'package:args/command_runner.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:watcher/watcher.dart'; +import 'package:watcher/src/watch_event.dart'; import 'environment.dart'; +import 'pipeline.dart'; import 'utils.dart'; -class BuildCommand extends Command with ArgUtils { +class BuildCommand extends Command with ArgUtils { BuildCommand() { - argParser - ..addFlag( + argParser.addFlag( 'watch', + defaultsTo: false, abbr: 'w', help: 'Run the build in watch mode so it rebuilds whenever a change' - 'is made.', + 'is made. Disabled by default.', ); } @@ -36,165 +36,69 @@ class BuildCommand extends Command with ArgUtils { FutureOr run() async { final FilePath libPath = FilePath.fromWebUi('lib'); final Pipeline buildPipeline = Pipeline(steps: [ - gn, - () => ninja(), + GnPipelineStep(), + NinjaPipelineStep(), ]); - await buildPipeline.start(); + await buildPipeline.run(); if (isWatchMode) { print('Initial build done!'); print('Watching directory: ${libPath.relativeToCwd}/'); - PipelineWatcher( + await PipelineWatcher( dir: libPath.absolute, pipeline: buildPipeline, // Ignore font files that are copied whenever tests run. - ignore: (event) => event.path.endsWith('.ttf'), + ignore: (WatchEvent event) => event.path.endsWith('.ttf'), ).start(); - // Return a never-ending future. - return Completer().future; - } else { - return true; } + return true; } } -Future gn() { - print('Running gn...'); - return runProcess( - path.join(environment.flutterDirectory.path, 'tools', 'gn'), - [ - '--unopt', - '--full-dart-sdk', - ], - ); -} - -// TODO(mdebbar): Make the ninja step interruptable in the pipeline. -Future ninja() { - print('Running autoninja...'); - return runProcess('autoninja', [ - '-C', - environment.hostDebugUnoptDir.path, - ]); -} - -enum PipelineStatus { - idle, - started, - stopping, - stopped, - error, - done, -} - -typedef PipelineStep = Future Function(); - -class Pipeline { - Pipeline({@required this.steps}); - - final Iterable steps; - - Future _currentStepFuture; - - PipelineStatus status = PipelineStatus.idle; +/// Runs `gn`. +/// +/// Not safe to interrupt as it may leave the `out/` directory in a corrupted +/// state. GN is pretty quick though, so it's OK to not support interruption. +class GnPipelineStep extends ProcessStep { + @override + String get description => 'gn'; - Future start() async { - status = PipelineStatus.started; - try { - for (PipelineStep step in steps) { - if (status != PipelineStatus.started) { - break; - } - _currentStepFuture = step(); - await _currentStepFuture; - } - status = PipelineStatus.done; - } catch (error, stackTrace) { - status = PipelineStatus.error; - print('Error in the pipeline: $error'); - print(stackTrace); - } finally { - _currentStepFuture = null; - } - } + @override + bool get isSafeToInterrupt => false; - Future stop() { - status = PipelineStatus.stopping; - return (_currentStepFuture ?? Future.value(null)).then((_) { - status = PipelineStatus.stopped; - }); + @override + Future createProcess() { + print('Running gn...'); + return startProcess( + path.join(environment.flutterDirectory.path, 'tools', 'gn'), + [ + '--unopt', + if (Platform.isMacOS) '--xcode-symlinks', + '--full-dart-sdk', + ], + ); } } -typedef WatchEventPredicate = bool Function(WatchEvent event); - -class PipelineWatcher { - PipelineWatcher({ - @required this.dir, - @required this.pipeline, - this.ignore, - }) : watcher = DirectoryWatcher(dir); - - /// The path of the directory to watch for changes. - final String dir; - - /// The pipeline to be executed when an event is fired by the watcher. - final Pipeline pipeline; - - /// Used to watch a directory for any file system changes. - final DirectoryWatcher watcher; - - /// A callback that determines whether to rerun the pipeline or not for a - /// given [WatchEvent] instance. - final WatchEventPredicate ignore; - - void start() { - watcher.events.listen(_onEvent); - } - - int _pipelineRunCount = 0; - Timer _scheduledPipeline; - - void _onEvent(WatchEvent event) { - if (ignore != null && ignore(event)) { - return; - } - - final String relativePath = path.relative(event.path, from: dir); - print('- [${event.type}] ${relativePath}'); - - _pipelineRunCount++; - _scheduledPipeline?.cancel(); - _scheduledPipeline = Timer(const Duration(milliseconds: 100), () { - _scheduledPipeline = null; - _runPipeline(); - }); - } - - void _runPipeline() { - int runCount; - switch (pipeline.status) { - case PipelineStatus.started: - pipeline.stop().then((_) { - runCount = _pipelineRunCount; - pipeline.start().then((_) => _pipelineDone(runCount)); - }); - break; - - case PipelineStatus.stopping: - // We are already trying to stop the pipeline. No need to do anything. - break; +/// Runs `autoninja`. +/// +/// Can be safely interrupted. +class NinjaPipelineStep extends ProcessStep { + @override + String get description => 'ninja'; - default: - runCount = _pipelineRunCount; - pipeline.start().then((_) => _pipelineDone(runCount)); - break; - } - } + @override + bool get isSafeToInterrupt => true; - void _pipelineDone(int pipelineRunCount) { - if (pipelineRunCount == _pipelineRunCount) { - print('*** Done! ***'); - } + @override + Future createProcess() { + print('Running autoninja...'); + return startProcess( + 'autoninja', + [ + '-C', + environment.hostDebugUnoptDir.path, + ], + ); } } diff --git a/lib/web_ui/dev/canvaskit_lock.yaml b/lib/web_ui/dev/canvaskit_lock.yaml new file mode 100644 index 0000000000000..78813b4e0fba0 --- /dev/null +++ b/lib/web_ui/dev/canvaskit_lock.yaml @@ -0,0 +1,4 @@ +# Specifies the version of CanvasKit to use for Flutter Web apps. +# +# See `lib/web_ui/README.md` for how to update this file. +canvaskit_version: "0.28.1" diff --git a/lib/web_ui/dev/canvaskit_roller.dart b/lib/web_ui/dev/canvaskit_roller.dart new file mode 100644 index 0000000000000..bb6c7e3299eb0 --- /dev/null +++ b/lib/web_ui/dev/canvaskit_roller.dart @@ -0,0 +1,168 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert' show json; +import 'dart:io'; + +import 'package:path/path.dart' as pathlib; +import 'package:yaml/yaml.dart'; + +import 'environment.dart'; +import 'utils.dart'; + +/// Rolls CanvasKit to the version specified in `dev/canvaskit_lock.yaml`. +/// +/// Detailed instructions for how to use this script can be found in +/// `lib/web_ui/README.md`. +Future main(List args) async { + final String canvaskitVersion = _readCanvaskitVersion(); + print('Rolling CanvasKit to version $canvaskitVersion'); + + final Directory canvaskitDirectory = await Directory.systemTemp.createTemp('canvaskit-roll-$canvaskitVersion-'); + print('Will use ${canvaskitDirectory.path} as staging directory.'); + + final String baseUrl = 'https://unpkg.com/canvaskit-wasm@$canvaskitVersion/bin/'; + print('Downloading CanvasKit from $baseUrl'); + final HttpClient client = HttpClient(); + for (final String assetPath in _canvaskitAssets) { + final String assetUrl = '$baseUrl/$assetPath'; + final File assetFile = File(pathlib.joinAll([ + canvaskitDirectory.path, + 'canvaskit', + ...assetPath.split('/'), // so it's compatible with Windows + ])); + await assetFile.parent.create(recursive: true); + final HttpClientRequest request = await client.getUrl(Uri.parse(assetUrl)); + final HttpClientResponse response = await request.close(); + final IOSink fileSink = assetFile.openWrite(); + await response.pipe(fileSink); + } + client.close(); + + final File cipdConfigFile = File(pathlib.join( + canvaskitDirectory.path, + 'cipd.yaml', + )); + await cipdConfigFile.writeAsString(''' +package: flutter/web/canvaskit_bundle +description: A build of CanvasKit bundled with Flutter Web apps +data: + - dir: canvaskit +'''); + + print('Uploading to CIPD'); + await runProcess('cipd', [ + 'create', + '--tag=version:$canvaskitVersion', + '--pkg-def=cipd.yaml', + '--json-output=result.json', + ], workingDirectory: canvaskitDirectory.path); + + final Map cipdResult = json.decode(File(pathlib.join( + canvaskitDirectory.path, + 'result.json', + )).readAsStringSync()) as Map; + final String cipdInstanceId = cipdResult['result']['instance_id'] as String; + + print('CIPD instance information:'); + final String cipdInfo = await evalProcess('cipd', [ + 'describe', + 'flutter/web/canvaskit_bundle', + '--version=$cipdInstanceId', + ], workingDirectory: canvaskitDirectory.path); + print(cipdInfo.trim().split('\n').map((String line) => ' • $line').join('\n')); + + print('Updating DEPS file'); + await _updateDepsFile(cipdInstanceId); + await _updateCanvaskitInitializationCode(canvaskitVersion); + + print('\nATTENTION: the roll process is not complete yet.'); + print('Last step: for the roll to take effect submit an engine pull request from local git changes.'); +} + +const List _canvaskitAssets = [ + 'canvaskit.js', + 'canvaskit.wasm', + 'profiling/canvaskit.js', + 'profiling/canvaskit.wasm', +]; + +String _readCanvaskitVersion() { + final YamlMap canvaskitLock = loadYaml(File(pathlib.join( + environment.webUiDevDir.path, + 'canvaskit_lock.yaml', + )).readAsStringSync()) as YamlMap; + return canvaskitLock['canvaskit_version'] as String; +} + +Future _updateDepsFile(String cipdInstanceId) async { + final File depsFile = File(pathlib.join( + environment.flutterDirectory.path, + 'DEPS', + )); + + final String originalDepsCode = await depsFile.readAsString(); + final List rewrittenDepsCode = []; + const String kCanvasKitDependencyKeyInDeps = '\'canvaskit_cipd_instance\': \''; + bool canvaskitDependencyFound = false; + for (final String line in originalDepsCode.split('\n')) { + if (line.trim().startsWith(kCanvasKitDependencyKeyInDeps)) { + canvaskitDependencyFound = true; + rewrittenDepsCode.add( + " 'canvaskit_cipd_instance': '$cipdInstanceId',", + ); + } else { + rewrittenDepsCode.add(line); + } + } + + if (!canvaskitDependencyFound) { + stderr.writeln( + 'Failed to update the DEPS file.\n' + 'Could not to locate CanvasKit dependency in the DEPS file. Make sure the ' + 'DEPS file contains a line like this:\n' + '\n' + ' \'canvaskit_cipd_instance\': \'SOME_VALUE\',' + ); + exit(1); + } + + await depsFile.writeAsString(rewrittenDepsCode.join('\n')); +} + +Future _updateCanvaskitInitializationCode(String canvaskitVersion) async { + const String kCanvasKitVersionKey = 'const String canvaskitVersion'; + const String kPathToInitializationCode = 'lib/src/engine/canvaskit/initialization.dart'; + final File initializationFile = File(pathlib.join( + environment.webUiRootDir.path, + kPathToInitializationCode, + )); + final String originalInitializationCode = await initializationFile.readAsString(); + + final List rewrittenCode = []; + bool canvaskitVersionFound = false; + for (final String line in originalInitializationCode.split('\n')) { + if (line.trim().startsWith(kCanvasKitVersionKey)) { + canvaskitVersionFound = true; + rewrittenCode.add( + "const String canvaskitVersion = '$canvaskitVersion';", + ); + } else { + rewrittenCode.add(line); + } + } + + if (!canvaskitVersionFound) { + stderr.writeln( + 'Failed to update CanvasKit version in $kPathToInitializationCode.\n' + 'Could not to locate the constant that defines the version. Make sure the ' + '$kPathToInitializationCode file contains a line like this:\n' + '\n' + 'const String canvaskitVersion = \'VERSION\';' + ); + exit(1); + } + + await initializationFile.writeAsString(rewrittenCode.join('\n')); +} diff --git a/lib/web_ui/dev/chrome.dart b/lib/web_ui/dev/chrome.dart index 356b4dd349154..152bdb5d29828 100644 --- a/lib/web_ui/dev/chrome.dart +++ b/lib/web_ui/dev/chrome.dart @@ -2,19 +2,53 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; +import 'dart:convert'; import 'dart:io'; +import 'dart:math' as math; +import 'package:image/image.dart'; import 'package:pedantic/pedantic.dart'; - -import 'package:test_core/src/util/io.dart'; // ignore: implementation_imports +import 'package:test_api/src/backend/runtime.dart'; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' + as wip; import 'browser.dart'; +import 'browser_lock.dart'; import 'chrome_installer.dart'; import 'common.dart'; +import 'environment.dart'; + +/// Provides an environment for desktop Chrome. +class ChromeEnvironment implements BrowserEnvironment { + @override + Browser launchBrowserInstance(Uri url, {bool debug = false}) { + return Chrome(url, debug: debug); + } + + @override + Runtime get packageTestRuntime => Runtime.chrome; + + @override + Future prepareEnvironment() async { + // Chrome doesn't need any special prep. + } + + @override + ScreenshotManager? getScreenshotManager() { + // Always compare screenshots when running tests locally. On CI only compare + // on Linux. + if (Platform.isLinux || !isLuci) { + return ChromeScreenshotManager(); + } + return null; + } -/// A class for running an instance of Chrome. + @override + String get packageTestConfigurationYamlFile => 'dart_test_chrome.yaml'; +} + +/// Runs desktop Chrome. /// /// Most of the communication with the browser is expected to happen via HTTP, /// so this exposes a bare-bones API. The browser starts as soon as the class is @@ -23,20 +57,16 @@ import 'common.dart'; /// Any errors starting or running the process are reported through [onExit]. class Chrome extends Browser { @override - final name = 'Chrome'; + final String name = 'Chrome'; @override final Future remoteDebuggerUrl; - static String version; - /// Starts a new instance of Chrome open to the given [url], which may be a /// [Uri] or a [String]. factory Chrome(Uri url, {bool debug = false}) { - version = ChromeArgParser.instance.version; - - assert(version != null); - var remoteDebuggerCompleter = Completer.sync(); + final String version = browserLock.chromeLock.versionForCurrentPlatform; + final Completer remoteDebuggerCompleter = Completer.sync(); return Chrome._(() async { final BrowserInstallation installation = await getOrInstallChrome( version, @@ -55,8 +85,8 @@ class Chrome extends Browser { // --disable-font-subpixel-positioning final bool isChromeNoSandbox = Platform.environment['CHROME_NO_SANDBOX'] == 'true'; - var dir = createTempDir(); - var args = [ + final String dir = environment.webUiDartToolDir.createTempSync('test_chrome_user_data_').resolveSymbolicLinksSync(); + final List args = [ '--user-data-dir=$dir', url.toString(), if (!debug) @@ -76,10 +106,10 @@ class Chrome extends Browser { ]; final Process process = - await Process.start(installation.executable, args); + await _spawnChromiumProcess(installation.executable, args); remoteDebuggerCompleter.complete( - getRemoteDebuggerUrl(Uri.parse('http://localhost:${kDevtoolsPort}'))); + getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort'))); unawaited(process.exitCode .then((_) => Directory(dir).deleteSync(recursive: true))); @@ -88,6 +118,158 @@ class Chrome extends Browser { }, remoteDebuggerCompleter.future); } - Chrome._(Future startBrowser(), this.remoteDebuggerUrl) + Chrome._(Future Function() startBrowser, this.remoteDebuggerUrl) : super(startBrowser); } + +/// Used by [Chrome] to detect a glibc bug and retry launching the +/// browser. +/// +/// Once every few thousands of launches we hit this glibc bug: +/// +/// https://sourceware.org/bugzilla/show_bug.cgi?id=19329. +/// +/// When this happens Chrome spits out something like the following then exits with code 127: +/// +/// Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed! +const String _kGlibcError = 'Inconsistency detected by ld.so'; + +Future _spawnChromiumProcess(String executable, List args, { String? workingDirectory }) async { + // Keep attempting to launch the browser until one of: + // - Chrome launched successfully, in which case we just return from the loop. + // - The tool detected an unretriable Chrome error, in which case we throw ToolExit. + while (true) { + final Process process = await Process.start(executable, args, workingDirectory: workingDirectory); + + process.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((String line) { + print('[CHROME STDOUT]: $line'); + }); + + // Wait until the DevTools are listening before trying to connect. This is + // only required for flutter_test --platform=chrome and not flutter run. + bool hitGlibcBug = false; + await process.stderr + .transform(utf8.decoder) + .transform(const LineSplitter()) + .map((String line) { + print('[CHROME STDERR]:$line'); + if (line.contains(_kGlibcError)) { + hitGlibcBug = true; + } + return line; + }) + .firstWhere((String line) => line.startsWith('DevTools listening'), orElse: () { + if (hitGlibcBug) { + const String message = 'Encountered glibc bug ' + 'https://sourceware.org/bugzilla/show_bug.cgi?id=19329. ' + 'Will try launching browser again.'; + print(message); + return message; + } + print('Failed to launch browser. Command used to launch it: ${args.join(' ')}'); + throw Exception( + 'Failed to launch browser. Make sure you are using an up-to-date ' + 'Chrome or Edge. Otherwise, consider using -d web-server instead ' + 'and filing an issue at https://github.com/flutter/flutter/issues.', + ); + }); + + if (!hitGlibcBug) { + return process; + } + + // A precaution that avoids accumulating browser processes, in case the + // glibc bug doesn't cause the browser to quit and we keep looping and + // launching more processes. + process.exitCode.timeout(const Duration(seconds: 1), onTimeout: () { + process.kill(); + return -1; + }); + } +} + +/// Returns the full URL of the Chrome remote debugger for the main page. +/// +/// This takes the [base] remote debugger URL (which points to a browser-wide +/// page) and uses its JSON API to find the resolved URL for debugging the host +/// page. +Future getRemoteDebuggerUrl(Uri base) async { + try { + final HttpClient client = HttpClient(); + final HttpClientRequest request = await client.getUrl(base.resolve('/json/list')); + final HttpClientResponse response = await request.close(); + final List? jsonObject = + await json.fuse(utf8).decoder.bind(response).single as List?; + return base.resolve(jsonObject!.first['devtoolsFrontendUrl'] as String); + } catch (_) { + // If we fail to talk to the remote debugger protocol, give up and return + // the raw URL rather than crashing. + return base; + } +} + +/// [ScreenshotManager] implementation for Chrome. +/// +/// This manager can be used for both macOS and Linux. +// TODO(yjbanov): extends tests to Window, https://github.com/flutter/flutter/issues/65673 +class ChromeScreenshotManager extends ScreenshotManager { + @override + String get filenameSuffix => ''; + + /// Capture a screenshot of the web content. + /// + /// Uses Webkit Inspection Protocol server's `captureScreenshot` API. + /// + /// [region] is used to decide which part of the web content will be used in + /// test image. It includes starting coordinate x,y as well as height and + /// width of the area to capture. + @override + Future capture(math.Rectangle? region) async { + final wip.ChromeConnection chromeConnection = + wip.ChromeConnection('localhost', kDevtoolsPort); + final wip.ChromeTab? chromeTab = await chromeConnection.getTab( + (wip.ChromeTab chromeTab) => chromeTab.url.contains('localhost')); + if (chromeTab == null) { + throw StateError( + 'Failed locate Chrome tab with the test page', + ); + } + final wip.WipConnection wipConnection = await chromeTab.connect(); + + Map? captureScreenshotParameters; + if (region != null) { + captureScreenshotParameters = { + 'format': 'png', + 'clip': { + 'x': region.left, + 'y': region.top, + 'width': region.width, + 'height': region.height, + 'scale': + // This is NOT the DPI of the page, instead it's the "zoom level". + 1, + }, + }; + } + + // Setting hardware-independent screen parameters: + // https://chromedevtools.github.io/devtools-protocol/tot/Emulation + await wipConnection + .sendCommand('Emulation.setDeviceMetricsOverride', { + 'width': kMaxScreenshotWidth, + 'height': kMaxScreenshotHeight, + 'deviceScaleFactor': 1, + 'mobile': false, + }); + final wip.WipResponse response = await wipConnection.sendCommand( + 'Page.captureScreenshot', captureScreenshotParameters); + + final Image screenshot = + decodePng(base64.decode(response.result!['data'] as String))!; + + return screenshot; + } +} diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index 3766488f497dd..6f6b677ee780d 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -2,63 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io' as io; +import 'dart:typed_data'; import 'package:archive/archive.dart'; import 'package:archive/archive_io.dart'; -import 'package:args/args.dart'; import 'package:http/http.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:yaml/yaml.dart'; +import 'browser_lock.dart'; import 'common.dart'; import 'environment.dart'; import 'exceptions.dart'; -class ChromeArgParser extends BrowserArgParser { - static final ChromeArgParser _singletonInstance = ChromeArgParser._(); - - /// The [ChromeArgParser] singleton. - static ChromeArgParser get instance => _singletonInstance; - - String _version; - int _pinnedChromeBuildNumber; - - ChromeArgParser._(); - - @override - void populateOptions(ArgParser argParser) { - final YamlMap browserLock = BrowserLock.instance.configuration; - _pinnedChromeBuildNumber = - PlatformBinding.instance.getChromeBuild(browserLock); - - argParser - ..addOption( - 'chrome-version', - defaultsTo: '$pinnedChromeBuildNumber', - help: 'The Chrome version to use while running tests. If the requested ' - 'version has not been installed, it will be downloaded and installed ' - 'automatically. A specific Chrome build version number, such as 695653, ' - 'will use that version of Chrome. Value "latest" will use the latest ' - 'available build of Chrome, installing it if necessary. Value "system" ' - 'will use the manually installed version of Chrome on this computer.', - ); - } - - @override - void parseOptions(ArgResults argResults) { - _version = argResults['chrome-version'] as String; - } - - @override - String get version => _version; - - String get pinnedChromeBuildNumber => _pinnedChromeBuildNumber.toString(); -} - /// Returns the installation of Chrome, installing it if necessary. /// /// If [requestedVersion] is null, uses the version specified on the @@ -73,7 +30,7 @@ class ChromeArgParser extends BrowserArgParser { /// https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ Future getOrInstallChrome( String requestedVersion, { - StringSink infoLog, + StringSink? infoLog, }) async { infoLog ??= io.stdout; @@ -84,7 +41,7 @@ Future getOrInstallChrome( ); } - ChromeInstaller installer; + ChromeInstaller? installer; try { installer = requestedVersion == 'latest' ? await ChromeInstaller.latest() @@ -96,11 +53,11 @@ Future getOrInstallChrome( } else { infoLog.writeln('Installing Chrome version: ${installer.version}'); await installer.install(); - final BrowserInstallation installation = installer.getInstallation(); + final BrowserInstallation installation = installer.getInstallation()!; infoLog.writeln( 'Installations complete. To launch it run ${installation.executable}'); } - return installer.getInstallation(); + return installer.getInstallation()!; } finally { installer?.close(); } @@ -121,7 +78,7 @@ Future _findSystemChromeExecutable() async { /// Manages the installation of a particular [version] of Chrome. class ChromeInstaller { factory ChromeInstaller({ - @required String version, + required String version, }) { if (version == 'system') { throw BrowserInstallerException( @@ -150,9 +107,9 @@ class ChromeInstaller { } ChromeInstaller._({ - @required this.version, - @required this.chromeInstallationDir, - @required this.versionDir, + required this.version, + required this.chromeInstallationDir, + required this.versionDir, }); /// Chrome version managed by this installer. @@ -171,7 +128,7 @@ class ChromeInstaller { return versionDir.existsSync(); } - BrowserInstallation getInstallation() { + BrowserInstallation? getInstallation() { if (!isInstalled) { return null; } @@ -222,7 +179,7 @@ class ChromeInstaller { final Stopwatch stopwatch = Stopwatch()..start(); // Read the Zip file from disk. - final bytes = downloadedFile.readAsBytesSync(); + final Uint8List bytes = downloadedFile.readAsBytesSync(); final Archive archive = ZipDecoder().decodeBytes(bytes); @@ -230,13 +187,13 @@ class ChromeInstaller { for (final ArchiveFile file in archive) { final String filename = file.name; if (file.isFile) { - final data = file.content as List; + final List data = file.content as List; io.File(path.join(versionDir.path, filename)) ..createSync(recursive: true) ..writeAsBytesSync(data); } else { - io.Directory(path.join(versionDir.path, filename)) - ..create(recursive: true); + io.Directory(path.join(versionDir.path, filename)).create( + recursive: true); } } @@ -270,7 +227,7 @@ Future fetchLatestChromeVersion() async { final Client client = Client(); try { final Response response = await client.get( - 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media'); + Uri.parse('https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media')); if (response.statusCode != 200) { throw BrowserInstallerException( 'Failed to fetch latest Chrome version. Server returned status code ${response.statusCode}'); @@ -281,18 +238,6 @@ Future fetchLatestChromeVersion() async { } } -/// Get the Chrome Driver version for the system Chrome. -// TODO(nurhan): https://github.com/flutter/flutter/issues/53179 -Future queryChromeDriverVersion() async { - final int chromeVersion = await _querySystemChromeMajorVersion(); - final io.File lockFile = io.File( - path.join(environment.webUiRootDir.path, 'dev', 'driver_version.yaml')); - YamlMap _configuration = loadYaml(lockFile.readAsStringSync()) as YamlMap; - final String chromeDriverVersion = - _configuration['chrome'][chromeVersion] as String; - return chromeDriverVersion; -} - /// Make sure LUCI bot has the pinned Chrome version and return the executable. /// /// We are using CIPD packages in LUCI. The pinned chrome version from the @@ -301,66 +246,15 @@ Future queryChromeDriverVersion() async { String preinstalledChromeExecutable() { // Note that build number and major version is different for Chrome. // For example for a build number `753189`, major version is 83. - final String buildNumber = ChromeArgParser.instance.pinnedChromeBuildNumber; + final String buildNumber = browserLock.chromeLock.versionForCurrentPlatform; final ChromeInstaller chromeInstaller = ChromeInstaller(version: buildNumber); if (chromeInstaller.isInstalled) { - print('INFO: Found chrome executable for LUCI: ' - '${chromeInstaller.getInstallation().executable}'); - return chromeInstaller.getInstallation().executable; + final String executable = chromeInstaller.getInstallation()!.executable; + print('INFO: Found chrome executable for LUCI: $executable'); + return executable; } else { throw StateError( - 'Failed to locate pinned Chrome build: $buildNumber on LUCI.'); - } -} - -Future _querySystemChromeMajorVersion() async { - String chromeExecutable = ''; - // LUCI uses the Chrome from CIPD packages. - if (isLuci) { - chromeExecutable = preinstalledChromeExecutable(); - } else if (io.Platform.isLinux) { - chromeExecutable = 'google-chrome'; - } else if (io.Platform.isMacOS) { - chromeExecutable = await _findChromeExecutableOnMac(); - } else { - throw UnimplementedError('Web installers only work on Linux and Mac.'); - } - - final io.ProcessResult versionResult = - await io.Process.run('$chromeExecutable', ['--version']); - - if (versionResult.exitCode != 0) { - throw Exception('Failed to locate system Chrome.'); - } - // The output looks like: Google Chrome 79.0.3945.36. - final String output = versionResult.stdout as String; - - print('INFO: chrome version in use $output'); - - // Version number such as 79.0.3945.36. - try { - final String versionAsString = output.trim().split(' ').last; - final String majorVersion = versionAsString.split('.')[0]; - return int.parse(majorVersion); - } catch (e) { - throw Exception( - 'Was expecting a version of the form Google Chrome 79.0.3945.36., ' - 'received $output'); + 'Failed to locate pinned Chrome build: $buildNumber on LUCI.', + ); } } - -/// Find Google Chrome App on Mac. -Future _findChromeExecutableOnMac() async { - io.Directory chromeDirectory = io.Directory('/Applications') - .listSync() - .whereType() - .firstWhere( - (d) => path.basename(d.path).endsWith('Chrome.app'), - orElse: () => throw Exception('Failed to locate system Chrome'), - ); - - final io.File chromeExecutableDir = io.File( - path.join(chromeDirectory.path, 'Contents', 'MacOS', 'Google Chrome')); - - return chromeExecutableDir.path; -} diff --git a/lib/web_ui/dev/clean.dart b/lib/web_ui/dev/clean.dart index b3d065fc66534..b31567c15cc12 100644 --- a/lib/web_ui/dev/clean.dart +++ b/lib/web_ui/dev/clean.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io' as io; @@ -12,18 +11,18 @@ import 'package:path/path.dart' as path; import 'environment.dart'; import 'utils.dart'; -class CleanCommand extends Command with ArgUtils { +class CleanCommand extends Command with ArgUtils { CleanCommand() { argParser ..addFlag( 'flutter', defaultsTo: true, - help: 'Cleans up the .dart_tool directory under engine/src/flutter.', + help: 'Cleans up the .dart_tool directory under engine/src/flutter. Enabled by default.', ) ..addFlag( 'ninja', defaultsTo: false, - help: 'Also clean up the engine out directory with ninja output.', + help: 'Also clean up the engine out directory with ninja output. Disabled by default.', ); } diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 511794a1e92f3..fa5166954f3c4 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -2,15 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:io' as io; -import 'package:args/args.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:yaml/yaml.dart'; -import 'environment.dart'; +import 'browser.dart'; +import 'browser_lock.dart'; +import 'chrome.dart'; +import 'edge.dart'; +import 'firefox.dart'; +import 'safari_ios.dart'; +import 'safari_macos.dart'; /// The port number for debugging. const int kDevtoolsPort = 12345; @@ -20,23 +22,24 @@ const double kMaxDiffRateFailure = 0.28 / 100; // 0.28% abstract class PlatformBinding { static PlatformBinding get instance { - if (_instance == null) { - if (io.Platform.isLinux) { - _instance = _LinuxBinding(); - } else if (io.Platform.isMacOS) { - _instance = _MacBinding(); - } else if (io.Platform.isWindows) { - _instance = _WindowsBinding(); - } else { - throw '${io.Platform.operatingSystem} is not supported'; - } - } - return _instance; + return _instance ??= _createInstance(); } + static PlatformBinding? _instance; - static PlatformBinding _instance; + static PlatformBinding _createInstance() { + if (io.Platform.isLinux) { + return _LinuxBinding(); + } + if (io.Platform.isMacOS) { + return _MacBinding(); + } + if (io.Platform.isWindows) { + return _WindowsBinding(); + } + throw '${io.Platform.operatingSystem} is not supported'; + } - int getChromeBuild(YamlMap chromeLock); + String getChromeBuild(ChromeLock chromeLock); String getChromeDownloadUrl(String version); String getFirefoxDownloadUrl(String version); String getFirefoxDownloadFilename(String version); @@ -52,14 +55,13 @@ const String _kBaseDownloadUrl = class _WindowsBinding implements PlatformBinding { @override - int getChromeBuild(YamlMap browserLock) { - final YamlMap chromeMap = browserLock['chrome'] as YamlMap; - return chromeMap['Win'] as int; + String getChromeBuild(ChromeLock chromeLock) { + return chromeLock.windows; } @override String getChromeDownloadUrl(String version) => - 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win%2F${version}%2Fchrome-win.zip?alt=media'; + 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win%2F$version%2Fchrome-win.zip?alt=media'; @override String getChromeExecutablePath(io.Directory versionDir) => @@ -67,11 +69,11 @@ class _WindowsBinding implements PlatformBinding { @override String getFirefoxDownloadUrl(String version) => - 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/win64/en-US/' + 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/$version/win64/en-US/' '${getFirefoxDownloadFilename(version)}'; @override - String getFirefoxDownloadFilename(String version) => 'firefox-${version}.exe'; + String getFirefoxDownloadFilename(String version) => 'firefox-$version.exe'; @override String getFirefoxExecutablePath(io.Directory versionDir) => @@ -91,9 +93,8 @@ class _WindowsBinding implements PlatformBinding { class _LinuxBinding implements PlatformBinding { @override - int getChromeBuild(YamlMap browserLock) { - final YamlMap chromeMap = browserLock['chrome'] as YamlMap; - return chromeMap['Linux'] as int; + String getChromeBuild(ChromeLock chromeLock) { + return chromeLock.linux; } @override @@ -106,12 +107,12 @@ class _LinuxBinding implements PlatformBinding { @override String getFirefoxDownloadUrl(String version) => - 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/linux-x86_64/en-US/' + 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/$version/linux-x86_64/en-US/' '${getFirefoxDownloadFilename(version)}'; @override String getFirefoxDownloadFilename(String version) => - 'firefox-${version}.tar.bz2'; + 'firefox-$version.tar.bz2'; @override String getFirefoxExecutablePath(io.Directory versionDir) => @@ -132,15 +133,15 @@ class _LinuxBinding implements PlatformBinding { class _MacBinding implements PlatformBinding { @override - int getChromeBuild(YamlMap browserLock) { - final YamlMap chromeMap = browserLock['chrome'] as YamlMap; - return chromeMap['Mac'] as int; + String getChromeBuild(ChromeLock chromeLock) { + return chromeLock.mac; } @override String getChromeDownloadUrl(String version) => '$_kBaseDownloadUrl/Mac%2F$version%2Fchrome-mac.zip?alt=media'; + @override String getChromeExecutablePath(io.Directory versionDir) => path.join( versionDir.path, 'chrome-mac', @@ -151,11 +152,11 @@ class _MacBinding implements PlatformBinding { @override String getFirefoxDownloadUrl(String version) => - 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/mac/en-US/' + 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/$version/mac/en-US/' '${getFirefoxDownloadFilename(version)}'; @override - String getFirefoxDownloadFilename(String version) => 'Firefox ${version}.dmg'; + String getFirefoxDownloadFilename(String version) => 'Firefox $version.dmg'; @override String getFirefoxExecutablePath(io.Directory versionDir) => @@ -175,8 +176,8 @@ class _MacBinding implements PlatformBinding { class BrowserInstallation { const BrowserInstallation({ - @required this.version, - @required this.executable, + required this.version, + required this.executable, }); /// Browser version. @@ -186,49 +187,19 @@ class BrowserInstallation { final String executable; } -abstract class BrowserArgParser { - const BrowserArgParser(); - - /// Populate options specific to a browser to the [ArgParser]. - void populateOptions(ArgParser argParser); - - /// Populate browser with results of the arguments passed. - void parseOptions(ArgResults argResults); - - String get version; -} - -/// Provides access to the contents of the `browser_lock.yaml` file. -class BrowserLock { - /// Initializes the [BrowserLock] singleton. - static final BrowserLock _singletonInstance = BrowserLock._(); - - /// The [Keyboard] singleton. - static BrowserLock get instance => _singletonInstance; - - YamlMap _configuration = YamlMap(); - YamlMap get configuration => _configuration; - - BrowserLock._() { - final io.File lockFile = io.File( - path.join(environment.webUiRootDir.path, 'dev', 'browser_lock.yaml')); - this._configuration = loadYaml(lockFile.readAsStringSync()) as YamlMap; - } -} - /// A string sink that swallows all input. class DevNull implements StringSink { @override - void write(Object obj) {} + void write(Object? obj) {} @override - void writeAll(Iterable objects, [String separator = ""]) {} + void writeAll(Iterable objects, [String separator = '']) {} @override void writeCharCode(int charCode) {} @override - void writeln([Object obj = ""]) {} + void writeln([Object? obj = '']) {} } /// Whether the felt command is running on Cirrus CI. @@ -240,3 +211,31 @@ bool get isLuci => io.Platform.environment['LUCI_CONTEXT'] != null; /// Whether the felt command is running on one of the Continuous Integration /// environements. bool get isCi => isCirrus || isLuci; + +const String kChrome = 'chrome'; +const String kEdge = 'edge'; +const String kFirefox = 'firefox'; +const String kSafari = 'safari'; +const String kSafariIos = 'ios-safari'; + +const List kAllBrowserNames = [ + kChrome, + kEdge, + kFirefox, + kSafari, + kSafariIos, +]; + +/// Creates an environment for a browser. +/// +/// The [browserName] matches the browser name passed as the `--browser` option. +BrowserEnvironment getBrowserEnvironment(String browserName) { + switch (browserName) { + case kChrome: return ChromeEnvironment(); + case kEdge: return EdgeEnvironment(); + case kFirefox: return FirefoxEnvironment(); + case kSafari: return SafariMacOsEnvironment(); + case kSafariIos: return SafariIosEnvironment(); + } + throw UnsupportedError('Browser $browserName is not supported.'); +} diff --git a/lib/web_ui/dev/create_simulator.dart b/lib/web_ui/dev/create_simulator.dart index f180c3beaaf8d..85fd076c4d6ed 100644 --- a/lib/web_ui/dev/create_simulator.dart +++ b/lib/web_ui/dev/create_simulator.dart @@ -2,31 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:simulators/simulator_manager.dart'; -import 'safari_installation.dart'; +import 'browser_lock.dart'; import 'utils.dart'; -class CreateSimulatorCommand extends Command with ArgUtils { - CreateSimulatorCommand() { - IosSafariArgParser.instance.populateOptions(argParser); - argParser - ..addOption( - 'type', - defaultsTo: _defaultType, - help: 'Type of the mobile simulator. Currently the only iOS ' - 'Simulators are supported. Android will be added soon. This option ' - 'is not case sensitive, ios, iOS, IOS are all valid.', - ); - } - - /// Currently the only iOS Simulators are supported. - static final String _defaultType = 'iOS'; - +class CreateSimulatorCommand extends Command with ArgUtils { @override String get name => 'create_simulator'; @@ -35,17 +19,14 @@ class CreateSimulatorCommand extends Command with ArgUtils { @override FutureOr run() async { - IosSafariArgParser.instance.parseOptions(argResults); - final String simulatorType = argResults['type'] as String; - if (simulatorType.toUpperCase() != 'IOS') { - throw Exception('Currently the only iOS Simulators are supported'); - } final IosSimulatorManager iosSimulatorManager = IosSimulatorManager(); try { + final SafariIosLock lock = browserLock.safariIosLock; final IosSimulator simulator = await iosSimulatorManager.createSimulator( - IosSafariArgParser.instance.iosMajorVersion, - IosSafariArgParser.instance.iosMinorVersion, - IosSafariArgParser.instance.iosDevice); + lock.majorVersion, + lock.minorVersion, + lock.device, + ); print('INFO: Simulator created ${simulator.toString()}'); } catch (e) { throw Exception('Error creating requested simulator. You can use Xcode ' diff --git a/lib/web_ui/dev/driver_manager.dart b/lib/web_ui/dev/driver_manager.dart deleted file mode 100644 index 753eb70d1c97f..0000000000000 --- a/lib/web_ui/dev/driver_manager.dart +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:io' as io; - -import 'package:path/path.dart' as pathlib; -import 'package:web_driver_installer/chrome_driver_installer.dart'; -import 'package:web_driver_installer/firefox_driver_installer.dart'; -import 'package:web_driver_installer/safari_driver_runner.dart'; -import 'package:yaml/yaml.dart'; - -import 'chrome_installer.dart'; -import 'common.dart'; -import 'environment.dart'; -import 'utils.dart'; - -/// [DriverManager] implementation for Chrome. -/// -/// This manager can be used for both macOS and Linux. -class ChromeDriverManager extends DriverManager { - /// Directory which contains the Chrome's major version. - /// - /// On LUCI we are using the CIPD packages to control Chrome binaries we use. - /// There will be multiple CIPD packages loaded at the same time. Yaml file - /// `driver_version.yaml` contains the version number we want to use. - /// - /// Initialized to the current first to avoid the `Non-nullable` error. - // TODO: https://github.com/flutter/flutter/issues/53179. Local integration - // tests are still using the system Chrome. - io.Directory _browserDriverDirWithVersion; - - ChromeDriverManager(String browser) : super(browser) { - final io.File lockFile = io.File(pathlib.join( - environment.webUiRootDir.path, 'dev', 'browser_lock.yaml')); - final YamlMap _configuration = - loadYaml(lockFile.readAsStringSync()) as YamlMap; - final String requiredChromeDriverVersion = - _configuration['required_driver_version']['chrome'] as String; - print('INFO: Major version for Chrome Driver $requiredChromeDriverVersion'); - _browserDriverDirWithVersion = io.Directory(pathlib.join( - environment.webUiDartToolDir.path, - 'drivers', - browser, - requiredChromeDriverVersion, - '${browser}driver-${io.Platform.operatingSystem.toString()}')); - } - - @override - Future _installDriver() async { - if (_browserDriverDirWithVersion.existsSync()) { - _browserDriverDirWithVersion.deleteSync(recursive: true); - } - - _browserDriverDirWithVersion.createSync(recursive: true); - temporaryDirectories.add(_drivers); - - final io.Directory temp = io.Directory.current; - io.Directory.current = _browserDriverDirWithVersion; - - try { - // TODO(nurhan): https://github.com/flutter/flutter/issues/53179 - final String chromeDriverVersion = await queryChromeDriverVersion(); - ChromeDriverInstaller chromeDriverInstaller = - ChromeDriverInstaller.withVersion(chromeDriverVersion); - await chromeDriverInstaller.install(alwaysInstall: true); - } finally { - io.Directory.current = temp; - } - } - - /// Throw an error if driver directory does not exists. - /// - /// Driver should already exist on LUCI as a CIPD package. - @override - Future _verifyDriverForLUCI() { - if (!_browserDriverDirWithVersion.existsSync()) { - throw StateError('Failed to locate Chrome driver on LUCI on path:' - '${_browserDriverDirWithVersion.path}'); - } - return Future.value(); - } - - @override - Future _startDriver() async { - await startProcess('./chromedriver/chromedriver', ['--port=4444'], - workingDirectory: _browserDriverDirWithVersion.path); - print('INFO: Driver started'); - } -} - -/// [DriverManager] implementation for Firefox. -/// -/// This manager can be used for both macOS and Linux. -class FirefoxDriverManager extends DriverManager { - FirefoxDriverManager(String browser) : super(browser); - - FirefoxDriverInstaller firefoxDriverInstaller = - FirefoxDriverInstaller(geckoDriverVersion: getLockedGeckoDriverVersion()); - - @override - Future _installDriver() async { - if (_browserDriverDir.existsSync()) { - _browserDriverDir.deleteSync(recursive: true); - } - - _browserDriverDir.createSync(recursive: true); - temporaryDirectories.add(_drivers); - - final io.Directory temp = io.Directory.current; - io.Directory.current = _browserDriverDir; - - try { - await firefoxDriverInstaller.install(alwaysInstall: false); - } finally { - io.Directory.current = temp; - } - } - - /// Throw an error if driver directory does not exist. - /// - /// Driver should already exist on LUCI as a CIPD package. - @override - Future _verifyDriverForLUCI() { - if (!_browserDriverDir.existsSync()) { - throw StateError('Failed to locate Firefox driver on LUCI on path:' - '${_browserDriverDir.path}'); - } - return Future.value(); - } - - @override - Future _startDriver() async { - await startProcess('./firefoxdriver/geckodriver', ['--port=4444'], - workingDirectory: _browserDriverDir.path); - print('INFO: Driver started'); - } - - /// Get the geckodriver version to be used with [FirefoxDriverInstaller]. - /// - /// For different versions of geckodriver. See: - /// https://github.com/mozilla/geckodriver/releases - static String getLockedGeckoDriverVersion() { - final YamlMap browserLock = BrowserLock.instance.configuration; - String geckoDriverReleaseVersion = browserLock['geckodriver'] as String; - return geckoDriverReleaseVersion; - } -} - -/// [DriverManager] implementation for Safari. -/// -/// This manager is will only be created/used for macOS. -class SafariDriverManager extends DriverManager { - SafariDriverManager(String browser) : super(browser); - - @override - Future _installDriver() { - // No-op. - // macOS comes with Safari Driver installed. - return new Future.value(); - } - - @override - Future _verifyDriverForLUCI() { - // No-op. - // macOS comes with Safari Driver installed. - return Future.value(); - } - - @override - Future _startDriver() async { - final SafariDriverRunner safariDriverRunner = SafariDriverRunner(); - - final io.Process process = - await safariDriverRunner.runDriver(version: 'system'); - - processesToCleanUp.add(process); - } -} - -/// Abstract class for preparing the browser driver before running the integration -/// tests. -abstract class DriverManager { - /// Installation directory for browser's driver. - final io.Directory _browserDriverDir; - - /// This is the parent directory for all drivers. - /// - /// This directory is saved to [temporaryDirectories] and deleted before - /// tests shutdown. - final io.Directory _drivers; - - DriverManager(String browser) - : this._browserDriverDir = io.Directory(pathlib.join( - environment.webUiDartToolDir.path, - 'drivers', - browser, - '${browser}driver-${io.Platform.operatingSystem.toString()}')), - this._drivers = io.Directory( - pathlib.join(environment.webUiDartToolDir.path, 'drivers')); - - Future prepareDriver() async { - if (!isLuci) { - // LUCI installs driver from CIPD, so we skip installing it on LUCI. - await _installDriver(); - } else { - await _verifyDriverForLUCI(); - } - await _startDriver(); - } - - /// Always re-install since driver can change frequently. - /// It usually changes with each the browser version changes. - /// A better solution would be installing the browser and the driver at the - /// same time. - /// TODO(nurhan): https://github.com/flutter/flutter/issues/53179. Partly - // solved. Remaining local integration tests using the locked Chrome version. - Future _installDriver(); - - Future _verifyDriverForLUCI(); - - Future _startDriver(); - - static DriverManager chooseDriver(String browser) { - if (browser == 'chrome') { - return ChromeDriverManager(browser); - } else if (browser == 'firefox') { - return FirefoxDriverManager(browser); - } else if (browser == 'safari' && io.Platform.isMacOS) { - return SafariDriverManager(browser); - } else { - throw StateError('Integration tests are only supported on Firefox, Chrome' - ' and on Safari (running on macOS)'); - } - } -} diff --git a/lib/web_ui/dev/driver_version.yaml b/lib/web_ui/dev/driver_version.yaml deleted file mode 100644 index f2c3baf9391f8..0000000000000 --- a/lib/web_ui/dev/driver_version.yaml +++ /dev/null @@ -1,14 +0,0 @@ -## Map for driver versions to use for each browser version. -## See: https://chromedriver.chromium.org/downloads -chrome: - 85: '85.0.4183.38' - 84: '84.0.4147.30' - 83: '83.0.4103.39' - 81: '81.0.4044.69' - 80: '80.0.3987.106' - 79: '79.0.3945.36' - 78: '78.0.3904.105' - 77: '77.0.3865.40' - 76: '76.0.3809.126' - 75: '75.0.3770.140' - 74: '74.0.3729.6' diff --git a/lib/web_ui/dev/edge.dart b/lib/web_ui/dev/edge.dart index e1f5cf497b12c..0b810bccca922 100644 --- a/lib/web_ui/dev/edge.dart +++ b/lib/web_ui/dev/edge.dart @@ -2,15 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io'; +import 'package:test_api/src/backend/runtime.dart'; + import 'browser.dart'; +import 'browser_lock.dart'; import 'common.dart'; import 'edge_installation.dart'; -/// A class for running an instance of Edge. +/// Provides an environment for the desktop Microsoft Edge (Chromium-based). +class EdgeEnvironment implements BrowserEnvironment { + @override + Browser launchBrowserInstance(Uri url, {bool debug = false}) { + return Edge(url); + } + + @override + Runtime get packageTestRuntime => Runtime.internetExplorer; + + @override + Future prepareEnvironment() async { + // Edge doesn't need any special prep. + } + + @override + ScreenshotManager? getScreenshotManager() => null; + + @override + String get packageTestConfigurationYamlFile => 'dart_test_edge.yaml'; +} + +/// Runs desktop Edge. /// /// Most of the communication with the browser is expected to happen via HTTP, /// so this exposes a bare-bones API. The browser starts as soon as the class is @@ -19,36 +43,32 @@ import 'edge_installation.dart'; /// Any errors starting or running the process are reported through [onExit]. class Edge extends Browser { @override - final name = 'Edge'; - - static String version; + final String name = 'Edge'; /// Starts a new instance of Safari open to the given [url], which may be a /// [Uri] or a [String]. - factory Edge(Uri url, {bool debug = false}) { - version = EdgeArgParser.instance.version; - - assert(version != null); + factory Edge(Uri url) { return Edge._(() async { - // TODO(nurhan): Configure info log for LUCI. final BrowserInstallation installation = await getEdgeInstallation( - version, + browserLock.edgeLock.launcherVersion, infoLog: DevNull(), ); // Debug is not a valid option for Edge. Remove it. String pathToOpen = url.toString(); if(pathToOpen.contains('debug')) { - int index = pathToOpen.indexOf('debug'); + final int index = pathToOpen.indexOf('debug'); pathToOpen = pathToOpen.substring(0, index-1); } - var process = - await Process.start(installation.executable, ['$pathToOpen','-k']); + final Process process = await Process.start( + installation.executable, + [pathToOpen,'-k'], + ); return process; }); } - Edge._(Future startBrowser()) : super(startBrowser); + Edge._(Future Function() startBrowser) : super(startBrowser); } diff --git a/lib/web_ui/dev/edge_installation.dart b/lib/web_ui/dev/edge_installation.dart index efdf03f30b1b1..c10e220cb0985 100644 --- a/lib/web_ui/dev/edge_installation.dart +++ b/lib/web_ui/dev/edge_installation.dart @@ -2,48 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io' as io; -import 'package:args/args.dart'; import 'package:http/http.dart'; import 'package:path/path.dart' as path; +import 'browser_lock.dart'; import 'common.dart'; import 'environment.dart'; -class EdgeArgParser extends BrowserArgParser { - static final EdgeArgParser _singletonInstance = EdgeArgParser._(); - - /// The [EdgeArgParser] singleton. - static EdgeArgParser get instance => _singletonInstance; - - String _version; - - EdgeArgParser._(); - - @override - void populateOptions(ArgParser argParser) { - argParser - ..addOption( - 'edge-version', - defaultsTo: 'system', - help: 'The Edge version to use while running tests. The Edge ' - 'browser installed on the system is used as the only option now.', - ); - } - - @override - void parseOptions(ArgResults argResults) { - _version = argResults['edge-version'] as String; - assert(_version == 'system'); - } - - @override - String get version => _version; -} - /// Returns the installation of Edge. /// /// Currently uses the Edge version installed on the operating system. @@ -52,11 +20,11 @@ class EdgeArgParser extends BrowserArgParser { /// exclusively with Windows 10 and cannot be downloaded or installed separately.` /// See: https://support.microsoft.com/en-us/help/17171/microsoft-edge-get-to-know /// -// TODO(nurhan): Investigate running tests for the tech preview downloads +// TODO(yjbanov): Investigate running tests for the tech preview downloads // from the beta channel. Future getEdgeInstallation( String requestedVersion, { - StringSink infoLog, + StringSink? infoLog, }) async { // For now these tests are aimed to run only on Windows machines local or on LUCI/CI. // In the future we can investigate to run them on Android or on MacOS. @@ -86,7 +54,7 @@ Future getEdgeInstallation( version: 'system', executable: io.Directory(path.join( edgeLauncher.launcherInstallationDir.path, - '${PlatformBinding.instance.getCommandToRunEdge()}')) + PlatformBinding.instance.getCommandToRunEdge())) .path, ); } else { @@ -114,7 +82,7 @@ class EdgeLauncher { bool get isInstalled => executable.existsSync(); /// Version number launcher executable `MicrosoftEdgeLauncher`. - final String version; + String get version => browserLock.edgeLock.launcherVersion; /// Url for downloading `MicrosoftEdgeLauncher`. /// @@ -122,12 +90,10 @@ class EdgeLauncher { String get windowsEdgeLauncherDownloadUrl => 'https://github.com/MicrosoftEdge/edge-launcher/releases/download/$version/MicrosoftEdgeLauncher.exe'; - EdgeLauncher() - : version = - BrowserLock.instance.configuration['edge']['launcher_version'] as String; + EdgeLauncher(); /// Install the launcher if it does not exist in this system. - void install() async { + Future install() async { // Checks if the `MicrosoftEdgeLauncher` executable exists. if (isInstalled) { return; diff --git a/lib/web_ui/dev/environment.dart b/lib/web_ui/dev/environment.dart index c323925567699..ec3f42bd420bb 100644 --- a/lib/web_ui/dev/environment.dart +++ b/lib/web_ui/dev/environment.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:io' as io; import 'package:path/path.dart' as pathlib; @@ -10,11 +9,10 @@ import 'exceptions.dart'; /// Contains various environment variables, such as common file paths and command-line options. Environment get environment { - _environment ??= Environment(); - return _environment; + return _environment ??= Environment(); } -Environment _environment; +Environment? _environment; /// Contains various environment variables, such as common file paths and command-line options. class Environment { @@ -31,10 +29,8 @@ class Environment { io.Directory(pathlib.join(hostDebugUnoptDir.path, 'dart-sdk')); final io.Directory webUiRootDir = io.Directory( pathlib.join(engineSrcDir.path, 'flutter', 'lib', 'web_ui')); - final io.Directory integrationTestsDir = io.Directory( - pathlib.join(engineSrcDir.path, 'flutter', 'e2etests', 'web')); - for (io.Directory expectedDirectory in [ + for (final io.Directory expectedDirectory in [ engineSrcDir, outDir, hostDebugUnoptDir, @@ -42,7 +38,7 @@ class Environment { webUiRootDir ]) { if (!expectedDirectory.existsSync()) { - throw ToolException('$expectedDirectory does not exist.'); + throw ToolExit('$expectedDirectory does not exist.'); } } @@ -51,7 +47,6 @@ class Environment { webUiRootDir: webUiRootDir, engineSrcDir: engineSrcDir, engineToolsDir: engineToolsDir, - integrationTestsDir: integrationTestsDir, outDir: outDir, hostDebugUnoptDir: hostDebugUnoptDir, dartSdkDir: dartSdkDir, @@ -59,14 +54,13 @@ class Environment { } Environment._({ - this.self, - this.webUiRootDir, - this.engineSrcDir, - this.engineToolsDir, - this.integrationTestsDir, - this.outDir, - this.hostDebugUnoptDir, - this.dartSdkDir, + required this.self, + required this.webUiRootDir, + required this.engineSrcDir, + required this.engineToolsDir, + required this.outDir, + required this.hostDebugUnoptDir, + required this.dartSdkDir, }); /// The Dart script that's currently running. @@ -81,9 +75,6 @@ class Environment { /// Path to the engine's "tools" directory. final io.Directory engineToolsDir; - /// Path to the web integration tests. - final io.Directory integrationTestsDir; - /// Path to the engine's "out" directory. /// /// This is where you'll find the ninja output, such as the Dart SDK. diff --git a/lib/web_ui/dev/exceptions.dart b/lib/web_ui/dev/exceptions.dart index 167d1734e655f..319eae17c261d 100644 --- a/lib/web_ui/dev/exceptions.dart +++ b/lib/web_ui/dev/exceptions.dart @@ -11,19 +11,13 @@ class BrowserInstallerException implements Exception { String toString() => message; } -class DriverException implements Exception { - DriverException(this.message); - - final String message; - - @override - String toString() => message; -} - -class ToolException implements Exception { - ToolException(this.message); +/// Throw this exception in felt command to exit felt with a message and a +/// non-zero exit code. +class ToolExit implements Exception { + ToolExit(this.message, { this.exitCode = 1 }); final String message; + final int exitCode; @override String toString() => message; diff --git a/lib/web_ui/dev/felt b/lib/web_ui/dev/felt index 2a9e378028b65..b1c3c383550f4 100755 --- a/lib/web_ui/dev/felt +++ b/lib/web_ui/dev/felt @@ -100,7 +100,7 @@ fi if [[ "$FELT_USE_SNAPSHOT" == "false" || "$FELT_USE_SNAPSHOT" == "0" ]]; then echo "[Snapshot mode: off]" - # Running without snapshot means there is high likelyhood of local changes. In + # Running without snapshot means there is high likelihood of local changes. In # that case, let's clear the snapshot to avoid any surprises. rm -f "$SNAPSHOT_PATH" rm -f "$STAMP_PATH" diff --git a/lib/web_ui/dev/felt.bat b/lib/web_ui/dev/felt.bat new file mode 100644 index 0000000000000..c3c15cf12e832 --- /dev/null +++ b/lib/web_ui/dev/felt.bat @@ -0,0 +1,69 @@ +:: felt: a command-line utility for Windows for building and testing +:: Flutter web engine. +:: FELT stands for Flutter Engine Local Tester. + +@ECHO OFF +SETLOCAL + +:: Make sure gclient and ninja exist. Otherwise felt won't work. +FOR /F "tokens=1-2 delims=:" %%a in ('where gclient') DO SET GCLIENT_PATH=%%b +IF %GCLIENT_PATH%==[] (ECHO "ERROR: gclient is not in your PATH") + +FOR /F "tokens=1-2 delims=:" %%a in ('where ninja') DO SET NINJA_PATH=%%b +IF %NINJA_PATH%==[] (ECHO "ERROR: ninja is not in your PATH") + +:: Starting from this script's path, walk up to engine source directory. +SET SCRIPT_DIR=%~dp0 +FOR %%a IN ("%SCRIPT_DIR:~0,-1%") DO SET TMP=%%~dpa +FOR %%a IN ("%TMP:~0,-1%") DO SET TMP=%%~dpa +FOR %%a IN ("%TMP:~0,-1%") DO SET TMP=%%~dpa +FOR %%a IN ("%TMP:~0,-1%") DO SET ENGINE_SRC_DIR=%%~dpa + +SET ENGINE_SRC_DIR=%ENGINE_SRC_DIR:~0,-1% +SET OUT_DIR=%ENGINE_SRC_DIR%\out +SET HOST_DEBUG_UNOPT_DIR=%OUT_DIR%\host_debug_unopt +SET DART_SDK_DIR=%HOST_DEBUG_UNOPT_DIR%\dart-sdk +SET DART_BIN=%DART_SDK_DIR%\bin\dart +SET PUB_BIN=%DART_SDK_DIR%\bin\pub +SET FLUTTER_DIR=%ENGINE_SRC_DIR%\flutter +SET WEB_UI_DIR=%FLUTTER_DIR%\lib\web_ui +SET DEV_DIR=%WEB_UI_DIR%\dev +SET FELT_PATH=%DEV_DIR%\felt.dart +SET DART_TOOL_DIR=%WEB_UI_DIR%\.dart_tool +SET SNAPSHOT_PATH=%DART_TOOL_DIR%\felt.snapshot + +SET needsHostDebugUnoptRebuild=0 +for %%x in (%*) do ( + if ["%%~x"]==["--clean"] ( + ECHO Clean rebuild requested + SET needsHostDebugUnoptRebuild=1 + ) +) + +IF NOT EXIST %OUT_DIR% (SET needsHostDebugUnoptRebuild=1) +IF NOT EXIST %HOST_DEBUG_UNOPT_DIR% (SET needsHostDebugUnoptRebuild=1) + +IF %needsHostDebugUnoptRebuild%==1 ( + ECHO Building host_debug_unopt + :: Delete old snapshot, if any, because the new Dart SDK may invalidate it. + IF EXIST "%SNAPSHOT_PATH%" ( + del %SNAPSHOT_PATH% + ) + CALL gclient sync -D + CALL python %GN% --unoptimized --full-dart-sdk + CALL ninja -C %HOST_DEBUG_UNOPT_DIR%) + +cd %WEB_UI_DIR% +IF NOT EXIST "%SNAPSHOT_PATH%" ( + ECHO Precompiling felt snapshot + CALL %PUB_BIN% get + %DART_BIN% --snapshot="%SNAPSHOT_PATH%" --packages="%WEB_UI_DIR%\.packages" %FELT_PATH% +) + +IF %1==test ( + %DART_SDK_DIR%\bin\dart --packages="%WEB_UI_DIR%\.packages" "%SNAPSHOT_PATH%" %* --browser=chrome +) ELSE ( + %DART_SDK_DIR%\bin\dart --packages="%WEB_UI_DIR%\.packages" "%SNAPSHOT_PATH%" %* +) + +EXIT /B %ERRORLEVEL% diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index 8e8817ec99391..ec036869aa025 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:io' as io; import 'package:args/command_runner.dart'; @@ -10,22 +9,27 @@ import 'package:args/command_runner.dart'; import 'build.dart'; import 'clean.dart'; import 'create_simulator.dart'; -import 'licenses.dart'; import 'exceptions.dart'; +import 'licenses.dart'; +import 'run.dart'; import 'test_runner.dart'; import 'utils.dart'; -CommandRunner runner = CommandRunner( +CommandRunner runner = CommandRunner( 'felt', 'Command-line utility for building and testing Flutter web engine.', ) + ..addCommand(BuildCommand()) ..addCommand(CleanCommand()) ..addCommand(CreateSimulatorCommand()) ..addCommand(LicensesCommand()) - ..addCommand(TestCommand()) - ..addCommand(BuildCommand()); + ..addCommand(RunCommand()) + ..addCommand(TestCommand()); + +Future main(List rawArgs) async { + // Remove --clean from the list as that's processed by the wrapper script. + final List args = rawArgs.where((String arg) => arg != '--clean').toList(); -void main(List args) async { if (args.isEmpty) { // The felt tool was invoked with no arguments. Print usage. runner.printUsage(); @@ -36,23 +40,23 @@ void main(List args) async { int exitCode = -1; try { - final bool result = (await runner.run(args)) as bool; - if (result == false) { - print('Sub-command returned false: `${args.join(' ')}`'); + final bool? result = await runner.run(args); + if (result != true) { + print('Sub-command failed: `${args.join(' ')}`'); exitCode = 1; } } on UsageException catch (e) { print(e); exitCode = 64; // Exit code 64 indicates a usage error. - } on ToolException catch (e) { - io.stderr.writeln(e.message); - exitCode = 1; + } on ToolExit catch (exception) { + io.stderr.writeln(exception.message); + exitCode = exception.exitCode; } on ProcessException catch (e) { io.stderr.writeln('description: ${e.description}' 'executable: ${e.executable} ' 'arguments: ${e.arguments} ' 'exit code: ${e.exitCode}'); - exitCode = e.exitCode; + exitCode = e.exitCode ?? 1; } catch (e) { rethrow; } finally { @@ -67,7 +71,7 @@ void main(List args) async { io.exit(io.exitCode); } -void _listenToShutdownSignals() async { +Future _listenToShutdownSignals() async { io.ProcessSignal.sigint.watch().listen((_) async { print('Received SIGINT. Shutting down.'); await cleanup(); diff --git a/lib/web_ui/dev/felt_windows.bat b/lib/web_ui/dev/felt_windows.bat index 2ae0e2ab8b125..f79bd49e0c52d 100644 --- a/lib/web_ui/dev/felt_windows.bat +++ b/lib/web_ui/dev/felt_windows.bat @@ -1,3 +1,4 @@ +:: TODO(yjbanov): migrate LUCI to felt.bat and delete this file. :: felt_windows: a command-line utility for Windows for building and testing :: Flutter web engine. :: FELT stands for Flutter Engine Local Tester. @@ -38,13 +39,6 @@ SET SNAPSHOT_PATH="%DART_TOOL_DIR%felt.snapshot" CD %FLUTTER_DIR% FOR /F "tokens=1 delims=:" %%a in ('git rev-parse HEAD') DO SET REVISION=%%a -:: Uncomment for debugging the values. -:: ECHO "FELT_DIR:%FELT_DIR%" -:: ECHO "WEB_UI_DIR:%WEB_UI_DIR%" -:: ECHO "FLUTTER_DIR:%FLUTTER_DIR%" -:: ECHO "ENGINE_SRC_DIR:%ENGINE_SRC_DIR%" -:: ECHO "REVISION:%REVISION%" - SET orTempValue=1 IF NOT EXIST %OUT_DIR% (SET orTempValue=0) IF NOT EXIST %HOST_DEBUG_UNOPT_DIR% (SET orTempValue=0) @@ -54,7 +48,7 @@ IF %orTempValue%==0 ( CALL python %GN% --unoptimized --full-dart-sdk CALL ninja -C %HOST_DEBUG_UNOPT_DIR%) -:: TODO(nurhan): The batch script does not support snanphot option. +:: TODO(yjbanov): The batch script does not support snanphot option. :: Support snapshot option. CALL :installdeps IF %1==test (%DART_SDK_DIR%\bin\dart "%DEV_DIR%\felt.dart" %* --browser=chrome) ELSE ( %DART_SDK_DIR%\bin\dart "%DEV_DIR%\felt.dart" %* ) diff --git a/lib/web_ui/dev/firefox.dart b/lib/web_ui/dev/firefox.dart index d3849c957dcc0..61c02da271c88 100644 --- a/lib/web_ui/dev/firefox.dart +++ b/lib/web_ui/dev/firefox.dart @@ -2,21 +2,43 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io'; -import 'package:pedantic/pedantic.dart'; - import 'package:path/path.dart' as path; -import 'package:test_core/src/util/io.dart'; // ignore: implementation_imports +import 'package:pedantic/pedantic.dart'; +import 'package:test_api/src/backend/runtime.dart'; +import 'package:test_core/src/util/io.dart'; import 'browser.dart'; +import 'browser_lock.dart'; import 'common.dart'; import 'environment.dart'; import 'firefox_installer.dart'; -/// A class for running an instance of Firefox. +/// Provides an environment for the desktop Firefox. +class FirefoxEnvironment implements BrowserEnvironment { + @override + Browser launchBrowserInstance(Uri url, {bool debug = false}) { + return Firefox(url, debug: debug); + } + + @override + Runtime get packageTestRuntime => Runtime.firefox; + + @override + Future prepareEnvironment() async { + // Firefox doesn't need any special prep. + } + + @override + String get packageTestConfigurationYamlFile => 'dart_test_firefox.yaml'; + + @override + ScreenshotManager? getScreenshotManager() => null; +} + +/// Runs desktop Firefox. /// /// Most of the communication with the browser is expected to happen via HTTP, /// so this exposes a bare-bones API. The browser starts as soon as the class is @@ -25,28 +47,23 @@ import 'firefox_installer.dart'; /// Any errors starting or running the process are reported through [onExit]. class Firefox extends Browser { @override - final name = 'Firefox'; + final String name = 'Firefox'; @override final Future remoteDebuggerUrl; - static String version; - /// Starts a new instance of Firefox open to the given [url], which may be a /// [Uri] or a [String]. factory Firefox(Uri url, {bool debug = false}) { - version = FirefoxArgParser.instance.version; - - assert(version != null); - var remoteDebuggerCompleter = Completer.sync(); + final Completer remoteDebuggerCompleter = Completer.sync(); return Firefox._(() async { final BrowserInstallation installation = await getOrInstallFirefox( - version, + browserLock.firefoxLock.version, infoLog: isCirrus ? stdout : DevNull(), ); // Using a profile on opening will prevent popups related to profiles. - final _profile = ''' + const String _profile = ''' user_pref("browser.shell.checkDefaultBrowser", false); user_pref("dom.disable_open_during_load", false); user_pref("dom.max_script_run_time", 0); @@ -65,16 +82,18 @@ user_pref("dom.max_script_run_time", 0); File(path.join(temporaryProfileDirectory.path, 'prefs.js')) .writeAsStringSync(_profile); - bool isMac = Platform.isMacOS; - var args = [ + final bool isMac = Platform.isMacOS; + final List args = [ url.toString(), '--profile', - '${temporaryProfileDirectory.path}', - '--headless', + temporaryProfileDirectory.path, + if (!debug) + '--headless', '-width $kMaxScreenshotWidth', '-height $kMaxScreenshotHeight', - isMac ? '--new-window' : '-new-window', - isMac ? '--new-instance' : '-new-instance', + // On Mac Firefox uses the -- option prefix, while elsewhere it uses the - prefix. + '${isMac ? '-' : ''}-new-window', + '${isMac ? '-' : ''}-new-instance', '--start-debugger-server $kDevtoolsPort', ]; @@ -92,6 +111,6 @@ user_pref("dom.max_script_run_time", 0); }, remoteDebuggerCompleter.future); } - Firefox._(Future startBrowser(), this.remoteDebuggerUrl) + Firefox._(Future Function() startBrowser, this.remoteDebuggerUrl) : super(startBrowser); } diff --git a/lib/web_ui/dev/firefox_installer.dart b/lib/web_ui/dev/firefox_installer.dart index e282d2a3df23f..2d48c16e025ce 100644 --- a/lib/web_ui/dev/firefox_installer.dart +++ b/lib/web_ui/dev/firefox_installer.dart @@ -1,55 +1,16 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 + import 'dart:io' as io; -import 'package:args/args.dart'; import 'package:http/http.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:yaml/yaml.dart'; import 'common.dart'; import 'environment.dart'; import 'exceptions.dart'; -class FirefoxArgParser extends BrowserArgParser { - static final FirefoxArgParser _singletonInstance = FirefoxArgParser._(); - - /// The [FirefoxArgParser] singleton. - static FirefoxArgParser get instance => _singletonInstance; - - String _version; - - FirefoxArgParser._(); - - @override - void populateOptions(ArgParser argParser) { - final YamlMap browserLock = BrowserLock.instance.configuration; - String firefoxVersion = browserLock['firefox']['version'] as String; - - argParser - ..addOption( - 'firefox-version', - defaultsTo: '$firefoxVersion', - help: 'The Firefox version to use while running tests. If the requested ' - 'version has not been installed, it will be downloaded and installed ' - 'automatically. Value "latest" will use the latest ' - 'stable build of Firefox, installing it if necessary. Value "system" ' - 'will use the manually installed version of Firefox on this computer.', - ); - } - - @override - void parseOptions(ArgResults argResults) { - _version = argResults['firefox-version'] as String; - } - - @override - String get version => _version; -} - /// Returns the installation of Firefox, installing it if necessary. /// /// If [requestedVersion] is null, uses the version specified on the @@ -64,7 +25,7 @@ class FirefoxArgParser extends BrowserArgParser { /// https://download-installer.cdn.mozilla.net/pub/firefox/releases/ Future getOrInstallFirefox( String requestedVersion, { - StringSink infoLog, + StringSink? infoLog, }) async { // These tests are aimed to run only on the Linux containers in Cirrus. // Therefore Firefox installation is implemented only for Linux now. @@ -82,7 +43,7 @@ Future getOrInstallFirefox( ); } - FirefoxInstaller installer; + FirefoxInstaller? installer; try { installer = requestedVersion == 'latest' ? await FirefoxInstaller.latest() @@ -94,11 +55,11 @@ Future getOrInstallFirefox( } else { infoLog.writeln('Installing Firefox version: ${installer.version}'); await installer.install(); - final BrowserInstallation installation = installer.getInstallation(); + final BrowserInstallation installation = installer.getInstallation()!; infoLog.writeln( 'Installations complete. To launch it run ${installation.executable}'); } - return installer.getInstallation(); + return installer.getInstallation()!; } finally { installer?.close(); } @@ -107,7 +68,7 @@ Future getOrInstallFirefox( /// Manages the installation of a particular [version] of Firefox. class FirefoxInstaller { factory FirefoxInstaller({ - @required String version, + required String version, }) { if (version == 'system') { throw BrowserInstallerException( @@ -138,9 +99,9 @@ class FirefoxInstaller { } FirefoxInstaller._({ - @required this.version, - @required this.firefoxInstallationDir, - @required this.versionDir, + required this.version, + required this.firefoxInstallationDir, + required this.versionDir, }); /// Firefox version managed by this installer. @@ -159,7 +120,7 @@ class FirefoxInstaller { return versionDir.existsSync(); } - BrowserInstallation getInstallation() { + BrowserInstallation? getInstallation() { if (!isInstalled) { return null; } @@ -197,7 +158,7 @@ class FirefoxInstaller { final io.File downloadedFile = io.File(path.join(versionDir.path, PlatformBinding.instance.getFirefoxDownloadFilename(version))); - io.IOSink sink = downloadedFile.openWrite(); + final io.IOSink sink = downloadedFile.openWrite(); await download.stream.pipe(sink); await sink.flush(); await sink.close(); @@ -227,12 +188,12 @@ class FirefoxInstaller { /// Mounts the dmg file using hdiutil, copies content of the volume to /// target path and then unmounts dmg ready for deletion. Future _mountDmgAndCopy(io.File dmgFile) async { - String volumeName = await _hdiUtilMount(dmgFile); + final String volumeName = await _hdiUtilMount(dmgFile); final String sourcePath = '$volumeName/Firefox.app'; final String targetPath = path.dirname(dmgFile.path); try { - io.ProcessResult installResult = await io.Process.run('cp', [ + final io.ProcessResult installResult = await io.Process.run('cp', [ '-r', sourcePath, targetPath, @@ -250,10 +211,10 @@ class FirefoxInstaller { } Future _hdiUtilMount(io.File dmgFile) async { - io.ProcessResult mountResult = await io.Process.run('hdiutil', [ + final io.ProcessResult mountResult = await io.Process.run('hdiutil', [ 'attach', '-readonly', - '${dmgFile.path}', + dmgFile.path, ]); if (mountResult.exitCode != 0) { throw BrowserInstallerException( @@ -261,8 +222,8 @@ class FirefoxInstaller { 'Exit code ${mountResult.exitCode}.\n${mountResult.stderr}'); } - List processOutput = (mountResult.stdout as String).split('\n'); - String volumePath = _volumeFromMountResult(processOutput); + final List processOutput = (mountResult.stdout as String).split('\n'); + final String? volumePath = _volumeFromMountResult(processOutput); if (volumePath == null) { throw BrowserInstallerException( 'Failed to parse mount dmg result ${processOutput.join('\n')}.\n' @@ -273,9 +234,9 @@ class FirefoxInstaller { // Parses volume from mount result. // Output is of form: {devicename} /Volumes/{name}. - String _volumeFromMountResult(List lines) { - for (String line in lines) { - int pos = line.indexOf('/Volumes'); + String? _volumeFromMountResult(List lines) { + for (final String line in lines) { + final int pos = line.indexOf('/Volumes'); if (pos != -1) { return line.substring(pos); } @@ -284,13 +245,13 @@ class FirefoxInstaller { } Future _hdiUtilUnmount(String volumeName) async { - io.ProcessResult unmountResult = await io.Process.run('hdiutil', [ + final io.ProcessResult unmountResult = await io.Process.run('hdiutil', [ 'unmount', - '$volumeName', + volumeName, ]); if (unmountResult.exitCode != 0) { throw BrowserInstallerException( - 'Failed to unmount Firefox disk image ${volumeName}.\n' + 'Failed to unmount Firefox disk image $volumeName.\n' 'Exit code ${unmountResult.exitCode}. ${unmountResult.stderr}'); } } @@ -303,13 +264,13 @@ class FirefoxInstaller { Future _findSystemFirefoxExecutable() async { final io.ProcessResult which = await io.Process.run('which', ['firefox']); - bool found = which.exitCode != 0; + final bool found = which.exitCode != 0; const String fireFoxDefaultInstallPath = '/Applications/Firefox.app/Contents/MacOS/firefox'; if (!found) { if (io.Platform.isMacOS && io.File(fireFoxDefaultInstallPath).existsSync()) { - return Future.value(fireFoxDefaultInstallPath); + return Future.value(fireFoxDefaultInstallPath); } throw BrowserInstallerException( 'Failed to locate system Firefox installation.'); @@ -319,35 +280,35 @@ Future _findSystemFirefoxExecutable() async { /// Fetches the latest available Firefox build version on Linux. Future fetchLatestFirefoxVersionLinux() async { - final RegExp forFirefoxVersion = RegExp("firefox-[0-9.]\+[0-9]"); + final RegExp forFirefoxVersion = RegExp('firefox-[0-9.]+[0-9]'); final io.HttpClientRequest request = await io.HttpClient() .getUrl(Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl())); request.followRedirects = false; // We will parse the HttpHeaders to find the redirect location. final io.HttpClientResponse response = await request.close(); - final String location = response.headers.value('location'); - final String version = forFirefoxVersion.stringMatch(location); + final String location = response.headers.value('location')!; + final String version = forFirefoxVersion.stringMatch(location)!; return version.substring(version.lastIndexOf('-') + 1); } /// Fetches the latest available Firefox build version on Mac OS. Future fetchLatestFirefoxVersionMacOS() async { - final RegExp forFirefoxVersion = RegExp("firefox\/releases\/[0-9.]\+[0-9]"); + final RegExp forFirefoxVersion = RegExp('firefox/releases/[0-9.]+[0-9]'); final io.HttpClientRequest request = await io.HttpClient() .getUrl(Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl())); request.followRedirects = false; // We will parse the HttpHeaders to find the redirect location. final io.HttpClientResponse response = await request.close(); - final String location = response.headers.value('location'); - final String version = forFirefoxVersion.stringMatch(location); + final String location = response.headers.value('location')!; + final String version = forFirefoxVersion.stringMatch(location)!; return version.substring(version.lastIndexOf('/') + 1); } Future getInstaller({String requestedVersion = 'latest'}) async { - FirefoxInstaller installer; + FirefoxInstaller? installer; try { installer = requestedVersion == 'latest' ? await FirefoxInstaller.latest() @@ -359,11 +320,11 @@ Future getInstaller({String requestedVersion = 'latest'}) a } else { print('Installing Firefox version: ${installer.version}'); await installer.install(); - final BrowserInstallation installation = installer.getInstallation(); + final BrowserInstallation installation = installer.getInstallation()!; print( 'Installations complete. To launch it run ${installation.executable}'); } - return installer.getInstallation(); + return installer.getInstallation()!; } finally { installer?.close(); } diff --git a/lib/web_ui/dev/firefox_installer_test.dart b/lib/web_ui/dev/firefox_installer_test.dart deleted file mode 100644 index fc18277f53b14..0000000000000 --- a/lib/web_ui/dev/firefox_installer_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -@TestOn('vm && linux') - -import 'dart:io' as io; - -import 'package:path/path.dart' as path; -import 'package:test/test.dart'; - -import 'common.dart'; -import 'environment.dart'; -import 'firefox_installer.dart'; - -void main() async { - void deleteFirefoxInstallIfExists() { - final io.Directory firefoxInstallationDir = io.Directory( - path.join(environment.webUiDartToolDir.path, 'firefox'), - ); - - if (firefoxInstallationDir.existsSync()) { - firefoxInstallationDir.deleteSync(recursive: true); - } - } - - setUpAll(() { - deleteFirefoxInstallIfExists(); - }); - - tearDown(() { - deleteFirefoxInstallIfExists(); - }); - - test('installs a given version of Firefox', () async { - FirefoxInstaller installer = FirefoxInstaller(version: '69.0.2'); - expect(installer.isInstalled, isFalse); - - BrowserInstallation installation = await getOrInstallFirefox('69.0.2'); - - expect(installation.version, '69.0.2'); - expect(installer.isInstalled, isTrue); - expect(io.File(installation.executable).existsSync(), isTrue); - }); -} diff --git a/lib/web_ui/dev/goldens.dart b/lib/web_ui/dev/goldens.dart deleted file mode 100644 index fef621c724385..0000000000000 --- a/lib/web_ui/dev/goldens.dart +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// @dart = 2.6 -import 'dart:io' as io; -import 'package:image/image.dart'; -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; -import 'package:yaml/yaml.dart'; - -import 'environment.dart'; -import 'utils.dart'; - -/// How to compares pixels within the image. -/// -/// Keep this enum in sync with the one defined in `golden_tester.dart`. -enum PixelComparison { - /// Allows minor blur and anti-aliasing differences by comparing a 3x3 grid - /// surrounding the pixel rather than direct 1:1 comparison. - fuzzy, - - /// Compares one pixel at a time. - /// - /// Anti-aliasing or blur will result in higher diff rate. - precise, -} - -void main(List args) { - final io.File fileA = io.File(args[0]); - final io.File fileB = io.File(args[1]); - final Image imageA = decodeNamedImage(fileA.readAsBytesSync(), 'a.png'); - final Image imageB = decodeNamedImage(fileB.readAsBytesSync(), 'b.png'); - final ImageDiff diff = ImageDiff(golden: imageA, other: imageB, pixelComparison: PixelComparison.fuzzy); - print('Diff: ${(diff.rate * 100).toStringAsFixed(4)}%'); -} - -/// This class encapsulates visually diffing an Image with any other. -/// Both images need to be the exact same size. -class ImageDiff { - - /// The image to match - final Image golden; - - /// The image being compared - final Image other; - - /// Algorithm used for comparing pixels. - final PixelComparison pixelComparison; - - /// The output of the comparison - /// Pixels in the output image can have 3 different colors depending on the comparison - /// between golden pixels and other pixels: - /// * white: when both pixels are the same - /// * red: when a pixel is found in other, but not in golden - /// * green: when a pixel is found in golden, but not in other - Image diff; - - /// The ratio of wrong pixels to all pixels in golden (between 0 and 1) - /// This gets set to 1 (100% difference) when golden and other aren't the same size. - double get rate => _wrongPixels / _pixelCount; - - ImageDiff({ - @required this.golden, - @required this.other, - @required this.pixelComparison, - }) { - _computeDiff(); - } - - int _pixelCount = 0; - int _wrongPixels = 0; - - /// That would be the distance between black and white. - static final double _maxTheoreticalColorDistance = Color.distance( - [255, 255, 255], // white - [0, 0, 0], // black - false, - ).toDouble(); - - // If the normalized color difference of a pixel is greater than this number, - // we consider it a wrong pixel. - static const double _kColorDistanceThreshold = 0.1; - - final int _colorOk = Color.fromRgb(255, 255, 255); - final int _colorBadPixel = Color.fromRgb(255, 0, 0); - final int _colorExpectedPixel = Color.fromRgb(0, 255, 0); - - /// Reads a pixel value out of [image] at [x] and [y]. - /// - /// If the pixel is out of bounds, reflects the [x] and [y] coordinates off - /// the border back into the image treating the border like a mirror. - static int _reflectedPixel(Image image, int x, int y) { - x = x.abs(); - if (x == image.width) { - x = image.width - 2; - } - - y = y.abs(); - if (y == image.height) { - y = image.height - 2; - } - - return image.getPixel(x, y); - } - - static int _average(Iterable values) { - return values.reduce((a, b) => a + b) ~/ values.length; - } - - /// The value of the pixel at [x] and [y] coordinates. - /// - /// If [pixelComparison] is [PixelComparison.precise], reads the RGB value of - /// the pixel. - /// - /// If [pixelComparison] is [PixelComparison.fuzzy], reads the RGB values of - /// the average of the 3x3 box of pixels centered at [x] and [y]. - List _getPixelRgbForComparison(Image image, int x, int y) { - switch (pixelComparison) { - case PixelComparison.fuzzy: - final List pixels = [ - _reflectedPixel(image, x - 1, y - 1), - _reflectedPixel(image, x - 1, y), - _reflectedPixel(image, x - 1, y + 1), - - _reflectedPixel(image, x, y - 1), - _reflectedPixel(image, x, y), - _reflectedPixel(image, x, y + 1), - - _reflectedPixel(image, x + 1, y - 1), - _reflectedPixel(image, x + 1, y), - _reflectedPixel(image, x + 1, y + 1), - ]; - return [ - _average(pixels.map((p) => getRed(p))), - _average(pixels.map((p) => getGreen(p))), - _average(pixels.map((p) => getBlue(p))), - ]; - case PixelComparison.precise: - final int pixel = image.getPixel(x, y); - return [ - getRed(pixel), - getGreen(pixel), - getBlue(pixel), - ]; - default: - throw 'Unrecognized pixel comparison value: ${pixelComparison}'; - } - } - - void _computeDiff() { - int goldenWidth = golden.width; - int goldenHeight = golden.height; - - _pixelCount = goldenWidth * goldenHeight; - diff = Image(goldenWidth, goldenHeight); - - if (goldenWidth == other.width && goldenHeight == other.height) { - for(int y = 0; y < goldenHeight; y++) { - for (int x = 0; x < goldenWidth; x++) { - final bool isExactlySame = golden.getPixel(x, y) == other.getPixel(x, y); - final List goldenPixel = _getPixelRgbForComparison(golden, x, y); - final List otherPixel = _getPixelRgbForComparison(other, x, y); - final double colorDistance = Color.distance(goldenPixel, otherPixel, false) / _maxTheoreticalColorDistance; - final bool isFuzzySame = colorDistance < _kColorDistanceThreshold; - if (isExactlySame || isFuzzySame) { - diff.setPixel(x, y, _colorOk); - } else { - final int goldenLuminance = getLuminanceRgb(goldenPixel[0], goldenPixel[1], goldenPixel[2]); - final int otherLuminance = getLuminanceRgb(otherPixel[0], otherPixel[1], otherPixel[2]); - if (goldenLuminance < otherLuminance) { - diff.setPixel(x, y, _colorExpectedPixel); - } else { - diff.setPixel(x, y, _colorBadPixel); - } - _wrongPixels++; - } - } - } - } else { - // Images are completely different resolutions. Bail out big time. - _wrongPixels = _pixelCount; - } - } -} - -// Returns text explaining pixel difference rate. -String getPrintableDiffFilesInfo(double diffRate, double maxRate) => - '(${((diffRate) * 100).toStringAsFixed(4)}% of pixels were different. ' - 'Maximum allowed rate is: ${(maxRate * 100).toStringAsFixed(4)}%).'; - -/// Fetches golden files from github.com/flutter/goldens, cloning the repository if necessary. -/// -/// The repository is cloned into web_ui/.dart_tool. -Future fetchGoldens() async { - await _GoldensRepoFetcher().fetch(); -} - -class _GoldensRepoFetcher { - String _repository; - String _revision; - - Future fetch() async { - final io.File lockFile = io.File( - path.join(environment.webUiDevDir.path, 'goldens_lock.yaml') - ); - final YamlMap lock = loadYaml(lockFile.readAsStringSync()) as YamlMap; - _repository = lock['repository'] as String; - _revision = lock['revision'] as String; - - final String localRevision = await _getLocalRevision(); - if (localRevision == _revision) { - return; - } - - print('Fetching $_repository@$_revision'); - - if (!environment.webUiGoldensRepositoryDirectory.existsSync()) { - environment.webUiGoldensRepositoryDirectory.createSync(recursive: true); - await runProcess( - 'git', - ['init'], - workingDirectory: environment.webUiGoldensRepositoryDirectory.path, - mustSucceed: true, - ); - await runProcess( - 'git', - ['remote', 'add', 'origin', _repository], - workingDirectory: environment.webUiGoldensRepositoryDirectory.path, - mustSucceed: true, - ); - } - - await runProcess( - 'git', - ['fetch', 'origin', 'master'], - workingDirectory: environment.webUiGoldensRepositoryDirectory.path, - mustSucceed: true, - ); - await runProcess( - 'git', - ['checkout', _revision], - workingDirectory: environment.webUiGoldensRepositoryDirectory.path, - mustSucceed: true, - ); - } - - Future _getLocalRevision() async { - final io.File head = io.File(path.join( - environment.webUiGoldensRepositoryDirectory.path, '.git', 'HEAD' - )); - - if (!head.existsSync()) { - return null; - } - - return head.readAsStringSync().trim(); - } -} diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index 067577a642f6f..8395991f969a7 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 1a4722227af42c3f51450266016b1a07ae459e73 +revision: 8517dd15b8eb62e07ecde82170f8a4016566ea49 diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart deleted file mode 100644 index e01a632f219cd..0000000000000 --- a/lib/web_ui/dev/integration_tests_manager.dart +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 - -import 'dart:io' as io; -import 'package:path/path.dart' as pathlib; - -import 'chrome_installer.dart'; -import 'driver_manager.dart'; -import 'environment.dart'; -import 'exceptions.dart'; -import 'common.dart'; -import 'utils.dart'; - -const String _unsupportedConfigurationWarning = 'WARNING: integration tests ' - 'are only supported on Chrome, Firefox and on Safari (running on macOS)'; - -class IntegrationTestsManager { - final String _browser; - - final bool _useSystemFlutter; - - final DriverManager _driverManager; - - IntegrationTestsManager(this._browser, this._useSystemFlutter) - : _driverManager = DriverManager.chooseDriver(_browser); - - Future runTests() async { - if (validateIfTestsShouldRun()) { - await _driverManager.prepareDriver(); - return await _runTests(); - } else { - return false; - } - } - - Future _runPubGet(String workingDirectory) async { - if (!_useSystemFlutter) { - await _cloneFlutterRepo(); - await _enableWeb(workingDirectory); - } - await runFlutter(workingDirectory, ['pub', 'get'], - useSystemFlutter: _useSystemFlutter); - } - - /// Clone flutter repository, use the youngest commit older than the engine - /// commit. - /// - /// Use engine/src/flutter/.dart_tools to clone the Flutter repo. - /// TODO(nurhan): Use git pull instead if repo exists. - Future _cloneFlutterRepo() async { - // Delete directory if exists. - if (environment.engineDartToolDir.existsSync()) { - environment.engineDartToolDir.deleteSync(recursive: true); - } - environment.engineDartToolDir.createSync(); - - final int exitCode = await runProcess( - environment.cloneFlutterScript.path, - [ - environment.engineDartToolDir.path, - ], - workingDirectory: environment.webUiRootDir.path, - ); - - if (exitCode != 0) { - throw ToolException('ERROR: Failed to clone flutter repo. Exited with ' - 'exit code $exitCode'); - } - } - - Future _enableWeb(String workingDirectory) async { - await runFlutter(workingDirectory, ['config', '--enable-web'], - useSystemFlutter: _useSystemFlutter); - } - - /// Runs all the web tests under e2e_tests/web. - Future _runTests() async { - // Only list the files under e2e_tests/web. - final List entities = - environment.integrationTestsDir.listSync(followLinks: false); - - bool allTestsPassed = true; - for (io.FileSystemEntity e in entities) { - // The tests should be under this directories. - if (e is io.Directory) { - allTestsPassed = allTestsPassed && await _validateAndRunTests(e); - } - } - return allTestsPassed; - } - - /// Run tests in a single directory under: e2e_tests/web. - /// - /// Run `flutter pub get` as the first step. - /// - /// Validate the directory before running the tests. Each directory is - /// expected to be a test project which includes a `pubspec.yaml` file - /// and a `test_driver` directory. - Future _validateAndRunTests(io.Directory directory) async { - _validateTestDirectory(directory); - await _runPubGet(directory.path); - final bool testResults = await _runTestsInDirectory(directory); - return testResults; - } - - Future _runTestsInDirectory(io.Directory directory) async { - final io.Directory testDirectory = - io.Directory(pathlib.join(directory.path, 'test_driver')); - final List entities = testDirectory - .listSync(followLinks: false) - .whereType() - .toList(); - - final List e2eTestsToRun = []; - final List blockedTests = - blockedTestsListsMap[getBlockedTestsListMapKey(_browser)] ?? []; - - // The following loops over the contents of the directory and saves an - // expected driver file name for each e2e test assuming any dart file - // not ending with `_test.dart` is an e2e test. - // Other files are not considered since developers can add files such as - // README. - for (io.File f in entities) { - final String basename = pathlib.basename(f.path); - if (!basename.contains('_test.dart') && basename.endsWith('.dart')) { - // Do not add the basename if it is in the `blockedTests`. - if (!blockedTests.contains(basename)) { - e2eTestsToRun.add(basename); - } else { - print('INFO: Test $basename is skipped since it is blocked for ' - '${getBlockedTestsListMapKey(_browser)}'); - } - } - } - - print( - 'INFO: In project ${directory} ${e2eTestsToRun.length} tests to run.'); - - int numberOfPassedTests = 0; - int numberOfFailedTests = 0; - for (String fileName in e2eTestsToRun) { - final bool testResults = - await _runTestsInProfileMode(directory, fileName); - if (testResults) { - numberOfPassedTests++; - } else { - numberOfFailedTests++; - } - } - final int numberOfTestsRun = numberOfPassedTests + numberOfFailedTests; - - print('INFO: ${numberOfTestsRun} tests run. ${numberOfPassedTests} passed ' - 'and ${numberOfFailedTests} failed.'); - return numberOfFailedTests == 0; - } - - Future _runTestsInProfileMode( - io.Directory directory, String testName) async { - final String executable = - _useSystemFlutter ? 'flutter' : environment.flutterCommand.path; - final IntegrationArguments arguments = - IntegrationArguments.fromBrowser(_browser); - final int exitCode = await runProcess( - executable, - arguments.getTestArguments(testName, 'profile'), - workingDirectory: directory.path, - ); - - if (exitCode != 0) { - io.stderr - .writeln('ERROR: Failed to run test. Exited with exit code $exitCode' - '. To run $testName locally use the following command:' - '\n\n${arguments.getCommandToRun(testName, 'profile')}'); - return false; - } else { - return true; - } - } - - /// Validate the directory has a `pubspec.yaml` file and a `test_driver` - /// directory. - /// - /// Also check the validity of files under `test_driver` directory calling - /// [_checkE2ETestsValidity] method. - void _validateTestDirectory(io.Directory directory) { - final List entities = - directory.listSync(followLinks: false); - - // Whether the project has the pubspec.yaml file. - bool pubSpecFound = false; - // The test directory 'test_driver'. - io.Directory testDirectory; - - for (io.FileSystemEntity e in entities) { - // The tests should be under this directories. - final String baseName = pathlib.basename(e.path); - if (e is io.Directory && baseName == 'test_driver') { - testDirectory = e; - } - if (e is io.File && baseName == 'pubspec.yaml') { - pubSpecFound = true; - } - } - if (!pubSpecFound) { - throw StateError('ERROR: pubspec.yaml file not found in the test project ' - 'in the directory ${directory.path}.'); - } - if (testDirectory == null) { - throw StateError( - 'ERROR: test_driver folder not found in the test project.' - 'in the directory ${directory.path}.'); - } else { - _checkE2ETestsValidity(testDirectory); - } - } - - /// Checks if each e2e test file in the directory has a driver test - /// file to run it. - /// - /// Prints informative message to the developer if an error has found. - /// For each e2e test which has name {name}.dart there will be a driver - /// file which drives it. The driver file should be named: - /// {name}_test.dart - void _checkE2ETestsValidity(io.Directory testDirectory) { - final Iterable directories = - testDirectory.listSync(followLinks: false).whereType(); - - if (directories.length > 0) { - throw StateError('${testDirectory.path} directory should not contain ' - 'any sub-directories'); - } - - final Iterable entities = - testDirectory.listSync(followLinks: false).whereType(); - - final Set expectedDriverFileNames = Set(); - final Set foundDriverFileNames = Set(); - int numberOfTests = 0; - - // The following loops over the contents of the directory and saves an - // expected driver file name for each e2e test assuming any file - // not ending with `_test.dart` is an e2e test. - for (io.File f in entities) { - final String basename = pathlib.basename(f.path); - if (basename.contains('_test.dart')) { - // First remove this from expectedSet if not there add to the foundSet. - if (!expectedDriverFileNames.remove(basename)) { - foundDriverFileNames.add(basename); - } - } else if (basename.contains('.dart')) { - // Only run on dart files. - final String e2efileName = pathlib.basenameWithoutExtension(f.path); - final String expectedDriverName = '${e2efileName}_test.dart'; - numberOfTests++; - // First remove this from foundSet if not there add to the expectedSet. - if (!foundDriverFileNames.remove(expectedDriverName)) { - expectedDriverFileNames.add(expectedDriverName); - } - } - } - - if (numberOfTests == 0) { - throw StateError( - 'WARNING: No tests to run in this directory ${testDirectory.path}'); - } - - // TODO(nurhan): In order to reduce the work required from team members, - // remove the need for driver file, by using the same template file. - // Some driver files are missing. - if (expectedDriverFileNames.length > 0) { - for (String expectedDriverName in expectedDriverFileNames) { - print('ERROR: Test driver file named has ${expectedDriverName} ' - 'not found under directory ${testDirectory.path}. Stopping the ' - 'integration tests. Please add ${expectedDriverName}. Check to ' - 'README file on more details on how to setup integration tests.'); - } - throw StateError('Error in test files. Check the logs for ' - 'further instructions'); - } - } - - /// Validate the given `browser`, `platform` combination is suitable for - /// integration tests to run. - bool validateIfTestsShouldRun() { - // Chrome tests should run at all Platforms (Linux, macOS, Windows). - // They can also run successfully on CI and local. - if (_browser == 'chrome') { - return true; - } else if (_browser == 'firefox' && - (io.Platform.isLinux || io.Platform.isMacOS)) { - return true; - } else if (_browser == 'safari' && io.Platform.isMacOS && !isLuci) { - return true; - } else { - io.stderr.writeln(_unsupportedConfigurationWarning); - return false; - } - } -} - -/// Interface for collecting arguments to give `flutter drive` to run the -/// integration tests. -abstract class IntegrationArguments { - IntegrationArguments(); - - factory IntegrationArguments.fromBrowser(String browser) { - if (browser == 'chrome') { - return ChromeIntegrationArguments(); - } else if (browser == 'firefox') { - return FirefoxIntegrationArguments(); - } else if (browser == 'safari' && io.Platform.isMacOS) { - return SafariIntegrationArguments(); - } else { - throw StateError(_unsupportedConfigurationWarning); - } - } - - List getTestArguments(String testName, String mode); - - String getCommandToRun(String testName, String mode); -} - -/// Arguments to give `flutter drive` to run the integration tests on Chrome. -class ChromeIntegrationArguments extends IntegrationArguments { - List getTestArguments(String testName, String mode) { - return [ - 'drive', - '--target=test_driver/${testName}', - '-d', - 'web-server', - '--$mode', - '--browser-name=chrome', - if (isLuci) '--chrome-binary=${preinstalledChromeExecutable()}', - if (isLuci) '--headless', - '--local-engine=host_debug_unopt', - ]; - } - - String getCommandToRun(String testName, String mode) { - String statementToRun = 'flutter drive ' - '--target=test_driver/${testName} -d web-server --profile ' - '--browser-name=chrome --local-engine=host_debug_unopt'; - if (isLuci) { - statementToRun = '$statementToRun --chrome-binary=' - '${preinstalledChromeExecutable()}'; - } - return statementToRun; - } -} - -/// Arguments to give `flutter drive` to run the integration tests on Firefox. -class FirefoxIntegrationArguments extends IntegrationArguments { - List getTestArguments(String testName, String mode) { - return [ - 'drive', - '--target=test_driver/${testName}', - '-d', - 'web-server', - '--$mode', - '--browser-name=firefox', - '--headless', - '--local-engine=host_debug_unopt', - ]; - } - - String getCommandToRun(String testName, String mode) => - 'flutter ${getTestArguments(testName, mode).join(' ')}'; -} - -/// Arguments to give `flutter drive` to run the integration tests on Safari. -class SafariIntegrationArguments extends IntegrationArguments { - SafariIntegrationArguments(); - - List getTestArguments(String testName, String mode) { - return [ - 'drive', - '--target=test_driver/${testName}', - '-d', - 'web-server', - '--$mode', - '--browser-name=safari', - '--local-engine=host_debug_unopt', - ]; - } - - String getCommandToRun(String testName, String mode) => - 'flutter ${getTestArguments(testName, mode).join(' ')}'; -} - -/// Prepares a key for the [blackList] map. -/// -/// Uses the browser name and the operating system name. -String getBlockedTestsListMapKey(String browser) => - '${browser}-${io.Platform.operatingSystem}'; - -/// Tests that should be skipped run for a specific platform-browser -/// combination. -/// -/// These tests might be failing or might have been implemented for a specific -/// configuration. -/// -/// For example when adding a tests only intended for mobile browsers, it should -/// be added to [blockedTests] for `chrome-linux`, `safari-macos` and -/// `chrome-macos`. It will work on `chrome-android`, `safari-ios`. -/// -/// Note that integration tests are only running on chrome for now. -const Map> blockedTestsListsMap = >{ - 'chrome-linux': [ - 'target_platform_android_integration.dart', - 'target_platform_ios_integration.dart', - 'target_platform_macos_integration.dart', - ], - 'chrome-macos': [ - 'target_platform_ios_integration.dart', - 'target_platform_android_integration.dart', - ], - 'safari-macos': [ - 'target_platform_ios_integration.dart', - 'target_platform_android_integration.dart', - 'image_loading_integration.dart', - ], - 'firefox-linux': [ - 'target_platform_android_integration.dart', - 'target_platform_ios_integration.dart', - 'target_platform_macos_integration.dart', - ], - 'firefox-macos': [ - 'target_platform_android_integration.dart', - 'target_platform_ios_integration.dart', - ], -}; diff --git a/lib/web_ui/dev/licenses.dart b/lib/web_ui/dev/licenses.dart index 5138008fb9ea5..7048576c62499 100644 --- a/lib/web_ui/dev/licenses.dart +++ b/lib/web_ui/dev/licenses.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:io' as io; import 'package:args/command_runner.dart'; @@ -30,9 +29,9 @@ class LicensesCommand extends Command { 'Dart source listing of ${environment.webUiRootDir.path} must not be empty.'); final List allDartPaths = - allSourceFiles.map((f) => f.path).toList(); + allSourceFiles.map((io.File f) => f.path).toList(); - for (String expectedDirectory in const [ + for (final String expectedDirectory in const [ 'lib', 'test', 'dev', @@ -42,7 +41,7 @@ class LicensesCommand extends Command { path.join(environment.webUiRootDir.path, expectedDirectory); _expect( allDartPaths - .where((p) => p.startsWith(expectedAbsoluteDirectory)) + .where((String p) => p.startsWith(expectedAbsoluteDirectory)) .isNotEmpty, 'Must include the $expectedDirectory/ directory', ); @@ -52,11 +51,11 @@ class LicensesCommand extends Command { print('License headers OK!'); } - final _copyRegex = + final RegExp _copyRegex = RegExp(r'// Copyright 2013 The Flutter Authors\. All rights reserved\.'); void _expectLicenseHeader(io.File file) { - List head = file.readAsStringSync().split('\n').take(3).toList(); + final List head = file.readAsStringSync().split('\n').take(3).toList(); _expect(head.length >= 3, 'File too short: ${file.path}'); _expect( @@ -70,18 +69,18 @@ class LicensesCommand extends Command { ); _expect( head[2] == '// found in the LICENSE file.', - 'Invalid second line of license header in file ${file.path}', + 'Invalid third line of license header in file ${file.path}', ); } void _expect(bool value, String requirement) { if (!value) { - throw Exception('Test failed: ${requirement}'); + throw Exception('Test failed: $requirement'); } } List _flatListSourceFiles(io.Directory directory) { - return directory.listSync(recursive: true).whereType().where((f) { + return directory.listSync(recursive: true).whereType().where((io.File f) { if (!f.path.endsWith('.dart') && !f.path.endsWith('.js')) { // Not a source file we're checking. return false; diff --git a/lib/web_ui/dev/macos_info.dart b/lib/web_ui/dev/macos_info.dart deleted file mode 100644 index b406aa6a32e88..0000000000000 --- a/lib/web_ui/dev/macos_info.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:convert'; - -import 'utils.dart'; - -class MacOSInfo { - /// Print information collected from the operating system. - /// - /// Built in tools such as `system_profiler` and `defaults` are utilized. - Future printInformation() async { - try { - await _printSafariApplications(); - } catch (error) { - print('Error thrown while getting Safari Applications: $error'); - } - - try { - await _printSafariDefaults(); - } catch (error) { - print('Error thrown while getting Safari defaults: $error'); - } - - try { - await _printUserLimits(); - } catch (error) { - print('Error thrown while getting user limits defaults: $error'); - } - } - - /// Print information on applications in the system that contains string - /// `Safari`. - Future _printSafariApplications() async { - final String systemProfileJson = await evalProcess( - 'system_profiler', ['SPApplicationsDataType', '-json']); - - final Map json = - jsonDecode(systemProfileJson) as Map; - final List systemProfile = json.values.first as List; - for (int i = 0; i < systemProfile.length; i++) { - final Map application = - systemProfile[i] as Map; - final String applicationName = application['_name'] as String; - if (applicationName.contains('Safari')) { - print('application: $applicationName ' - 'fullInfo: ${application.toString()}'); - } - } - } - - /// Print all the defaults in the system related to Safari. - Future _printSafariDefaults() async { - final String defaults = - await evalProcess('/usr/bin/defaults', ['find', 'Safari']); - - print('Safari related defaults:\n $defaults'); - } - - /// Print user limits (file and process). - Future _printUserLimits() async { - final String fileLimit = await evalProcess('ulimit', ['-n']); - - print('MacOS file limit: $fileLimit'); - - final String processLimit = await evalProcess('ulimit', ['-u']); - - print('MacOS process limit: $processLimit'); - } -} diff --git a/lib/web_ui/dev/pipeline.dart b/lib/web_ui/dev/pipeline.dart new file mode 100644 index 0000000000000..18399fe225af2 --- /dev/null +++ b/lib/web_ui/dev/pipeline.dart @@ -0,0 +1,266 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io' as io; + +import 'package:path/path.dart' as path; +import 'package:watcher/watcher.dart'; + +import 'utils.dart'; + +/// Describes what [Pipeline] is currently doing. +enum PipelineStatus { + /// The pipeline has not started yet. + /// + /// This is the initial state of the pipeline. + idle, + + /// The pipeline is running build steps. + started, + + /// The pipeline is stopping. + stopping, + + /// The pipeline is not running anything because it has been interrupted. + interrupted, + + /// The pipeline is not running anything because it encountered an error. + error, + + /// The pipeline is not running anything because it finished all build steps successfully. + done, +} + +/// A step in the build pipeline. +abstract class PipelineStep { + /// The name of this step. + /// + /// This value appears in logs, so it should be descriptive and human-readable. + String get description; + + /// Whether it is safe to interrupt this step while it's running. + bool get isSafeToInterrupt; + + /// Runs this step. + /// + /// The returned future is completed when the step is finished. The future + /// completes with an error if the step failed. + Future run(); + + /// Interrupts this step, if it's already running. + /// + /// [Pipeline] only calls this if [isSafeToInterrupt] returns true. + Future interrupt(); +} + +/// A helper class for implementing [PipelineStep] in terms of a process. +abstract class ProcessStep implements PipelineStep { + ProcessManager? _process; + bool _isInterrupted = false; + + /// Starts and returns the process that implements the logic of this pipeline + /// step. + Future createProcess(); + + @override + Future interrupt() async { + _isInterrupted = true; + _process?.kill(); + } + + @override + Future run() async { + final ProcessManager process = await createProcess(); + + if (_isInterrupted) { + // If the step was interrupted while creating the process, the + // `interrupt` won't kill the process; it must be done here. + process.kill(); + return; + } + + _process = process; + await process.wait(); + _process = null; + } +} + +/// Executes a sequence of asynchronous tasks, typically as part of a build/test +/// process. +/// +/// The pipeline can be executed by calling [start] and stopped by calling +/// [stop]. +/// +/// When a pipeline is stopped, it switches to the [PipelineStatus.stopping] +/// state. If [PipelineStep.isSafeToInterrupt] is true, interrupts the currently +/// running step and skips the rest. Otherwise, waits until the current task +/// finishes and skips the rest. +class Pipeline { + Pipeline({required this.steps}); + + final Iterable steps; + + PipelineStep? _currentStep; + Future? _currentStepFuture; + + PipelineStatus get status => _status; + PipelineStatus _status = PipelineStatus.idle; + + /// Runs the steps of the pipeline. + /// + /// Returns a future that resolves after all steps have been performed. + /// + /// The future resolves to an error as soon as any of the steps fails. + /// + /// The pipeline may be interrupted by calling [stop] before the future + /// resolves. + Future run() async { + _status = PipelineStatus.started; + try { + for (final PipelineStep step in steps) { + if (status != PipelineStatus.started) { + break; + } + _currentStep = step; + _currentStepFuture = step.run(); + await _currentStepFuture; + } + _status = PipelineStatus.done; + } catch (_) { + _status = PipelineStatus.error; + rethrow; + } finally { + _currentStep = null; + } + } + + /// Stops executing any more tasks in the pipeline. + /// + /// Tasks that are safe to interrupt (according to [PipelineStep.isSafeToInterrupt]), + /// are interrupted. Otherwise, waits for the current step to finish before + /// interrupting the pipeline. + Future stop() async { + _status = PipelineStatus.stopping; + final PipelineStep? step = _currentStep; + if (step == null) { + _status = PipelineStatus.interrupted; + return; + } + if (step.isSafeToInterrupt) { + print('Interrupting ${step.description}'); + await step.interrupt(); + _status = PipelineStatus.interrupted; + return; + } + print('${step.description} cannot be interrupted. Waiting for it to complete.'); + await _currentStepFuture; + _status = PipelineStatus.interrupted; + } +} + +/// Signature of functions to be called when a [WatchEvent] is received. +typedef WatchEventPredicate = bool Function(WatchEvent event); + +/// Responsible for watching a directory [dir] and executing the given +/// [pipeline] whenever a change occurs in the directory. +/// +/// The [ignore] callback can be used to customize the watching behavior to +/// ignore certain files. +class PipelineWatcher { + PipelineWatcher({ + required this.dir, + required this.pipeline, + this.ignore, + }) : watcher = DirectoryWatcher(dir); + + /// The path of the directory to watch for changes. + final String dir; + + /// The pipeline to be executed when an event is fired by the watcher. + final Pipeline pipeline; + + /// Used to watch a directory for any file system changes. + final DirectoryWatcher watcher; + + /// A callback that determines whether to rerun the pipeline or not for a + /// given [WatchEvent] instance. + final WatchEventPredicate? ignore; + + /// Activates the watcher. + Future start() async { + watcher.events.listen(_onEvent); + + // Listen to the `q` key stroke to stop the pipeline. + print('Press \'q\' to exit felt'); + + // Key strokes should be reported immediately and one at a time rather than + // wait for the user to hit ENTER and report the whole line. To achieve + // that, echo mode and line mode must be disabled. + io.stdin.echoMode = false; + io.stdin.lineMode = false; + + await io.stdin.firstWhere((List event) { + const int qKeyCode = 113; + final bool qEntered = event.isNotEmpty && event.first == qKeyCode; + return qEntered; + }); + print('Stopping felt'); + await pipeline.stop(); + } + + int _pipelineRunCount = 0; + Timer? _scheduledPipeline; + + void _onEvent(WatchEvent event) { + if (ignore?.call(event) == true) { + return; + } + + final String relativePath = path.relative(event.path, from: dir); + print('- [${event.type}] $relativePath'); + + _pipelineRunCount++; + _scheduledPipeline?.cancel(); + _scheduledPipeline = Timer(const Duration(milliseconds: 100), () { + _scheduledPipeline = null; + _runPipeline(); + }); + } + + Future _runPipeline() async { + if (pipeline.status == PipelineStatus.stopping) { + // We are already trying to stop the pipeline. No need to do anything. + return; + } + + if (pipeline.status == PipelineStatus.started) { + // If the pipeline already running, stop it before starting it again. + await pipeline.stop(); + } + + final int runCount = _pipelineRunCount; + try { + await pipeline.run(); + _pipelineSucceeded(runCount); + } catch(error, stackTrace) { + // The error is printed but not rethrown. This is because in watch mode + // failures are expected. The idea is that the developer corrects the + // error, saves the file, and the pipeline reruns. + _pipelineFailed(error, stackTrace); + } + } + + void _pipelineSucceeded(int pipelineRunCount) { + if (pipelineRunCount == _pipelineRunCount) { + print('*** Done! ***'); + print('Press \'q\' to exit felt'); + } + } + + void _pipelineFailed(Object error, StackTrace stackTrace) { + print('felt command failed: $error'); + print(stackTrace); + } +} diff --git a/lib/web_ui/dev/run.dart b/lib/web_ui/dev/run.dart new file mode 100644 index 0000000000000..f45770ec22cf6 --- /dev/null +++ b/lib/web_ui/dev/run.dart @@ -0,0 +1,94 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io' as io; + +import 'package:args/command_runner.dart'; + +import 'common.dart'; +import 'pipeline.dart'; +import 'steps/compile_tests_step.dart'; +import 'steps/run_tests_step.dart'; +import 'utils.dart'; + +/// Runs build and test steps. +/// +/// This command is designed to be invoked by the LUCI build graph. However, it +/// is also usable locally. +/// +/// Usage: +/// +/// felt run name_of_build_step +class RunCommand extends Command with ArgUtils { + RunCommand() { + argParser.addFlag( + 'list', + abbr: 'l', + defaultsTo: false, + help: 'Lists all available build steps.', + ); + } + + @override + String get name => 'run'; + + bool get isListSteps => boolArg('list'); + + @override + String get description => 'Runs a build step.'; + + /// Build steps to run, in order specified. + List get stepNames => argResults!.rest; + + @override + FutureOr run() async { + // All available build steps. + final Map buildSteps = { + 'compile_tests': CompileTestsStep(), + for (final String browserName in kAllBrowserNames) + 'run_tests_$browserName': RunTestsStep( + browserEnvironment: getBrowserEnvironment(browserName), + isDebug: false, + doUpdateScreenshotGoldens: false, + ), + }; + + if (isListSteps) { + buildSteps.keys.forEach(print); + return true; + } + + if (stepNames.isEmpty) { + throw UsageException('No build steps specified.', argParser.usage); + } + + final List unrecognizedStepNames = []; + for (final String stepName in stepNames) { + if (!buildSteps.containsKey(stepName)) { + unrecognizedStepNames.add(stepName); + } + } + if (unrecognizedStepNames.isNotEmpty) { + io.stderr.writeln( + 'Unknown build steps specified: ${unrecognizedStepNames.join(', ')}', + ); + return false; + } + + final List steps = []; + print('Running steps ${steps.join(', ')}'); + for (final String stepName in stepNames) { + steps.add(buildSteps[stepName]!); + } + + final Stopwatch stopwatch = Stopwatch()..start(); + final Pipeline pipeline = Pipeline(steps: steps); + await pipeline.run(); + stopwatch.stop(); + print('Finished running steps in ${stopwatch.elapsedMilliseconds / 1000} seconds.'); + + return true; + } +} diff --git a/lib/web_ui/dev/safari.dart b/lib/web_ui/dev/safari.dart deleted file mode 100644 index 3de4116a3862c..0000000000000 --- a/lib/web_ui/dev/safari.dart +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:async'; -import 'dart:io'; - -import 'browser.dart'; -import 'safari_installation.dart'; -import 'common.dart'; - -/// A class for running an instance of Safari. -/// -/// Most of the communication with the browser is expected to happen via HTTP, -/// so this exposes a bare-bones API. The browser starts as soon as the class is -/// constructed, and is killed when [close] is called. -/// -/// Any errors starting or running the process are reported through [onExit]. -class Safari extends Browser { - @override - final name = 'Safari'; - - static String version; - - static bool isMobileBrowser; - - /// Starts a new instance of Safari open to the given [url], which may be a - /// [Uri] or a [String]. - factory Safari(Uri url, {bool debug = false}) { - version = SafariArgParser.instance.version; - isMobileBrowser = SafariArgParser.instance.isMobileBrowser; - assert(version != null); - return Safari._(() async { - if (isMobileBrowser) { - // iOS-Safari - // Uses `xcrun simctl`. It is a command line utility to control the - // Simulator. For more details on interacting with the simulator: - // https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/iOS_Simulator_Guide/InteractingwiththeiOSSimulator/InteractingwiththeiOSSimulator.html - var process = await Process.start('xcrun', [ - 'simctl', - 'openurl', // Opens the url on Safari installed on the simulator. - 'booted', // The simulator is already booted. - '${url.toString()}' - ]); - - return process; - } else { - // Desktop-Safari - // TODO(nurhan): Configure info log for LUCI. - final BrowserInstallation installation = await getOrInstallSafari( - version, - infoLog: DevNull(), - ); - - // In the macOS Catalina opening Safari browser with a file brings - // a popup which halts the test. - // The following list of arguments needs to be provided to the `open` - // command to open Safari for a given URL. In summary, `open` tool opens - // a new Safari browser (even if one is already open), opens it with no - // persistent state and wait until it opens. - // The details copied from `man open` on macOS. - // TODO(nurhan): https://github.com/flutter/flutter/issues/50809 - var process = await Process.start(installation.executable, [ - // These are flags for `open` command line tool. - '-F', // Open a fresh Safari with no persistant state. - '-W', // Wait until the Safari opens. - '-n', // Open a new instance of the Safari even another one is open. - '-b', // Specifies the bundle identifier for the application to use. - 'com.apple.Safari', // Bundle identifier for Safari. - '${url.toString()}' - ]); - - return process; - } - }); - } - - Safari._(Future startBrowser()) : super(startBrowser); -} diff --git a/lib/web_ui/dev/safari_installation.dart b/lib/web_ui/dev/safari_installation.dart index b428d8c01286a..497ae06dc7f86 100644 --- a/lib/web_ui/dev/safari_installation.dart +++ b/lib/web_ui/dev/safari_installation.dart @@ -2,161 +2,63 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io' as io; -import 'package:args/args.dart'; import 'package:simulators/simulator_manager.dart'; -import 'package:yaml/yaml.dart'; +import 'browser_lock.dart'; import 'common.dart'; import 'utils.dart'; -class SafariArgParser extends BrowserArgParser { - static final SafariArgParser _singletonInstance = SafariArgParser._(); - - /// The [SafariArgParser] singleton. - static SafariArgParser get instance => _singletonInstance; - - String _version; - - SafariArgParser._(); - - @override - void populateOptions(ArgParser argParser) { - argParser - ..addOption( - 'safari-version', - defaultsTo: 'system', - help: 'The Safari version to use while running tests. The Safari ' - 'browser installed on the system is used as the only option now.' - 'Soon we will add support for using different versions using the ' - 'tech previews.', - ); - - // Populate options for Ios Safari. - IosSafariArgParser.instance.populateOptions(argParser); +/// Returns [IosSimulator] if the [Platform] is `macOS` and simulator +/// is started. +/// +/// Throws an [StateError] if these two conditions are not met. +IosSimulator get iosSimulator { + if (!io.Platform.isMacOS) { + throw StateError('iOS Simulator is only available on macOS machines.'); } - - @override - void parseOptions(ArgResults argResults) { - _version = argResults['safari-version'] as String; - assert(_version == 'system'); - final String browser = argResults['browser'] as String; - _isMobileBrowser = browser == 'ios-safari' ? true : false; + if (_iosSimulator == null) { + throw StateError( + 'iOS Simulator not started. Please first call initIOSSimulator method', + ); } - - @override - String get version => _version; - - bool _isMobileBrowser; - bool get isMobileBrowser => _isMobileBrowser; + return _iosSimulator!; } +IosSimulator? _iosSimulator; -class IosSafariArgParser extends BrowserArgParser { - static final IosSafariArgParser _singletonInstance = IosSafariArgParser._(); - - /// The [IosSafariArgParser] singleton. - static IosSafariArgParser get instance => _singletonInstance; - - String get version => 'iOS ${iosMajorVersion}.${iosMinorVersion}'; - - int _pinnedIosMajorVersion; - int _iosMajorVersion; - int get iosMajorVersion => _iosMajorVersion ?? _pinnedIosMajorVersion; - - int _pinnedIosMinorVersion; - int _iosMinorVersion; - int get iosMinorVersion => _iosMinorVersion ?? _pinnedIosMinorVersion; - - String _pinnedIosDevice; - String _iosDevice; - String get iosDevice => _iosDevice ?? _pinnedIosDevice; - - IosSafariArgParser._(); - - /// Returns [IosSimulator] if the [Platform] is `macOS` and simulator - /// is started. - /// - /// Throws an [StateError] if these two conditions are not met. - IosSimulator get iosSimulator => io.Platform.isMacOS - ? (_iosSimulator != null - ? _iosSimulator - : throw StateError('iosSimulator not started. Please first call ' - 'initIOSSimulator method')) - : throw StateError('iOS Simulator is only avaliable on macOS machines.'); - - IosSimulator _iosSimulator; - - /// Inializes and boots an [IosSimulator] using the [iosMajorVersion], - /// [iosMinorVersion] and [iosDevice] arguments. - Future initIosSimulator() async { - if (_iosSimulator != null) { - throw StateError('_iosSimulator can only be initialized once'); - } - final IosSimulatorManager iosSimulatorManager = IosSimulatorManager(); - try { - _iosSimulator = await iosSimulatorManager.getSimulator( - iosMajorVersion, - iosMinorVersion, - iosDevice, - ); - } catch (e) { - throw Exception('Error getting requested simulator. Try running ' - '`felt create` command first before running the tests. Exception: ' - '$e'); - } - - if (!_iosSimulator.booted) { - await _iosSimulator.boot(); - print('INFO: Simulator ${_iosSimulator.id} booted.'); - cleanupCallbacks.add(() async { - await _iosSimulator.shutdown(); - print('INFO: Simulator ${_iosSimulator.id} shutdown.'); - }); - } +/// Inializes and boots an [IosSimulator] using the [iosMajorVersion], +/// [iosMinorVersion] and [iosDevice] arguments. +Future initIosSimulator() async { + if (_iosSimulator != null) { + throw StateError('_iosSimulator can only be initialized once'); } - - @override - void populateOptions(ArgParser argParser) { - final YamlMap browserLock = BrowserLock.instance.configuration; - _pinnedIosMajorVersion = browserLock['ios-safari']['majorVersion'] as int; - _pinnedIosMinorVersion = browserLock['ios-safari']['minorVersion'] as int; - final pinnedIosVersion = - '${_pinnedIosMajorVersion}.${_pinnedIosMinorVersion}'; - _pinnedIosDevice = browserLock['ios-safari']['device'] as String; - argParser - ..addOption('version', - defaultsTo: '$pinnedIosVersion', - help: 'The version for the iOS operating system the iOS Simulator ' - 'will use for tests. For example for testing with iOS 13.2, ' - 'use `13.2`. Use command: ' - '`xcrun simctl list runtimes` to list available versions. Use ' - 'XCode to install more versions: Xcode > Preferences > Components' - 'If this value is not filled version locked in the ' - 'browser_lock.yaml file will be user.') - ..addOption('device', - defaultsTo: '$_pinnedIosDevice', - help: 'The device to be used for the iOS Simulator during the tests. ' - 'Use `.` instead of space for seperating the words. ' - 'Common examples: iPhone.8, iPhone.8.Plus, iPhone.11, ' - 'iPhone 11 Pro. Use command: ' - '`xcrun simctl list devices` for listing the available ' - 'devices. If this value is not filled device locked in the ' - 'browser_lock.yaml file will be user.'); + final IosSimulatorManager iosSimulatorManager = IosSimulatorManager(); + final IosSimulator simulator; + final SafariIosLock lock = browserLock.safariIosLock; + try { + simulator = await iosSimulatorManager.getSimulator( + lock.majorVersion, + lock.minorVersion, + lock.device, + ); + _iosSimulator = simulator; + } catch (e) { + io.stderr.writeln( + 'Error getting iOS Simulator for ${lock.simulatorDescription}.\n' + 'Try running `felt create` command before running tests.', + ); + rethrow; } - @override - void parseOptions(ArgResults argResults) { - final String iosVersion = argResults['version'] as String; - // The version will contain major and minor version seperated by a comma, - // for example: 13.1, 12.2 - assert(iosVersion.split('.').length == 2, - 'The version should be in format 13.5'); - _iosMajorVersion = int.parse(iosVersion.split('.')[0]); - _iosMinorVersion = int.parse(iosVersion.split('.')[1]); - _iosDevice = (argResults['device'] as String).replaceAll('.', ' '); + if (!simulator.booted) { + await simulator.boot(); + print('INFO: Simulator ${simulator.id} booted.'); + cleanupCallbacks.add(() async { + await simulator.shutdown(); + print('INFO: Simulator ${simulator.id} shutdown.'); + }); } } @@ -167,11 +69,8 @@ class IosSafariArgParser extends BrowserArgParser { /// Latest Safari version for Catalina, Mojave, High Siera is 13. /// /// Latest Safari version for Sierra is 12. -// TODO(nurhan): user latest version to download and install the latest -// technology preview. -Future getOrInstallSafari( - String requestedVersion, { - StringSink infoLog, +Future getOrInstallSafari({ + StringSink? infoLog, }) async { // These tests are aimed to run only on macOS machines local or on LUCI. if (!io.Platform.isMacOS) { @@ -181,16 +80,11 @@ Future getOrInstallSafari( infoLog ??= io.stdout; - if (requestedVersion == 'system') { - // Since Safari is included in macOS, always assume there will be one on the - // system. - infoLog.writeln('Using the system version that is already installed.'); - return BrowserInstallation( - version: 'system', - executable: PlatformBinding.instance.getMacApplicationLauncher(), - ); - } else { - infoLog.writeln('Unsupported version $requestedVersion.'); - throw UnimplementedError(); - } + // Since Safari is included in macOS, always assume there will be one on the + // system. + infoLog.writeln('Using the system version that is already installed.'); + return BrowserInstallation( + version: 'system', + executable: PlatformBinding.instance.getMacApplicationLauncher(), + ); } diff --git a/lib/web_ui/dev/safari_ios.dart b/lib/web_ui/dev/safari_ios.dart new file mode 100644 index 0000000000000..cd68ec0fc7a9a --- /dev/null +++ b/lib/web_ui/dev/safari_ios.dart @@ -0,0 +1,213 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io' as io; +import 'dart:math' as math; + +import 'package:image/image.dart'; +import 'package:path/path.dart' as path; +import 'package:test_api/src/backend/runtime.dart'; + +import 'browser.dart'; +import 'browser_lock.dart'; +import 'environment.dart'; +import 'safari_installation.dart'; +import 'utils.dart'; + +/// Provides an environment for the mobile variant of Safari running in an iOS +/// simulator. +class SafariIosEnvironment implements BrowserEnvironment { + @override + Browser launchBrowserInstance(Uri url, {bool debug = false}) { + return SafariIos(url); + } + + @override + Runtime get packageTestRuntime => Runtime.safari; + + @override + Future prepareEnvironment() async { + await initIosSimulator(); + } + + @override + ScreenshotManager? getScreenshotManager() { + return SafariIosScreenshotManager(); + } + + @override + String get packageTestConfigurationYamlFile => 'dart_test_safari.yaml'; +} + +/// Runs an instance of Safari for iOS (i.e. mobile Safari). +/// +/// Most of the communication with the browser is expected to happen via HTTP, +/// so this exposes a bare-bones API. The browser starts as soon as the class is +/// constructed, and is killed when [close] is called. +/// +/// Any errors starting or running the process are reported through [onExit]. +class SafariIos extends Browser { + @override + final String name = 'Safari iOS'; + + /// Starts a new instance of Safari open to the given [url], which may be a + /// [Uri]. + factory SafariIos(Uri url) { + return SafariIos._(() async { + // iOS-Safari + // Uses `xcrun simctl`. It is a command line utility to control the + // Simulator. For more details on interacting with the simulator: + // https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/iOS_Simulator_Guide/InteractingwiththeiOSSimulator/InteractingwiththeiOSSimulator.html + final io.Process process = await io.Process.start('xcrun', [ + 'simctl', + 'openurl', // Opens the url on Safari installed on the simulator. + 'booted', // The simulator is already booted. + url.toString(), + ]); + + return process; + }); + } + + SafariIos._(Future Function() startBrowser) : super(startBrowser); +} + +/// [ScreenshotManager] implementation for Safari. +/// +/// This manager will only be created/used for macOS. +class SafariIosScreenshotManager extends ScreenshotManager { + @override + String get filenameSuffix => '.iOS_Safari'; + + SafariIosScreenshotManager() { + final SafariIosLock lock = browserLock.safariIosLock; + _heightOfHeader = lock.heightOfHeader; + _heightOfFooter = lock.heightOfFooter; + _scaleFactor = lock.scaleFactor; + + /// Create the directory to use for taking screenshots, if it does not + /// exists. + if (!environment.webUiSimulatorScreenshotsDirectory.existsSync()) { + environment.webUiSimulatorScreenshotsDirectory.createSync(); + } + // Temporary directories are deleted in the clenaup phase of after `felt` + // runs the tests. + temporaryDirectories.add(environment.webUiSimulatorScreenshotsDirectory); + } + + /// This scale factor is used to enlarge/shrink the screenshot region + /// sent from the tests. + /// For more details see [_scaleScreenshotRegion(region)]. + late final double _scaleFactor; + + /// Height of the part to crop from the top of the image. + /// + /// `xcrun simctl` command takes the screenshot of the entire simulator. We + /// are cropping top bit from screenshot, otherwise due to the clock on top of + /// the screen, the screenshot will differ between each run. + /// Note that this gap can change per phone and per iOS version. For more + /// details refer to `browser_lock.yaml` file. + late final int _heightOfHeader; + + /// Height of the part to crop from the bottom of the image. + /// + /// This area is the footer navigation bar of the phone, it is not the area + /// used by tests (which is inside the browser). + late final int _heightOfFooter; + + /// Used as a suffix for the temporary file names used for screenshots. + int _fileNameCounter = 0; + + /// Capture a screenshot of entire simulator. + /// + /// Example screenshot with dimensions: W x H. + /// + /// <---------- W -------------> + /// _____________________________ + /// | Phone Top bar (clock etc.) | Ʌ + /// |_____________________________| | + /// | Broswer search bar | | + /// |_____________________________| | + /// | Web page content | | + /// | | | + /// | | + /// | | H + /// | | + /// | | | + /// | | | + /// | | | + /// | | | + /// |_____________________________| | + /// | Phone footer bar | | + /// |_____________________________| V + /// + /// After taking the screenshot, the image is cropped as heigh as + /// [_heightOfHeader] and [_heightOfFooter] from the top and bottom parts + /// consecutively. Hence web content has the dimensions: + /// + /// W x (H - [_heightOfHeader] - [_heightOfFooter]) + /// + /// [region] is used to decide which part of the web content will be used in + /// test image. It includes starting coordinate x,y as well as height and + /// width of the area to capture. + /// + /// Uses simulator tool `xcrun simctl`'s 'screenshot' command. + @override + Future capture(math.Rectangle? region) async { + final String filename = 'screenshot$_fileNameCounter.png'; + _fileNameCounter++; + + await iosSimulator.takeScreenshot( + filename, environment.webUiSimulatorScreenshotsDirectory, + ); + + final io.File file = io.File(path.join( + environment.webUiSimulatorScreenshotsDirectory.path, filename)); + List imageBytes; + if (!file.existsSync()) { + throw Exception('Failed to read the screenshot ' + 'screenshot$_fileNameCounter.png.'); + } + imageBytes = await file.readAsBytes(); + file.deleteSync(); + + final Image screenshot = decodePng(imageBytes)!; + // Create an image with no footer and header. The _heightOfHeader, + // _heightOfFooter values are already in real coordinates therefore + // they don't need to be scaled. + final Image content = copyCrop( + screenshot, + 0, + _heightOfHeader, + screenshot.width, + screenshot.height - _heightOfFooter - _heightOfHeader, + ); + + if (region == null) { + return content; + } else { + final math.Rectangle scaledRegion = _scaleScreenshotRegion(region); + return copyCrop( + content, + scaledRegion.left.toInt(), + scaledRegion.top.toInt(), + scaledRegion.width.toInt(), + scaledRegion.height.toInt(), + ); + } + } + + /// Perform a linear transform on the screenshot region to convert its + /// dimensions from linear coordinated to coordinated on the phone screen. + /// This uniform/isotropic scaling is done using [_scaleFactor]. + math.Rectangle _scaleScreenshotRegion(math.Rectangle region) { + return math.Rectangle( + region.left * _scaleFactor, + region.top * _scaleFactor, + region.width * _scaleFactor, + region.height * _scaleFactor, + ); + } +} diff --git a/lib/web_ui/dev/safari_macos.dart b/lib/web_ui/dev/safari_macos.dart new file mode 100644 index 0000000000000..5bfc70ef8c1bf --- /dev/null +++ b/lib/web_ui/dev/safari_macos.dart @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:convert' show json; +import 'dart:io'; + +import 'package:path/path.dart' as pathlib; +import 'package:test_api/src/backend/runtime.dart'; + +import 'browser.dart'; +import 'utils.dart'; + +/// Provides an environment for the desktop variant of Safari running on macOS. +class SafariMacOsEnvironment implements BrowserEnvironment { + @override + Browser launchBrowserInstance(Uri url, {bool debug = false}) { + return SafariMacOs(url); + } + + @override + Runtime get packageTestRuntime => Runtime.safari; + + @override + Future prepareEnvironment() async { + // Nothing extra to prepare for desktop Safari. + } + + // We do not yet support screenshots on desktop Safari. + @override + ScreenshotManager? getScreenshotManager() => null; + + @override + String get packageTestConfigurationYamlFile => 'dart_test_safari.yaml'; +} + +/// Runs an instance of Safari for macOS (i.e. desktop Safari). +/// +/// Most of the communication with the browser is expected to happen via HTTP, +/// so this exposes a bare-bones API. The browser starts as soon as the class is +/// constructed, and is killed when [close] is called. +/// +/// Any errors starting or running the process are reported through [onExit]. +class SafariMacOs extends Browser { + @override + final String name = 'Safari macOS'; + + /// Starts a new instance of Safari open to the given [url]. + factory SafariMacOs(Uri url) { + return SafariMacOs._(() async { + // This hack to programmatically launch a test in Safari is borrowed from + // Karma: https://github.com/karma-runner/karma-safari-launcher/issues/29 + // + // The issue is that opening an HTML file directly causes Safari to pop up + // a UI prompt to confirm the opening of a file. However, files under + // Library/Containers/com.apple.Safari/Data are exempt from this pop up. + // We create a "trampoline" file in this directory. The trampoline + // redirects the browser to the test URL in a + '''); + + final Process process = await Process.start( + '/Applications/Safari.app/Contents/MacOS/Safari', + [trampoline.path], + ); + + return process; + }); + } + + SafariMacOs._(Future Function() startBrowser) : super(startBrowser); +} diff --git a/lib/web_ui/dev/screenshot_manager.dart b/lib/web_ui/dev/screenshot_manager.dart deleted file mode 100644 index 3cceaa267d4fc..0000000000000 --- a/lib/web_ui/dev/screenshot_manager.dart +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:io' as io; -import 'dart:convert'; -import 'dart:math'; - -import 'package:image/image.dart'; -import 'package:path/path.dart' as path; -import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' - as wip; -import 'package:yaml/yaml.dart'; - -import 'common.dart'; -import 'environment.dart'; -import 'safari_installation.dart'; -import 'utils.dart'; - -/// [ScreenshotManager] implementation for Chrome. -/// -/// This manager can be used for both macOS and Linux. -// TODO: https://github.com/flutter/flutter/issues/65673 -class ChromeScreenshotManager extends ScreenshotManager { - String get filenameSuffix => ''; - - /// Capture a screenshot of the web content. - /// - /// Uses Webkit Inspection Protocol server's `captureScreenshot` API. - /// - /// [region] is used to decide which part of the web content will be used in - /// test image. It includes starting coordinate x,y as well as height and - /// width of the area to capture. - Future capture(Rectangle region) async { - final wip.ChromeConnection chromeConnection = - wip.ChromeConnection('localhost', kDevtoolsPort); - final wip.ChromeTab chromeTab = await chromeConnection.getTab( - (wip.ChromeTab chromeTab) => chromeTab.url.contains('localhost')); - final wip.WipConnection wipConnection = await chromeTab.connect(); - - Map captureScreenshotParameters = null; - if (region != null) { - captureScreenshotParameters = { - 'format': 'png', - 'clip': { - 'x': region.left, - 'y': region.top, - 'width': region.width, - 'height': region.height, - 'scale': - // This is NOT the DPI of the page, instead it's the "zoom level". - 1, - }, - }; - } - - // Setting hardware-independent screen parameters: - // https://chromedevtools.github.io/devtools-protocol/tot/Emulation - await wipConnection - .sendCommand('Emulation.setDeviceMetricsOverride', { - 'width': kMaxScreenshotWidth, - 'height': kMaxScreenshotHeight, - 'deviceScaleFactor': 1, - 'mobile': false, - }); - final wip.WipResponse response = await wipConnection.sendCommand( - 'Page.captureScreenshot', captureScreenshotParameters); - - final Image screenshot = - decodePng(base64.decode(response.result['data'] as String)); - - return screenshot; - } -} - -/// [ScreenshotManager] implementation for Safari. -/// -/// This manager will only be created/used for macOS. -class IosSafariScreenshotManager extends ScreenshotManager { - String get filenameSuffix => '.iOS_Safari'; - - IosSafariScreenshotManager() { - final YamlMap browserLock = BrowserLock.instance.configuration; - _heightOfHeader = browserLock['ios-safari']['heightOfHeader'] as int; - _heightOfFooter = browserLock['ios-safari']['heightOfFooter'] as int; - _scaleFactor = browserLock['ios-safari']['scaleFactor'] as double; - - /// Create the directory to use for taking screenshots, if it does not - /// exists. - if (!environment.webUiSimulatorScreenshotsDirectory.existsSync()) { - environment.webUiSimulatorScreenshotsDirectory.createSync(); - } - // Temporary directories are deleted in the clenaup phase of after `felt` - // runs the tests. - temporaryDirectories.add(environment.webUiSimulatorScreenshotsDirectory); - } - - /// This scale factor is used to enlarge/shrink the screenshot region - /// sent from the tests. - /// For more details see [_scaleScreenshotRegion(region)]. - double _scaleFactor; - - /// Height of the part to crop from the top of the image. - /// - /// `xcrun simctl` command takes the screenshot of the entire simulator. We - /// are cropping top bit from screenshot, otherwise due to the clock on top of - /// the screen, the screenshot will differ between each run. - /// Note that this gap can change per phone and per iOS version. For more - /// details refer to `browser_lock.yaml` file. - int _heightOfHeader; - - /// Height of the part to crop from the bottom of the image. - /// - /// This area is the footer navigation bar of the phone, it is not the area - /// used by tests (which is inside the browser). - int _heightOfFooter; - - /// Used as a suffix for the temporary file names used for screenshots. - int _fileNameCounter = 0; - - /// Capture a screenshot of entire simulator. - /// - /// Example screenshot with dimensions: W x H. - /// - /// <---------- W -------------> - /// _____________________________ - /// | Phone Top bar (clock etc.) | Ʌ - /// |_____________________________| | - /// | Broswer search bar | | - /// |_____________________________| | - /// | Web page content | | - /// | | | - /// | | - /// | | H - /// | | - /// | | | - /// | | | - /// | | | - /// | | | - /// |_____________________________| | - /// | Phone footer bar | | - /// |_____________________________| V - /// - /// After taking the screenshot, the image is cropped as heigh as - /// [_heightOfHeader] and [_heightOfFooter] from the top and bottom parts - /// consecutively. Hence web content has the dimensions: - /// - /// W x (H - [_heightOfHeader] - [_heightOfFooter]) - /// - /// [region] is used to decide which part of the web content will be used in - /// test image. It includes starting coordinate x,y as well as height and - /// width of the area to capture. - /// - /// Uses simulator tool `xcrun simctl`'s 'screenshot' command. - Future capture(Rectangle region) async { - final String filename = 'screenshot${_fileNameCounter}.png'; - _fileNameCounter++; - - await IosSafariArgParser.instance.iosSimulator.takeScreenshot( - filename, environment.webUiSimulatorScreenshotsDirectory); - - final io.File file = io.File(path.join( - environment.webUiSimulatorScreenshotsDirectory.path, filename)); - List imageBytes; - if (!file.existsSync()) { - throw Exception('Failed to read the screenshot ' - 'screenshot${_fileNameCounter}.png.'); - } - imageBytes = await file.readAsBytes(); - file.deleteSync(); - - final Image screenshot = decodePng(imageBytes); - // Create an image with no footer and header. The _heightOfHeader, - // _heightOfFooter values are already in real coordinates therefore - // they don't need to be scaled. - final Image content = copyCrop( - screenshot, - 0, - _heightOfHeader, - screenshot.width, - screenshot.height - _heightOfFooter - _heightOfHeader, - ); - - if (region == null) { - return content; - } else { - final Rectangle scaledRegion = _scaleScreenshotRegion(region); - return copyCrop( - content, - scaledRegion.left.toInt(), - scaledRegion.top.toInt(), - scaledRegion.width.toInt(), - scaledRegion.height.toInt(), - ); - } - } - - /// Perform a linear transform on the screenshot region to convert its - /// dimensions from linear coordinated to coordinated on the phone screen. - /// This uniform/isotropic scaling is done using [_scaleFactor]. - Rectangle _scaleScreenshotRegion(Rectangle region) { - return Rectangle( - region.left * _scaleFactor, - region.top * _scaleFactor, - region.width * _scaleFactor, - region.height * _scaleFactor, - ); - } -} - -const String _kBrowserChrome = 'chrome'; -const String _kBrowserIOSSafari = 'ios-safari'; - -typedef ScreenshotManagerFactory = ScreenshotManager Function(); - -/// Abstract class for taking screenshots in one of the browsers. -abstract class ScreenshotManager { - static final Map _browserFactories = - { - _kBrowserChrome: () => ChromeScreenshotManager(), - _kBrowserIOSSafari: () => IosSafariScreenshotManager(), - }; - - static bool isBrowserSupported(String browser) => - _browserFactories.containsKey(browser); - - static ScreenshotManager choose(String browser) { - if (isBrowserSupported(browser)) { - return _browserFactories[browser](); - } - throw StateError('Screenshot tests are only supported on Chrome and on ' - 'iOS Safari'); - } - - /// Capture a screenshot. - /// - /// Please read the details for the implementing classes. - Future capture(Rectangle region); - - /// Suffix to be added to the end of the filename. - /// - /// Example file names: - /// - Chrome, no-suffix: backdrop_filter_clip_moved.actual.png - /// - iOS Safari: backdrop_filter_clip_moved.actual.iOS_Safari.png - String get filenameSuffix; -} diff --git a/lib/web_ui/dev/steps/compile_tests_step.dart b/lib/web_ui/dev/steps/compile_tests_step.dart new file mode 100644 index 0000000000000..dcd688039d325 --- /dev/null +++ b/lib/web_ui/dev/steps/compile_tests_step.dart @@ -0,0 +1,226 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:path/path.dart' as pathlib; +import 'package:pool/pool.dart'; +import 'package:web_test_utils/goldens.dart'; + +import '../environment.dart'; +import '../exceptions.dart'; +import '../pipeline.dart'; +import '../utils.dart'; + +/// Compiles web tests and their dependencies. +/// +/// Includes: +/// * compile the test code itself +/// * compile the page that hosts the tests +/// * fetch the golden repo for screenshot comparison +class CompileTestsStep implements PipelineStep { + CompileTestsStep({ + this.skipGoldensRepoFetch = false, + this.testFiles, + }); + + final bool skipGoldensRepoFetch; + final List? testFiles; + + @override + String get description => 'compile_tests'; + + @override + bool get isSafeToInterrupt => true; + + @override + Future interrupt() async { + await cleanup(); + } + + @override + Future run() async { + if (!skipGoldensRepoFetch) { + await fetchGoldensRepo(); + } + await buildHostPage(); + await compileTests(testFiles ?? findAllTests()); + } +} + +/// Compiles the specified unit tests. +Future compileTests(List testFiles) async { + final Stopwatch stopwatch = Stopwatch()..start(); + + // Separate HTML targets from CanvasKit targets because the two use + // different dart2js options. + final List htmlTargets = []; + final List canvasKitTargets = []; + final String canvasKitTestDirectory = + pathlib.join(environment.webUiTestDir.path, 'canvaskit'); + for (final FilePath testFile in testFiles) { + if (pathlib.isWithin(canvasKitTestDirectory, testFile.absolute)) { + canvasKitTargets.add(testFile); + } else { + htmlTargets.add(testFile); + } + } + + await Future.wait(>[ + if (htmlTargets.isNotEmpty) + _compileTestsInParallel(targets: htmlTargets, forCanvasKit: false), + if (canvasKitTargets.isNotEmpty) + _compileTestsInParallel(targets: canvasKitTargets, forCanvasKit: true), + ]); + + stopwatch.stop(); + + final int targetCount = htmlTargets.length + canvasKitTargets.length; + print( + 'Built $targetCount tests in ${stopwatch.elapsedMilliseconds ~/ 1000} ' + 'seconds using $_dart2jsConcurrency concurrent dart2js processes.', + ); +} + +// Maximum number of concurrent dart2js processes to use. +const int _dart2jsConcurrency = int.fromEnvironment('FELT_DART2JS_CONCURRENCY', defaultValue: 8); + +final Pool _dart2jsPool = Pool(_dart2jsConcurrency); + +/// Spawns multiple dart2js processes to compile [targets] in parallel. +Future _compileTestsInParallel({ + required List targets, + required bool forCanvasKit, +}) async { + final Stream results = _dart2jsPool.forEach( + targets, + (FilePath file) => compileUnitTest(file, forCanvasKit: forCanvasKit), + ); + await for (final bool isSuccess in results) { + if (!isSuccess) { + throw ToolExit('Failed to compile tests.'); + } + } +} + +/// Compiles one unit test using `dart2js`. +/// +/// When building for CanvasKit we have to use extra argument +/// `DFLUTTER_WEB_USE_SKIA=true`. +/// +/// Dart2js creates the following outputs: +/// - target.browser_test.dart.js +/// - target.browser_test.dart.js.deps +/// - target.browser_test.dart.js.maps +/// under the same directory with test file. If all these files are not in +/// the same directory, Chrome dev tools cannot load the source code during +/// debug. +/// +/// All the files under test already copied from /test directory to /build +/// directory before test are build. See [_copyFilesFromTestToBuild]. +/// +/// Later the extra files will be deleted in [_cleanupExtraFilesUnderTestDir]. +Future compileUnitTest(FilePath input, { required bool forCanvasKit }) async { + final String targetFileName = pathlib.join( + environment.webUiBuildDir.path, + '${input.relativeToWebUi}.browser_test.dart.js', + ); + + final io.Directory directoryToTarget = io.Directory(pathlib.join( + environment.webUiBuildDir.path, + pathlib.dirname(input.relativeToWebUi))); + + if (!directoryToTarget.existsSync()) { + directoryToTarget.createSync(recursive: true); + } + + final List arguments = [ + '--no-minify', + '--disable-inlining', + '--enable-asserts', + '--enable-experiment=non-nullable', + '--no-sound-null-safety', + + // We do not want to auto-select a renderer in tests. As of today, tests + // are designed to run in one specific mode. So instead, we specify the + // renderer explicitly. + '-DFLUTTER_WEB_AUTO_DETECT=false', + '-DFLUTTER_WEB_USE_SKIA=$forCanvasKit', + + '-O2', + '-o', + targetFileName, // target path. + input.relativeToWebUi, // current path. + ]; + + final int exitCode = await runProcess( + environment.dart2jsExecutable, + arguments, + workingDirectory: environment.webUiRootDir.path, + ); + + if (exitCode != 0) { + io.stderr.writeln('ERROR: Failed to compile test $input. ' + 'Dart2js exited with exit code $exitCode'); + return false; + } else { + return true; + } +} + +Future buildHostPage() async { + final String hostDartPath = pathlib.join('lib', 'static', 'host.dart'); + final io.File hostDartFile = io.File(pathlib.join( + environment.webEngineTesterRootDir.path, + hostDartPath, + )); + final io.File timestampFile = io.File(pathlib.join( + environment.webEngineTesterRootDir.path, + '$hostDartPath.js.timestamp', + )); + + final String timestamp = + hostDartFile.statSync().modified.millisecondsSinceEpoch.toString(); + if (timestampFile.existsSync()) { + final String lastBuildTimestamp = timestampFile.readAsStringSync(); + if (lastBuildTimestamp == timestamp) { + // The file is still fresh. No need to rebuild. + return; + } else { + // Record new timestamp, but don't return. We need to rebuild. + print('${hostDartFile.path} timestamp changed. Rebuilding.'); + } + } else { + print('Building ${hostDartFile.path}.'); + } + + final int exitCode = await runProcess( + environment.dart2jsExecutable, + [ + hostDartPath, + '-o', + '$hostDartPath.js', + ], + workingDirectory: environment.webEngineTesterRootDir.path, + ); + + if (exitCode != 0) { + throw ToolExit( + 'Failed to compile ${hostDartFile.path}. Compiler ' + 'exited with exit code $exitCode', + exitCode: exitCode, + ); + } + + // Record the timestamp to avoid rebuilding unless the file changes. + timestampFile.writeAsStringSync(timestamp); +} + +Future fetchGoldensRepo() async { + print('INFO: Fetching goldens repo'); + final GoldensRepoFetcher goldensRepoFetcher = GoldensRepoFetcher( + environment.webUiGoldensRepositoryDirectory, + pathlib.join(environment.webUiDevDir.path, 'goldens_lock.yaml')); + await goldensRepoFetcher.fetch(); +} diff --git a/lib/web_ui/dev/steps/run_tests_step.dart b/lib/web_ui/dev/steps/run_tests_step.dart new file mode 100644 index 0000000000000..99a9cf7256189 --- /dev/null +++ b/lib/web_ui/dev/steps/run_tests_step.dart @@ -0,0 +1,291 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:path/path.dart' as pathlib; +// TODO(yjbanov): remove hacks when this is fixed: +// https://github.com/dart-lang/test/issues/1521 +import 'package:test_api/src/backend/group.dart' as hack; +import 'package:test_api/src/backend/live_test.dart' as hack; +import 'package:test_api/src/backend/runtime.dart' as hack; +import 'package:test_core/src/executable.dart' as test; +import 'package:test_core/src/runner/configuration/reporters.dart' as hack; +import 'package:test_core/src/runner/engine.dart' as hack; +import 'package:test_core/src/runner/hack_register_platform.dart' as hack; +import 'package:test_core/src/runner/reporter.dart' as hack; + +import '../browser.dart'; +import '../environment.dart'; +import '../exceptions.dart'; +import '../pipeline.dart'; +import '../test_platform.dart'; +import '../utils.dart'; + +// Maximum number of tests that run concurrently. +const int _testConcurrency = int.fromEnvironment('FELT_TEST_CONCURRENCY', defaultValue: 10); + +/// Runs web tests. +/// +/// Assumes the artifacts from [CompileTestsStep] are available, either from +/// running it prior to this step locally, or by having the build graph copy +/// them from another bot. +class RunTestsStep implements PipelineStep { + RunTestsStep({ + required this.browserEnvironment, + required this.isDebug, + required this.doUpdateScreenshotGoldens, + this.testFiles, + }); + + final BrowserEnvironment browserEnvironment; + final List? testFiles; + final bool isDebug; + final bool doUpdateScreenshotGoldens; + + /// Global list of shards that failed. + /// + /// This is used to make sure that when there's a test failure anywhere we + /// exit with a non-zero exit code. + /// + /// Shards must never be removed from this list, only added. + List failedShards = []; + + /// Whether all test shards succeeded. + bool get allShardsPassed => failedShards.isEmpty; + + @override + String get description => 'run_tests'; + + @override + bool get isSafeToInterrupt => true; + + @override + Future interrupt() async {} + + @override + Future run() async { + _copyTestFontsIntoWebUi(); + await _prepareTestResultsDirectory(); + await browserEnvironment.prepareEnvironment(); + + final List testFiles = this.testFiles ?? findAllTests(); + + // Separate screenshot tests from unit-tests. Screenshot tests must run + // one at a time. Otherwise, they will end up screenshotting each other. + // This is not an issue for unit-tests. + final FilePath failureSmokeTestPath = FilePath.fromWebUi( + 'test/golden_tests/golden_failure_smoke_test.dart', + ); + final List screenshotTestFiles = []; + final List unitTestFiles = []; + + for (final FilePath testFilePath in testFiles) { + if (!testFilePath.absolute.endsWith('_test.dart')) { + // Not a test file at all. Skip. + continue; + } + if (testFilePath == failureSmokeTestPath) { + // A smoke test that fails on purpose. Skip. + continue; + } + + // All files under test/golden_tests are considered golden tests. + final bool isUnderGoldenTestsDirectory = + pathlib.split(testFilePath.relativeToWebUi).contains('golden_tests'); + // Any file whose name ends with "_golden_test.dart" is run as a golden test. + final bool isGoldenTestFile = pathlib + .basename(testFilePath.relativeToWebUi) + .endsWith('_golden_test.dart'); + if (isUnderGoldenTestsDirectory || isGoldenTestFile) { + screenshotTestFiles.add(testFilePath); + } else { + unitTestFiles.add(testFilePath); + } + } + + // This test returns a non-zero exit code on purpose. Run it separately. + if (testFiles.contains(failureSmokeTestPath)) { + await _runTestBatch( + testFiles: [failureSmokeTestPath], + browserEnvironment: browserEnvironment, + concurrency: 1, + expectFailure: true, + isDebug: isDebug, + doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, + ); + } + + // Run all unit-tests as a single batch and with high concurrency. + if (unitTestFiles.isNotEmpty) { + await _runTestBatch( + testFiles: unitTestFiles, + browserEnvironment: browserEnvironment, + concurrency: _testConcurrency, + expectFailure: false, + isDebug: isDebug, + doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, + ); + _checkExitCode('Unit tests'); + } + + // Run screenshot tests one at a time. Otherwise, tests end up + // screenshotting each other. + for (final FilePath screenshotTestFilePath in screenshotTestFiles) { + await _runTestBatch( + testFiles: [screenshotTestFilePath], + browserEnvironment: browserEnvironment, + concurrency: 1, + expectFailure: false, + isDebug: isDebug, + doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, + ); + _checkExitCode('Golden tests'); + } + + if (!allShardsPassed) { + throw ToolExit(_createFailedShardsMessage()); + } + } + + void _checkExitCode(String shard) { + if (io.exitCode != 0) { + failedShards.add(shard); + } + } + + String _createFailedShardsMessage() { + final StringBuffer message = StringBuffer( + 'The following test shards failed:\n', + ); + for (final String failedShard in failedShards) { + message.writeln(' - $failedShard'); + } + return message.toString(); + } +} + +Future _prepareTestResultsDirectory() async { + if (environment.webUiTestResultsDirectory.existsSync()) { + environment.webUiTestResultsDirectory.deleteSync(recursive: true); + } + environment.webUiTestResultsDirectory.createSync(recursive: true); +} + +const List _kTestFonts = [ + 'ahem.ttf', + 'Roboto-Regular.ttf', + 'NotoNaskhArabic-Regular.ttf', + 'NotoColorEmoji.ttf', +]; + +void _copyTestFontsIntoWebUi() { + final String fontsPath = pathlib.join( + environment.flutterDirectory.path, + 'third_party', + 'txt', + 'third_party', + 'fonts', + ); + + for (final String fontFile in _kTestFonts) { + final io.File sourceTtf = io.File(pathlib.join(fontsPath, fontFile)); + final String destinationTtfPath = + pathlib.join(environment.webUiRootDir.path, 'lib', 'assets', fontFile); + sourceTtf.copySync(destinationTtfPath); + } +} + +/// Runs a batch of tests. +/// +/// Unless [expectFailure] is set to false, sets [io.exitCode] to a non-zero +/// value if any tests fail. +Future _runTestBatch({ + required List testFiles, + required bool isDebug, + required BrowserEnvironment browserEnvironment, + required bool doUpdateScreenshotGoldens, + required int concurrency, + required bool expectFailure, +}) async { + final String configurationFilePath = pathlib.join( + environment.webUiRootDir.path, + browserEnvironment.packageTestConfigurationYamlFile, + ); + final List testArgs = [ + ...['-r', 'compact'], + '--concurrency=$concurrency', + if (isDebug) '--pause-after-load', + // Don't pollute logs with output from tests that are expected to fail. + if (expectFailure) + '--reporter=name-only', + '--platform=${browserEnvironment.packageTestRuntime.identifier}', + '--precompiled=${environment.webUiBuildDir.path}', + '--configuration=$configurationFilePath', + '--', + ...testFiles.map((FilePath f) => f.relativeToWebUi).toList(), + ]; + + if (expectFailure) { + hack.registerReporter( + 'name-only', + hack.ReporterDetails( + 'Prints the name of the test, but suppresses all other test output.', + (_, hack.Engine engine, __) => NameOnlyReporter(engine)), + ); + } + + hack.registerPlatformPlugin([ + browserEnvironment.packageTestRuntime, + ], () { + return BrowserPlatform.start( + browserEnvironment: browserEnvironment, + // It doesn't make sense to update a screenshot for a test that is + // expected to fail. + doUpdateScreenshotGoldens: !expectFailure && doUpdateScreenshotGoldens, + ); + }); + + // We want to run tests with `web_ui` as a working directory. + final dynamic originalCwd = io.Directory.current; + io.Directory.current = environment.webUiRootDir.path; + try { + await test.main(testArgs); + } finally { + io.Directory.current = originalCwd; + } + + if (expectFailure) { + if (io.exitCode != 0) { + // It failed, as expected. + print('Test successfully failed, as expected.'); + io.exitCode = 0; + } else { + io.stderr.writeln( + 'Tests ${testFiles.join(', ')} did not fail. Expected failure.', + ); + io.exitCode = 1; + } + } +} + +/// Prints the name of the test, but suppresses all other test output. +/// +/// This is useful to prevent pollution of logs by tests that are expected to +/// fail. +class NameOnlyReporter implements hack.Reporter { + NameOnlyReporter(hack.Engine testEngine) { + testEngine.onTestStarted.listen(_printTestName); + } + + void _printTestName(hack.LiveTest test) { + print('Running ${test.groups.map((hack.Group group) => group.name).join(' ')} ${test.individualName}'); + } + + @override + void pause() {} + + @override + void resume() {} +} diff --git a/lib/web_ui/dev/supported_browsers.dart b/lib/web_ui/dev/supported_browsers.dart deleted file mode 100644 index 5c475d5c5c8dd..0000000000000 --- a/lib/web_ui/dev/supported_browsers.dart +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'package:test_api/src/backend/runtime.dart'; - -import 'browser.dart'; -import 'chrome.dart'; -import 'chrome_installer.dart'; -import 'common.dart'; -import 'edge.dart'; -import 'environment.dart'; -import 'edge_installation.dart'; -import 'firefox.dart'; -import 'firefox_installer.dart'; -import 'safari.dart'; -import 'safari_installation.dart'; - -/// Utilities for browsers, that tests are supported. -/// -/// Extending [Browser] is not enough for supporting test. -/// -/// Each new browser should be added to the [Runtime] map, to the [getBrowser] -/// method. -/// -/// One should also implement [BrowserArgParser] and add it to the [argParsers]. -class SupportedBrowsers { - final List argParsers = List.of([ - ChromeArgParser.instance, - EdgeArgParser.instance, - FirefoxArgParser.instance, - SafariArgParser.instance - ]); - - final List supportedBrowserNames = [ - 'chrome', - 'edge', - 'firefox', - 'safari' - ]; - - final Map supportedBrowsersToRuntimes = { - 'chrome': Runtime.chrome, - 'edge': Runtime.internetExplorer, - 'firefox': Runtime.firefox, - 'safari': Runtime.safari, - 'ios-safari': Runtime.safari, - }; - - final Map supportedBrowserToPlatform = { - 'chrome': 'chrome', - 'edge': 'ie', - 'firefox': 'firefox', - 'safari': 'safari', - 'ios-safari': 'safari', - }; - - final Map browserToConfiguration = { - 'chrome': - '--configuration=${environment.webUiRootDir.path}/dart_test_chrome.yaml', - 'edge': - '--configuration=${environment.webUiRootDir.path}/dart_test_edge.yaml', - 'firefox': - '--configuration=${environment.webUiRootDir.path}/dart_test_firefox.yaml', - 'safari': - '--configuration=${environment.webUiRootDir.path}/dart_test_safari.yaml', - 'ios-safari': - '--configuration=${environment.webUiRootDir.path}/dart_test_safari.yaml', - }; - - static final SupportedBrowsers _singletonInstance = SupportedBrowsers._(); - - /// The [SupportedBrowsers] singleton. - static SupportedBrowsers get instance => _singletonInstance; - - SupportedBrowsers._(); - - Browser getBrowser(Runtime runtime, Uri url, {bool debug = false}) { - if (runtime == Runtime.chrome) { - return Chrome(url, debug: debug); - } else if (runtime == Runtime.internetExplorer) { - return Edge(url, debug: debug); - } else if (runtime == Runtime.firefox) { - return Firefox(url, debug: debug); - } else if (runtime == Runtime.safari) { - return Safari(url, debug: debug); - } else { - throw new UnsupportedError('The browser type not supported in tests'); - } - } -} diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 18dd92cf1c45e..b7e1c11fe6189 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -17,69 +16,67 @@ import 'package:path/path.dart' as p; import 'package:pool/pool.dart'; import 'package:shelf/shelf.dart' as shelf; import 'package:shelf/shelf_io.dart' as shelf_io; +import 'package:shelf_packages_handler/shelf_packages_handler.dart'; import 'package:shelf_static/shelf_static.dart'; import 'package:shelf_web_socket/shelf_web_socket.dart'; -import 'package:shelf_packages_handler/shelf_packages_handler.dart'; import 'package:stream_channel/stream_channel.dart'; -import 'package:web_socket_channel/web_socket_channel.dart'; -import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports -import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports -import 'package:test_core/src/runner/runner_suite.dart'; // ignore: implementation_imports -import 'package:test_core/src/runner/platform.dart'; // ignore: implementation_imports -import 'package:test_core/src/util/stack_trace_mapper.dart'; // ignore: implementation_imports -import 'package:test_api/src/utils.dart'; // ignore: implementation_imports -import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports -import 'package:test_core/src/runner/plugin/platform_helpers.dart'; // ignore: implementation_imports -import 'package:test_core/src/runner/environment.dart'; // ignore: implementation_imports +import 'package:test_api/src/backend/runtime.dart'; +import 'package:test_api/src/backend/suite_platform.dart'; +import 'package:test_core/src/runner/configuration.dart'; +import 'package:test_core/src/runner/environment.dart'; +import 'package:test_core/src/runner/platform.dart'; +import 'package:test_core/src/runner/plugin/platform_helpers.dart'; +import 'package:test_core/src/runner/runner_suite.dart'; +import 'package:test_core/src/runner/suite.dart'; +import 'package:test_core/src/util/io.dart'; +import 'package:test_core/src/util/stack_trace_mapper.dart'; -import 'package:test_core/src/util/io.dart'; // ignore: implementation_imports -import 'package:test_core/src/runner/configuration.dart'; // ignore: implementation_imports +import 'package:web_socket_channel/web_socket_channel.dart'; +import 'package:web_test_utils/goldens.dart'; +import 'package:web_test_utils/image_compare.dart'; import 'browser.dart'; import 'common.dart'; import 'environment.dart' as env; -import 'goldens.dart'; -import 'screenshot_manager.dart'; -import 'supported_browsers.dart'; +/// Custom test platform that serves web engine unit tests. class BrowserPlatform extends PlatformPlugin { /// Starts the server. /// - /// [root] is the root directory that the server should serve. It defaults to - /// the working directory. - static Future start(String name, - {String root, bool doUpdateScreenshotGoldens: false}) async { - assert(SupportedBrowsers.instance.supportedBrowserNames.contains(name)); - var server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); + /// [browserEnvironment] provides the browser environment to run the test. + /// + /// If [doUpdateScreenshotGoldens] is true updates screenshot golden files + /// instead of failing the test on screenshot mismatches. + static Future start({ + required BrowserEnvironment browserEnvironment, + required bool doUpdateScreenshotGoldens, + }) async { + final shelf_io.IOServer server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); return BrowserPlatform._( - name, - server, - Configuration.current, - p.fromUri(await Isolate.resolvePackageUri( + browserEnvironment: browserEnvironment, + server: server, + isDebug: Configuration.current.pauseAfterLoad, + faviconPath: p.fromUri(await Isolate.resolvePackageUri( Uri.parse('package:test/src/runner/browser/static/favicon.ico'))), - root: root, doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, + packageConfig: await loadPackageConfigUri((await Isolate.packageConfig)!), ); } - /// The test runner configuration. - final Configuration _config; + /// If true, runs the browser with a visible windows (i.e. not headless) and + /// pauses before running the tests to give the developer a chance to set + /// breakpoints in the code. + final bool isDebug; /// The underlying server. - final shelf.Server _server; - - /// Name for the running browser. Not final on purpose can be mutated later. - String browserName; + final shelf.Server server; - /// A randomly-generated secret. - /// - /// This is used to ensure that other users on the same system can't snoop - /// on data being served through this server. - final _secret = Uri.encodeComponent(randomBase64(24)); + /// Provides the environment for the browser running tests. + final BrowserEnvironment browserEnvironment; /// The URL for this server. - Uri get url => _server.url.resolve(_secret + '/'); + Uri get url => server.url.resolve('/'); /// A [OneOffHandler] for servicing WebSocket connections for /// [BrowserManager]s. @@ -88,19 +85,10 @@ class BrowserPlatform extends PlatformPlugin { /// WebSocket, final OneOffHandler _webSocketHandler = OneOffHandler(); - /// A [PathHandler] used to serve compiled JS. - final PathHandler _jsHandler = PathHandler(); - - /// The root directory served statically by this server. - final String _root; - - /// The HTTP client to use when caching JS files in `pub serve`. - final HttpClient _http; - /// Handles taking screenshots during tests. /// /// Implementation will differ depending on the browser. - ScreenshotManager _screenshotManager; + final ScreenshotManager? _screenshotManager; /// Whether [close] has been called. bool get _closed => _closeMemo.hasRun; @@ -108,50 +96,139 @@ class BrowserPlatform extends PlatformPlugin { /// Whether to update screenshot golden files. final bool doUpdateScreenshotGoldens; - BrowserPlatform._( - String name, this._server, Configuration config, String faviconPath, - {String root, this.doUpdateScreenshotGoldens}) - : this.browserName = name, - _config = config, - _root = root == null ? p.current : root, - _http = config.pubServeUrl == null ? null : HttpClient() { - var cascade = shelf.Cascade().add(_webSocketHandler.handler); - - if (_config.pubServeUrl == null) { - // We server static files from here (JS, HTML, etc) - final String staticFilePath = - config.suiteDefaults.precompiledPath ?? _root; - cascade = cascade - .add(packagesDirHandler()) - .add(_jsHandler.handler) - .add(createStaticHandler(staticFilePath, - // Precompiled directories often contain symlinks - serveFilesOutsidePath: - config.suiteDefaults.precompiledPath != null)) - .add(_wrapperHandler); - // Screenshot tests are only enabled in Chrome and Safari iOS for now. - if (browserName == 'chrome' || browserName == 'ios-safari') { - cascade = cascade.add(_screeshotHandler); - _screenshotManager = ScreenshotManager.choose(browserName); - } - } - - var pipeline = shelf.Pipeline() - .addMiddleware(PathHandler.nestedIn(_secret)) - .addHandler(cascade.handler); - - _server.mount(shelf.Cascade() + late final shelf.Handler _packageUrlHandler = packagesDirHandler(); + + final PackageConfig packageConfig; + + BrowserPlatform._({ + required this.browserEnvironment, + required this.server, + required this.isDebug, + required String faviconPath, + required this.doUpdateScreenshotGoldens, + required this.packageConfig, + }) : _screenshotManager = browserEnvironment.getScreenshotManager() { + // The cascade of request handlers. + final shelf.Cascade cascade = shelf.Cascade() + // The web socket that carries the test channels for running tests and + // reporting restuls. See [_browserManagerFor] and [BrowserManager.start] + // for details on how the channels are established. + .add(_webSocketHandler.handler) + + // Serves /favicon.ico .add(createFileHandler(faviconPath)) - .add(pipeline) - .handler); + + // Serves /packages/* requests; fetches files and sources from + // pubspec dependencies. + // + // Includes: + // * Requests for Dart sources from source maps + // * Assets that are part of the engine sources, such as Ahem.ttf + .add(_packageUrlHandler) + + // Serves files from the web_ui/build/ directory at the root (/) URL path. + // + // Includes: + // * Precompiles .js files for tests + // * Sourcemaps + .add(createStaticHandler(env.environment.webUiBuildDir.path)) + + // Serves the initial HTML for the test. + .add(_testBootstrapHandler) + + // Serves files from the root of web_ui. + // + // This is needed because sourcemaps refer to local files, i.e. those + // that don't come from package dependencies, relative to web_ui/. + // + // Examples of URLs that this handles: + // * /test/alarm_clock_test.dart + // * /lib/src/engine/alarm_clock.dart + .add(createStaticHandler(env.environment.webUiRootDir.path)) + + // Serves absolute package URLs (i.e. not /packages/* but /Users/user/*/hosted/pub.dartlang.org/*). + // This handler goes last, after all more specific handlers failed to handle the request. + .add(_createAbsolutePackageUrlHandler()) + .add(_screeshotHandler); + + server.mount(cascade.handler); } - Future _screeshotHandler(shelf.Request request) async { - if (browserName != 'chrome' && browserName != 'ios-safari') { - throw Exception('Screenshots tests are only available in Chrome ' - 'and in Safari-iOS.'); + /// Handles URLs pointing to Dart sources using absolute URI paths. + /// + /// Dart source paths that dart2js puts in source maps for pub packages are + /// relative to the source map file. Example: + /// + /// ../../../../../../../../../Users/yegor/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/frame.dart + /// + /// When the browser requests the file from the source map it sends a GET + /// request like this: + /// + /// GET /Users/yegor/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/src/frame.dart + /// + /// There's no predictable structure in this URL. It's unclear whether this + /// is a request for a source file, or someone trying to hack your + /// workstation. + /// + /// This handler treats the URL as an absolute path, but instead of + /// unconditionally serving it, it first checks with `package_config.json` on + /// whether this is a request for a Dart source that's listed in pubspec + /// dependencies. For example, the `stack_trace` package would be listed in + /// `package_config.json` as: + /// + /// file:///C:/Users/yegor/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0 + /// + /// If the requested URL points into one of the packages in the package config, + /// the file is served. Otherwise, HTTP 404 is returned without file contents. + /// + /// To handle drive letters (C:\) and *nix file system roots, the URL and + /// package paths are initially stripped of the root and compared to each + /// other as prefixes. To actually read the file, the file system root is + /// prepended before creating the file. + shelf.Handler _createAbsolutePackageUrlHandler() { + final Map urlToPackage = {}; + for (final Package package in packageConfig.packages) { + // Turns the URI as encoded in package_config.json to a file path. + final String configPath = p.fromUri(package.root); + + // Strips drive letter and root prefix, if any, for example: + // + // C:\Users\user\AppData => Users\user\AppData + // /home/user/path.dart => home/user/path.dart + final String rootRelativePath = p.relative(configPath, from: p.rootPrefix(configPath)); + urlToPackage[p.toUri(rootRelativePath).path] = package; } + return (shelf.Request request) async { + final String requestedPath = request.url.path; + // The cast is needed because keys are non-null String, so there's no way + // to return null for a mismatch. + final String? packagePath = urlToPackage.keys.cast().firstWhere( + (String? packageUrl) => requestedPath.startsWith(packageUrl!), + orElse: () => null, + ); + if (packagePath == null) { + return shelf.Response.notFound('Not a pub.dartlang.org request'); + } + + // Attach the root prefix, such as drive letter, and convert from URI to path. + // Examples: + // + // Users\user\AppData => C:\Users\user\AppData + // home/user/path.dart => /home/user/path.dart + final Package package = urlToPackage[packagePath]!; + final String filePath = p.join( + p.rootPrefix(p.fromUri(package.root.path)), + p.fromUri(requestedPath), + ); + final File fileInPackage = File(filePath); + if (!fileInPackage.existsSync()) { + return shelf.Response.notFound('File not found: $requestedPath'); + } + return shelf.Response.ok(fileInPackage.openRead()); + }; + } + Future _screeshotHandler(shelf.Request request) async { if (!request.requestedUri.path.endsWith('/screenshot')) { return shelf.Response.notFound( 'This request is not handled by the screenshot handler'); @@ -161,6 +238,15 @@ class BrowserPlatform extends PlatformPlugin { final Map requestData = json.decode(payload) as Map; final String filename = requestData['filename'] as String; + + if (_screenshotManager == null) { + print( + 'INFO: Skipping screenshot check for $filename. Current browser/OS ' + 'combination does not support screenshots.', + ); + return shelf.Response.ok(json.encode('OK')); + } + final bool write = requestData['write'] as bool; final double maxDiffRate = requestData.containsKey('maxdiffrate') ? (requestData['maxdiffrate'] as num) @@ -169,7 +255,7 @@ class BrowserPlatform extends PlatformPlugin { final Map region = requestData['region'] as Map; final PixelComparison pixelComparison = PixelComparison.values.firstWhere( - (value) => value.toString() == requestData['pixelComparison']); + (PixelComparison value) => value.toString() == requestData['pixelComparison']); final String result = await _diffScreenshot( filename, write, maxDiffRate, region, pixelComparison); return shelf.Response.ok(json.encode(result)); @@ -186,7 +272,7 @@ class BrowserPlatform extends PlatformPlugin { } filename = - filename.replaceAll('.png', '${_screenshotManager.filenameSuffix}.png'); + filename.replaceAll('.png', '${_screenshotManager!.filenameSuffix}.png'); String goldensDirectory; if (filename.startsWith('__local__')) { @@ -197,17 +283,6 @@ class BrowserPlatform extends PlatformPlugin { 'golden_files', ); } else { - // On LUCI MacOS bots the goldens are fetched by the recipe code. - // Fetch the goldens if: - // - Tests are running on a local machine. - // - Tests are running on an OS other than macOS. - if (!isLuci || !Platform.isMacOS) { - await fetchGoldens(); - } else { - if (!env.environment.webUiGoldensRepositoryDirectory.existsSync()) { - throw Exception('The goldens directory must have been copied'); - } - } goldensDirectory = p.join( env.environment.webUiGoldensRepositoryDirectory.path, 'engine', @@ -215,20 +290,7 @@ class BrowserPlatform extends PlatformPlugin { ); } - // Bail out fast if golden doesn't exist, and user doesn't want to create it. - final File file = File(p.join( - goldensDirectory, - filename, - )); - if (!file.existsSync() && !write) { - return ''' -Golden file $filename does not exist on path ${file.absolute.path} - -To automatically create this file call matchGoldenFile('$filename', write: true). -'''; - } - - final Rectangle regionAsRectange = Rectangle( + final Rectangle regionAsRectange = Rectangle( region['x'] as num, region['y'] as num, region['width'] as num, @@ -236,129 +298,28 @@ To automatically create this file call matchGoldenFile('$filename', write: true) ); // Take screenshot. - final Image screenshot = await _screenshotManager.capture(regionAsRectange); - - if (write) { - // Don't even bother with the comparison, just write and return - print('Updating screenshot golden: $file'); - file.writeAsBytesSync(encodePng(screenshot), flush: true); - if (doUpdateScreenshotGoldens) { - // Do not fail tests when bulk-updating screenshot goldens. - return 'OK'; - } else { - return 'Golden file $filename was updated. You can remove "write: true" in the call to matchGoldenFile.'; - } - } - - // Compare screenshots. - ImageDiff diff = ImageDiff( - golden: decodeNamedImage(file.readAsBytesSync(), filename), - other: screenshot, - pixelComparison: pixelComparison, - ); - - if (diff.rate > 0) { - final String testResultsPath = - env.environment.webUiTestResultsDirectory.path; - final String basename = p.basenameWithoutExtension(file.path); - - final File actualFile = - File(p.join(testResultsPath, '$basename.actual.png')); - actualFile.writeAsBytesSync(encodePng(screenshot), flush: true); - - final File diffFile = File(p.join(testResultsPath, '$basename.diff.png')); - diffFile.writeAsBytesSync(encodePng(diff.diff), flush: true); - - final File expectedFile = - File(p.join(testResultsPath, '$basename.expected.png')); - file.copySync(expectedFile.path); - - final File reportFile = - File(p.join(testResultsPath, '$basename.report.html')); - reportFile.writeAsStringSync(''' -Golden file $filename did not match the image generated by the test. - - - - - - - - - - - - -
ExpectedDiffActual
- - - - - -
-'''); - - final StringBuffer message = StringBuffer(); - message.writeln( - 'Golden file $filename did not match the image generated by the test.'); - message.writeln(getPrintableDiffFilesInfo(diff.rate, maxDiffRateFailure)); - message - .writeln('You can view the test report in your browser by opening:'); - - // Cirrus cannot serve HTML pages generated by build jobs, so we - // archive all the files so that they can be downloaded and inspected - // locally. - if (isCirrus) { - final String taskId = Platform.environment['CIRRUS_TASK_ID']; - final String baseArtifactsUrl = - 'https://api.cirrus-ci.com/v1/artifact/task/$taskId/web_engine_test/test_results'; - final String cirrusReportUrl = '$baseArtifactsUrl/$basename.report.zip'; - message.writeln(cirrusReportUrl); - - await Process.run( - 'zip', - [ - '$basename.report.zip', - '$basename.report.html', - '$basename.expected.png', - '$basename.diff.png', - '$basename.actual.png', - ], - workingDirectory: testResultsPath, - ); - } else { - final String localReportPath = '$testResultsPath/$basename.report.html'; - message.writeln(localReportPath); - } - - message.writeln( - 'To update the golden file call matchGoldenFile(\'$filename\', write: true).'); - message.writeln('Golden file: ${expectedFile.path}'); - message.writeln('Actual file: ${actualFile.path}'); - - if (diff.rate < maxDiffRateFailure) { - // Issue a warning but do not fail the test. - print('WARNING:'); - print(message); - return 'OK'; - } else { - // Fail test - return '$message'; - } - } - return 'OK'; + final Image screenshot = await _screenshotManager!.capture(regionAsRectange); + + return compareImage( + screenshot, + doUpdateScreenshotGoldens, + filename, + pixelComparison, + maxDiffRateFailure, + goldensDirectory: goldensDirectory, + write: write); } - /// A handler that serves wrapper files used to bootstrap tests. - shelf.Response _wrapperHandler(shelf.Request request) { - var path = p.fromUri(request.url); + /// Serves the HTML file that bootstraps the test. + shelf.Response _testBootstrapHandler(shelf.Request request) { + final String path = p.fromUri(request.url); if (path.endsWith('.html')) { - var test = p.withoutExtension(path) + '.dart'; + final String test = p.withoutExtension(path) + '.dart'; // Link to the Dart wrapper. - var scriptBase = htmlEscape.convert(p.basename(test)); - var link = ''; + final String scriptBase = htmlEscape.convert(p.basename(test)); + final String link = ''; return shelf.Response.ok(''' @@ -369,75 +330,83 @@ Golden file $filename did not match the image generated by the test. - ''', headers: {'Content-Type': 'text/html'}); + ''', headers: {'Content-Type': 'text/html'}); } return shelf.Response.notFound('Not found.'); } + void _checkNotClosed() { + if (_closed) { + throw StateError('Cannot load test suite. Test platform is closed.'); + } + } + /// Loads the test suite at [path] on the platform [platform]. /// /// This will start a browser to load the suite if one isn't already running. /// Throws an [ArgumentError] if `platform.platform` isn't a browser. + @override Future load(String path, SuitePlatform platform, SuiteConfiguration suiteConfig, Object message) async { + _checkNotClosed(); if (suiteConfig.precompiledPath == null) { throw Exception('This test platform only supports precompiled JS.'); } - var browser = platform.runtime; + final Runtime browser = platform.runtime; assert(suiteConfig.runtimes.contains(browser.identifier)); if (!browser.isBrowser) { throw ArgumentError('$browser is not a browser.'); } + _checkNotClosed(); - if (_closed) { - return null; - } - Uri suiteUrl = url.resolveUri( - p.toUri(p.withoutExtension(p.relative(path, from: _root)) + '.html')); - - if (_closed) { - return null; - } + final Uri suiteUrl = url.resolveUri( + p.toUri(p.withoutExtension(p.relative(path, from: env.environment.webUiBuildDir.path)) + '.html')); + _checkNotClosed(); - var browserManager = await _browserManagerFor(browser); - if (_closed || browserManager == null) { - return null; + final BrowserManager? browserManager = await _startBrowserManager(); + if (browserManager == null) { + throw StateError('Failed to initialize browser manager for ${browser.name}'); } + _checkNotClosed(); - var suite = await browserManager.load(path, suiteUrl, suiteConfig, message); - if (_closed) { - return null; - } + final RunnerSuite suite = await browserManager.load(path, suiteUrl, suiteConfig, message); + _checkNotClosed(); return suite; } - StreamChannel loadChannel(String path, SuitePlatform platform) => + @override + StreamChannel loadChannel(String path, SuitePlatform platform) => throw UnimplementedError(); - Future _browserManager; + Future? _browserManager; - /// Returns the [BrowserManager] for [runtime], which should be a browser. + /// Starts a browser manager for the browser provided by [browserEnvironment]; /// /// If no browser manager is running yet, starts one. - Future _browserManagerFor(Runtime browser) { + Future _startBrowserManager() { if (_browserManager != null) { - return _browserManager; + return _browserManager!; } - var completer = Completer.sync(); - var path = _webSocketHandler.create(webSocketHandler(completer.complete)); - var webSocketUrl = url.replace(scheme: 'ws').resolve(path); - var hostUrl = (_config.pubServeUrl == null ? url : _config.pubServeUrl) + final Completer completer = Completer.sync(); + final String path = _webSocketHandler.create(webSocketHandler(completer.complete)); + final Uri webSocketUrl = url.replace(scheme: 'ws').resolve(path); + final Uri hostUrl = url .resolve('packages/web_engine_tester/static/index.html') .replace(queryParameters: { 'managerUrl': webSocketUrl.toString(), - 'debug': _config.pauseAfterLoad.toString() + 'debug': isDebug.toString() }); - var future = BrowserManager.start(browser, hostUrl, completer.future, - debug: _config.pauseAfterLoad); + final Future future = BrowserManager.start( + browserEnvironment: browserEnvironment, + url: hostUrl, + future: completer.future, + packageConfig: packageConfig, + debug: isDebug, + ); // Store null values for browsers that error out so we know not to load them // again. @@ -450,10 +419,11 @@ Golden file $filename did not match the image generated by the test. /// /// Note that this doesn't close the server itself. Browser tests can still be /// loaded, they'll just spawn new browsers. + @override Future closeEphemeral() async { - final BrowserManager result = await _browserManager; - if (result != null) { - await result.close(); + if (_browserManager != null) { + final BrowserManager? result = await _browserManager!; + await result?.close(); } } @@ -461,22 +431,19 @@ Golden file $filename did not match the image generated by the test. /// /// Returns a [Future] that completes once the server is closed and its /// resources have been fully released. + @override Future close() { return _closeMemo.runOnce(() async { final List> futures = >[]; futures.add(Future.microtask(() async { - final BrowserManager result = await _browserManager; - if (result != null) { - await result.close(); + if (_browserManager != null) { + final BrowserManager? result = await _browserManager!; + await result?.close(); } })); - futures.add(_server.close()); + futures.add(server.close()); await Future.wait(futures); - - if (_config.pubServeUrl != null) { - _http.close(); - } }); } @@ -489,10 +456,10 @@ Golden file $filename did not match the image generated by the test. /// invalid and don't need to have a persistent URL. class OneOffHandler { /// A map from URL paths to handlers. - final _handlers = Map(); + final Map _handlers = {}; /// The counter of handlers that have been activated. - var _counter = 0; + int _counter = 0; /// The actual [shelf.Handler] that dispatches requests. shelf.Handler get handler => _onRequest; @@ -504,7 +471,7 @@ class OneOffHandler { /// /// [handler] will be unmounted as soon as it receives a request. String create(shelf.Handler handler) { - var path = _counter.toString(); + final String path = _counter.toString(); _handlers[path] = handler; _counter++; return path; @@ -512,13 +479,13 @@ class OneOffHandler { /// Dispatches [request] to the appropriate handler. FutureOr _onRequest(shelf.Request request) { - var components = p.url.split(request.url.path); + final List components = p.url.split(request.url.path); if (components.isEmpty) { return shelf.Response.notFound(null); } - var path = components.removeAt(0); - var handler = _handlers.remove(path); + final String path = components.removeAt(0); + final shelf.Handler? handler = _handlers.remove(path); if (handler == null) { return shelf.Response.notFound(null); } @@ -526,82 +493,23 @@ class OneOffHandler { } } -/// A handler that routes to sub-handlers based on exact path prefixes. -class PathHandler { - /// A trie of path components to handlers. - final _paths = _Node(); - - /// The shelf handler. - shelf.Handler get handler => _onRequest; - - /// Returns middleware that nests all requests beneath the URL prefix - /// [beneath]. - static shelf.Middleware nestedIn(String beneath) { - return (handler) { - var pathHandler = PathHandler()..add(beneath, handler); - return pathHandler.handler; - }; - } - - /// Routes requests at or under [path] to [handler]. - /// - /// If [path] is a parent or child directory of another path in this handler, - /// the longest matching prefix wins. - void add(String path, shelf.Handler handler) { - var node = _paths; - for (var component in p.url.split(path)) { - node = node.children.putIfAbsent(component, () => _Node()); - } - node.handler = handler; - } - - FutureOr _onRequest(shelf.Request request) { - shelf.Handler handler; - int handlerIndex; - var node = _paths; - var components = p.url.split(request.url.path); - for (var i = 0; i < components.length; i++) { - node = node.children[components[i]]; - if (node == null) { - break; - } - if (node.handler == null) { - continue; - } - handler = node.handler; - handlerIndex = i; - } - - if (handler == null) { - return shelf.Response.notFound('Not found.'); - } - - return handler( - request.change(path: p.url.joinAll(components.take(handlerIndex + 1)))); - } -} - -/// A trie node. -class _Node { - shelf.Handler handler; - final children = Map(); -} - -/// A class that manages the connection to a single running browser. +/// Manages the connection to a single running browser. /// /// This is in charge of telling the browser which test suites to load and /// converting its responses into [Suite] objects. class BrowserManager { + final PackageConfig packageConfig; + /// The browser instance that this is connected to via [_channel]. final Browser _browser; - /// The [Runtime] for [_browser]. - final Runtime _runtime; + /// The browser environment for this test. + final BrowserEnvironment _browserEnvironment; /// The channel used to communicate with the browser. /// /// This is connected to a page running `static/host.dart`. - MultiChannel _channel; + late final MultiChannel _channel; /// A pool that ensures that limits the number of initial connections the /// manager will wait for at once. @@ -610,7 +518,7 @@ class BrowserManager { /// loaded in the same browser. However, the browser can only load so many at /// once, and we want a timeout in case they fail so we only wait for so many /// at once. - final _pool = Pool(8); + final Pool _pool = Pool(8); /// The ID of the next suite to be loaded. /// @@ -625,25 +533,25 @@ class BrowserManager { /// /// This will be `null` as long as the browser isn't displaying a pause /// screen. - CancelableCompleter _pauseCompleter; + CancelableCompleter? _pauseCompleter; /// The controller for [_BrowserEnvironment.onRestart]. - final _onRestartController = StreamController.broadcast(); + final StreamController _onRestartController = StreamController.broadcast(); /// The environment to attach to each suite. - Future<_BrowserEnvironment> _environment; + late final Future<_BrowserEnvironment> _environment; /// Controllers for every suite in this browser. /// /// These are used to mark suites as debugging or not based on the browser's /// pings. - final _controllers = Set(); + final Set _controllers = {}; // A timer that's reset whenever we receive a message from the browser. // // Because the browser stops running code when the user is actively debugging, // this lets us detect whether they're debugging reasonably accurately. - RestartableTimer _timer; + late final RestartableTimer _timer; /// Starts the browser identified by [runtime] and has it connect to [url]. /// @@ -656,34 +564,38 @@ class BrowserManager { /// /// Returns the browser manager, or throws an [Exception] if a /// connection fails to be established. - static Future start( - Runtime runtime, Uri url, Future future, - {bool debug = false}) { - var browser = _newBrowser(url, runtime, debug: debug); + static Future start({ + required BrowserEnvironment browserEnvironment, + required Uri url, + required Future future, + required PackageConfig packageConfig, + bool debug = false, + }) { + final Browser browser = _newBrowser(url, browserEnvironment, debug: debug); - var completer = Completer(); + final Completer completer = Completer(); // For the cases where we use a delegator such as `adb` (for Android) or // `xcrun` (for IOS), these delegator processes can shut down before the - // websocket is available. Therefore do not throw an error if proccess + // websocket is available. Therefore do not throw an error if process // exits with exitCode 0. Note that `browser` will throw and error if the // exit code was not 0, which will be processed by the next callback. - browser.onExit.catchError((dynamic error, StackTrace stackTrace) { + browser.onExit.catchError((Object error, StackTrace stackTrace) { if (completer.isCompleted) { return; } completer.completeError(error, stackTrace); }); - future.then((webSocket) { + future.then((WebSocketChannel webSocket) { if (completer.isCompleted) { return; } - completer.complete(BrowserManager._(browser, runtime, webSocket)); - }).catchError((dynamic error, StackTrace stackTrace) { + completer.complete(BrowserManager._(packageConfig, browser, browserEnvironment, webSocket)); + }).catchError((Object error, StackTrace stackTrace) { browser.close(); if (completer.isCompleted) { - return; + return null; } completer.completeError(error, stackTrace); }); @@ -691,24 +603,24 @@ class BrowserManager { return completer.future; } - /// Starts the browser identified by [browser] using [settings] and has it load [url]. + /// Starts the browser and requests that it load the test page at [url]. /// /// If [debug] is true, starts the browser in debug mode. - static Browser _newBrowser(Uri url, Runtime browser, {bool debug = false}) { - return SupportedBrowsers.instance.getBrowser(browser, url, debug: debug); + static Browser _newBrowser(Uri url, BrowserEnvironment browserEnvironment, {bool debug = false}) { + return browserEnvironment.launchBrowserInstance(url, debug: debug); } - /// Creates a new BrowserManager that communicates with [browser] over + /// Creates a new BrowserManager that communicates with the browser over /// [webSocket]. - BrowserManager._(this._browser, this._runtime, WebSocketChannel webSocket) { + BrowserManager._(this.packageConfig, this._browser, this._browserEnvironment, WebSocketChannel webSocket) { // The duration should be short enough that the debugging console is open as // soon as the user is done setting breakpoints, but long enough that a test // doing a lot of synchronous work doesn't trigger a false positive. // // Start this canceled because we don't want it to start ticking until we // get some response from the iframe. - _timer = RestartableTimer(Duration(seconds: 3), () { - for (var controller in _controllers) { + _timer = RestartableTimer(const Duration(seconds: 3), () { + for (final RunnerSuiteController controller in _controllers) { controller.setDebugging(true); } }) @@ -717,12 +629,12 @@ class BrowserManager { // Whenever we get a message, no matter which child channel it's for, we the // know browser is still running code which means the user isn't debugging. _channel = MultiChannel( - webSocket.cast().transform(jsonDocument).changeStream((stream) { - return stream.map((message) { + webSocket.cast().transform(jsonDocument).changeStream((Stream stream) { + return stream.map((Object? message) { if (!_closed) { _timer.reset(); } - for (var controller in _controllers) { + for (final RunnerSuiteController controller in _controllers) { controller.setDebugging(false); } @@ -732,7 +644,7 @@ class BrowserManager { _environment = _loadBrowserEnvironment(); _channel.stream - .listen((dynamic message) => _onMessage(message as Map), onDone: close); + .listen((dynamic message) => _onMessage(message as Map), onDone: close); } /// Loads [_BrowserEnvironment]. @@ -751,31 +663,31 @@ class BrowserManager { url = url.replace( fragment: Uri.encodeFull(jsonEncode({ 'metadata': suiteConfig.metadata.serialize(), - 'browser': _runtime.identifier + 'browser': _browserEnvironment.packageTestRuntime.identifier }))); - var suiteID = _suiteID++; - RunnerSuiteController controller; + final int suiteID = _suiteID++; + RunnerSuiteController? controller; void closeIframe() { if (_closed) { return; } _controllers.remove(controller); - _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); + _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); } // The virtual channel will be closed when the suite is closed, in which // case we should unload the iframe. - var virtualChannel = _channel.virtualChannel(); - var suiteChannelID = virtualChannel.id; - var suiteChannel = virtualChannel.transformStream( - StreamTransformer.fromHandlers(handleDone: (sink) { + final VirtualChannel virtualChannel = _channel.virtualChannel(); + final int suiteChannelID = virtualChannel.id; + final StreamChannel suiteChannel = virtualChannel.transformStream( + StreamTransformer.fromHandlers(handleDone: (EventSink sink) { closeIframe(); sink.close(); })); - return await _pool.withResource(() async { - _channel.sink.add({ + return _pool.withResource(() async { + _channel.sink.add({ 'command': 'loadSuite', 'url': url.toString(), 'id': suiteID, @@ -783,7 +695,7 @@ class BrowserManager { }); try { - controller = deserializeSuite(path, currentPlatform(_runtime), + controller = deserializeSuite(path, currentPlatform(_browserEnvironment.packageTestRuntime), suiteConfig, await _environment, suiteChannel, message); final String sourceMapFileName = @@ -793,10 +705,8 @@ class BrowserManager { final String mapPath = p.join(env.environment.webUiRootDir.path, 'build', pathToTest, sourceMapFileName); - PackageConfig packageConfig = - await loadPackageConfigUri(await Isolate.packageConfig); - Map packageMap = { - for (var p in packageConfig.packages) p.name: p.packageUriRoot + final Map packageMap = { + for (Package p in packageConfig.packages) p.name: p.packageUriRoot }; final JSStackTraceMapper mapper = JSStackTraceMapper( await File(mapPath).readAsString(), @@ -805,10 +715,10 @@ class BrowserManager { sdkRoot: p.toUri(sdkDir), ); - controller.channel('test.browser.mapper').sink.add(mapper.serialize()); + controller!.channel('test.browser.mapper').sink.add(mapper.serialize()); - _controllers.add(controller); - return await controller.suite; + _controllers.add(controller!); + return await controller!.suite; } catch (_) { closeIframe(); rethrow; @@ -817,27 +727,29 @@ class BrowserManager { } /// An implementation of [Environment.displayPause]. - CancelableOperation _displayPause() { - if (_pauseCompleter != null) { - return _pauseCompleter.operation; + CancelableOperation _displayPause() { + CancelableCompleter? pauseCompleter = _pauseCompleter; + if (pauseCompleter != null) { + return pauseCompleter.operation; } - _pauseCompleter = CancelableCompleter(onCancel: () { - _channel.sink.add({'command': 'resume'}); + pauseCompleter = CancelableCompleter(onCancel: () { + _channel.sink.add({'command': 'resume'}); _pauseCompleter = null; }); + _pauseCompleter = pauseCompleter; - _pauseCompleter.operation.value.whenComplete(() { + pauseCompleter.operation.value.whenComplete(() { _pauseCompleter = null; }); - _channel.sink.add({'command': 'displayPause'}); + _channel.sink.add({'command': 'displayPause'}); - return _pauseCompleter.operation; + return pauseCompleter.operation; } /// The callback for handling messages received from the host page. - void _onMessage(Map message) { + void _onMessage(Map message) { switch (message['command'] as String) { case 'ping': break; @@ -847,9 +759,7 @@ class BrowserManager { break; case 'resume': - if (_pauseCompleter != null) { - _pauseCompleter.complete(); - } + _pauseCompleter?.complete(); break; default: @@ -861,12 +771,10 @@ class BrowserManager { /// Closes the manager and releases any resources it owns, including closing /// the browser. - Future close() => _closeMemoizer.runOnce(() { + Future close() => _closeMemoizer.runOnce(() { _closed = true; _timer.cancel(); - if (_pauseCompleter != null) { - _pauseCompleter.complete(); - } + _pauseCompleter?.complete(); _pauseCompleter = null; _controllers.clear(); return _browser.close(); @@ -880,18 +788,23 @@ class BrowserManager { class _BrowserEnvironment implements Environment { final BrowserManager _manager; - final supportsDebugging = true; + @override + final bool supportsDebugging = true; - final Uri observatoryUrl; + @override + final Uri? observatoryUrl; - final Uri remoteDebuggerUrl; + @override + final Uri? remoteDebuggerUrl; - final Stream onRestart; + @override + final Stream onRestart; _BrowserEnvironment(this._manager, this.observatoryUrl, this.remoteDebuggerUrl, this.onRestart); - CancelableOperation displayPause() => _manager._displayPause(); + @override + CancelableOperation displayPause() => _manager._displayPause(); } bool get isCirrus => Platform.environment['CIRRUS_CI'] == 'true'; diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 5ad1b3b53dfc3..1022f4c2d0829 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -2,63 +2,44 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:io' as io; import 'package:args/command_runner.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:pool/pool.dart'; -import 'package:test_core/src/runner/hack_register_platform.dart' - as hack; // ignore: implementation_imports -import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports -import 'package:test_core/src/executable.dart' - as test; // ignore: implementation_imports +import 'package:watcher/src/watch_event.dart'; + +import 'browser.dart'; import 'common.dart'; -import 'environment.dart'; -import 'exceptions.dart'; -import 'integration_tests_manager.dart'; -import 'macos_info.dart'; -import 'safari_installation.dart'; -import 'supported_browsers.dart'; -import 'test_platform.dart'; +import 'pipeline.dart'; +import 'steps/compile_tests_step.dart'; +import 'steps/run_tests_step.dart'; import 'utils.dart'; -/// The type of tests requested by the tool user. -enum TestTypesRequested { - /// For running the unit tests only. - unit, - - /// For running the integration tests only. - integration, - - /// For running both unit and integration tests. - all, -} - -class TestCommand extends Command with ArgUtils { +/// Runs tests. +class TestCommand extends Command with ArgUtils { TestCommand() { argParser ..addFlag( 'debug', + defaultsTo: false, help: 'Pauses the browser before running a test, giving you an ' 'opportunity to add breakpoints or inspect loaded code before ' 'running the code.', ) ..addFlag( - 'unit-tests-only', + 'watch', defaultsTo: false, - help: 'felt test command runs the unit tests and the integration tests ' - 'at the same time. If this flag is set, only run the unit tests.', + abbr: 'w', + help: 'Run in watch mode so the tests re-run whenever a change is ' + 'made.', ) ..addFlag( - 'integration-tests-only', + 'unit-tests-only', defaultsTo: false, help: 'felt test command runs the unit tests and the integration tests ' - 'at the same time. If this flag is set, only run the integration ' - 'tests.', + 'at the same time. If this flag is set, only run the unit tests.', ) ..addFlag('use-system-flutter', defaultsTo: false, @@ -80,15 +61,28 @@ class TestCommand extends Command with ArgUtils { '.dart_tool/goldens. Use this option to bulk-update all screenshots, ' 'for example, when a new browser version affects pixels.', ) + ..addFlag( + 'skip-goldens-repo-fetch', + defaultsTo: false, + help: 'If set reuses the existig flutter/goldens repo clone. Use this ' + 'to avoid overwriting local changes when iterating on golden ' + 'tests. This is off by default.', + ) ..addOption( 'browser', defaultsTo: 'chrome', help: 'An option to choose a browser to run the tests. Tests only work ' ' on Chrome for now.', + ) + ..addFlag( + 'fail-early', + defaultsTo: false, + negatable: true, + help: 'If set, causes the test runner to exit upon the first test ' + 'failure. If not set, the test runner will continue running ' + 'test despite failures and will report them after all tests ' + 'finish.', ); - - SupportedBrowsers.instance.argParsers - .forEach((t) => t.populateOptions(argParser)); } @override @@ -97,212 +91,9 @@ class TestCommand extends Command with ArgUtils { @override final String description = 'Run tests.'; - TestTypesRequested testTypesRequested = null; - - /// How many dart2js build tasks are running at the same time. - final Pool _pool = Pool(8); - - /// Check the flags to see what type of tests are requested. - TestTypesRequested findTestType() { - if (boolArg('unit-tests-only') && boolArg('integration-tests-only')) { - throw ArgumentError('Conflicting arguments: unit-tests-only and ' - 'integration-tests-only are both set'); - } else if (boolArg('unit-tests-only')) { - print('Running the unit tests only'); - return TestTypesRequested.unit; - } else if (boolArg('integration-tests-only')) { - if (!isChrome && !isSafariOnMacOS && !isFirefox) { - throw UnimplementedError( - 'Integration tests are only available on Chrome Desktop for now'); - } - return TestTypesRequested.integration; - } else { - return TestTypesRequested.all; - } - } - - @override - Future run() async { - SupportedBrowsers.instance - ..argParsers.forEach((t) => t.parseOptions(argResults)); - - // Check the flags to see what type of integration tests are requested. - testTypesRequested = findTestType(); - - if (isSafariOnMacOS) { - /// Collect information on the bot. - final MacOSInfo macOsInfo = new MacOSInfo(); - await macOsInfo.printInformation(); - } - - switch (testTypesRequested) { - case TestTypesRequested.unit: - return runUnitTests(); - case TestTypesRequested.integration: - return runIntegrationTests(); - case TestTypesRequested.all: - if (runAllTests && isIntegrationTestsAvailable) { - bool unitTestResult = await runUnitTests(); - bool integrationTestResult = await runIntegrationTests(); - if (integrationTestResult != unitTestResult) { - print('Tests run. Integration tests passed: $integrationTestResult ' - 'unit tests passed: $unitTestResult'); - } - return integrationTestResult && unitTestResult; - } else { - return await runUnitTests(); - } - } - return false; - } - - Future runIntegrationTests() async { - return IntegrationTestsManager(browser, useSystemFlutter).runTests(); - } - - Future runUnitTests() async { - _copyTestFontsIntoWebUi(); - await _buildHostPage(); - if (io.Platform.isWindows) { - // On Dart 2.7 or greater, it gives an error for not - // recognized "pub" version and asks for "pub" get. - // See: https://github.com/dart-lang/sdk/issues/39738 - await _runPubGet(); - } - - await _prepare(); - await _buildTargets(); - - if (runAllTests) { - await _runAllTestsForCurrentPlatform(); - } else { - await _runSpecificTests(targetFiles); - } - return true; - } - - /// Preparations before running the tests such as booting simulators or - /// creating directories. - Future _prepare() async { - if (environment.webUiTestResultsDirectory.existsSync()) { - environment.webUiTestResultsDirectory.deleteSync(recursive: true); - } - environment.webUiTestResultsDirectory.createSync(recursive: true); - - // In order to run iOS Safari unit tests we need to make sure iOS Simulator - // is booted. - if (isSafariIOS) { - await IosSafariArgParser.instance.initIosSimulator(); - } - } - - /// Builds all test targets that will be run. - Future _buildTargets() async { - final Stopwatch stopwatch = Stopwatch()..start(); - List allTargets; - if (runAllTests) { - allTargets = environment.webUiTestDir - .listSync(recursive: true) - .whereType() - .where((io.File f) => f.path.endsWith('_test.dart')) - .map((io.File f) => FilePath.fromWebUi( - path.relative(f.path, from: environment.webUiRootDir.path))) - .toList(); - } else { - allTargets = targetFiles; - } + bool get isWatchMode => boolArg('watch'); - // Separate HTML targets from CanvasKit targets because the two use - // different dart2js options (and different build.*.yaml files). - final List htmlTargets = []; - final List canvasKitTargets = []; - final String canvasKitTestDirectory = - path.join(environment.webUiTestDir.path, 'canvaskit'); - for (FilePath target in allTargets) { - if (path.isWithin(canvasKitTestDirectory, target.absolute)) { - canvasKitTargets.add(target); - } else { - htmlTargets.add(target); - } - } - - if (htmlTargets.isNotEmpty) { - await _buildTestsInParallel(targets: htmlTargets, forCanvasKit: false); - } - - // Currently iOS Safari tests are running on simulator, which does not - // support canvaskit backend. - if (canvasKitTargets.isNotEmpty) { - await _buildTestsInParallel( - targets: canvasKitTargets, forCanvasKit: true); - } - - _copyFilesFromTestToBuid(); - _copyFilesFromLibToBuid(); - - stopwatch.stop(); - print('The build took ${stopwatch.elapsedMilliseconds ~/ 1000} seconds.'); - - _cleanupExtraFilesUnderTestDir(); - } - - /// Copy image files from test/ to build/test/. - /// - /// By copying all the files helps with the following: - /// - Tests using on an asset/image are able to reach these files. - /// - Source maps can locate test files. - /// - /// A side effect is this file copies all the images even when only one - /// target test is asked to run. - void _copyFilesFromTestToBuid() { - final List contents = - environment.webUiTestDir.listSync(recursive: true); - _copyFiles(contents); - } - - /// Copy contents of /lib under /build. - /// - /// Since the source map are created to assume library files are under - /// `../../../../lib/src/`. Unless these files are copied under /build, - /// they are not visible during debug. - /// - /// This operation was handled by `build_runner` before we started using - /// plain `dart2js`. - void _copyFilesFromLibToBuid() { - final List contents = - environment.webUiLibDir.listSync(recursive: true); - _copyFiles(contents); - } - - void _copyFiles(List contents) { - contents.whereType().forEach((final io.File entity) { - final String directoryPath = path.relative(path.dirname(entity.path), - from: environment.webUiRootDir.path); - final io.Directory directory = io.Directory( - path.join(environment.webUiBuildDir.path, directoryPath)); - if (!directory.existsSync()) { - directory.createSync(recursive: true); - } - final String pathRelativeToWebUi = path.relative(entity.absolute.path, - from: environment.webUiRootDir.path); - entity.copySync( - path.join(environment.webUiBuildDir.path, pathRelativeToWebUi)); - }); - } - - /// Dart2js initially run under /test directory. - /// - /// The following files are copied under /build directory after that. - /// - void _cleanupExtraFilesUnderTestDir() { - final List contents = - environment.webUiTestDir.listSync(recursive: true); - contents.whereType().forEach((final io.File entity) { - if (path.basename(entity.path).contains('.browser_test.dart.js')) { - entity.deleteSync(); - } - }); - } + bool get failEarly => boolArg('fail-early'); /// Whether to start the browser in debug mode. /// @@ -311,392 +102,97 @@ class TestCommand extends Command with ArgUtils { bool get isDebug => boolArg('debug'); /// Paths to targets to run, e.g. a single test. - List get targets => argResults.rest; + List get targets => argResults!.rest; /// The target test files to run. - /// - /// The value can be null if the developer prefers to run all the tests. - List get targetFiles => (targets.isEmpty) - ? null - : targets.map((t) => FilePath.fromCwd(t)).toList(); + List get targetFiles => targets.map((String t) => FilePath.fromCwd(t)).toList(); /// Whether all tests should run. bool get runAllTests => targets.isEmpty; /// The name of the browser to run tests in. - String get browser => (argResults != null) ? stringArg('browser') : 'chrome'; - - /// Whether [browser] is set to "chrome". - bool get isChrome => browser == 'chrome'; - - /// Whether [browser] is set to "firefox". - bool get isFirefox => browser == 'firefox'; - - /// Whether [browser] is set to "safari". - bool get isSafariOnMacOS => browser == 'safari' && io.Platform.isMacOS; - - /// Whether [browser] is set to "ios-safari". - bool get isSafariIOS => browser == 'ios-safari' && io.Platform.isMacOS; - - /// Due to lack of resources Chrome integration tests only run on Linux on - /// LUCI. - /// - /// They run on all platforms for local. - bool get isChromeIntegrationTestAvailable => - (isChrome && isLuci && io.Platform.isLinux) || (isChrome && !isLuci); - - /// Due to efficiancy constraints, Firefox integration tests only run on - /// Linux on LUCI. - /// - /// For now Firefox integration tests only run on Linux and Mac on local. - /// - // TODO: https://github.com/flutter/flutter/issues/63832 - bool get isFirefoxIntegrationTestAvailable => - (isFirefox && isLuci && io.Platform.isLinux) || - (isFirefox && !isLuci && !io.Platform.isWindows); - - /// Latest versions of Safari Desktop are only available on macOS. - /// - /// Integration testing on LUCI is not supported at the moment. - // TODO: https://github.com/flutter/flutter/issues/63710 - bool get isSafariIntegrationTestAvailable => isSafariOnMacOS && !isLuci; - - /// Due to various factors integration tests might be missing on a given - /// platform and given environment. - /// See: [isChromeIntegrationTestAvailable] - /// See: [isSafariIntegrationTestAvailable] - /// See: [isFirefoxIntegrationTestAvailable] - bool get isIntegrationTestsAvailable => - isChromeIntegrationTestAvailable || - isFirefoxIntegrationTestAvailable || - isSafariIntegrationTestAvailable; - - /// Use system flutter instead of cloning the repository. - /// - /// Read the flag help for more details. Uses PATH to locate flutter. - bool get useSystemFlutter => boolArg('use-system-flutter'); + String get browser => stringArg('browser'); /// When running screenshot tests writes them to the file system into /// ".dart_tool/goldens". bool get doUpdateScreenshotGoldens => boolArg('update-screenshot-goldens'); - /// Runs all tests specified in [targets]. - /// - /// Unlike [_runAllTestsForCurrentPlatform], this does not filter targets - /// by platform/browser capabilites, and instead attempts to run all of - /// them. - Future _runSpecificTests(List targets) async { - await _runTestBatch(targets, concurrency: 1, expectFailure: false); - _checkExitCode(); - } - - /// Runs as many tests as possible on the current OS/browser combination. - Future _runAllTestsForCurrentPlatform() async { - final io.Directory testDir = io.Directory(path.join( - environment.webUiRootDir.path, - 'test', - )); - - // Screenshot tests and smoke tests only run on: "Chrome/iOS Safari" - // locally and on LUCI. They are not available on Windows bots: - // TODO: https://github.com/flutter/flutter/issues/63710 - if ((isChrome && isLuci && io.Platform.isLinux) || - ((isChrome || isSafariIOS) && !isLuci) || - (isSafariIOS && isLuci)) { - print('INFO: Also running the screenshot tests.'); - // Separate screenshot tests from unit-tests. Screenshot tests must run - // one at a time. Otherwise, they will end up screenshotting each other. - // This is not an issue for unit-tests. - final FilePath failureSmokeTestPath = FilePath.fromWebUi( - 'test/golden_tests/golden_failure_smoke_test.dart', - ); - final List screenshotTestFiles = []; - final List unitTestFiles = []; - - for (io.File testFile - in testDir.listSync(recursive: true).whereType()) { - final FilePath testFilePath = FilePath.fromCwd(testFile.path); - if (!testFilePath.absolute.endsWith('_test.dart')) { - // Not a test file at all. Skip. - continue; - } - if (testFilePath == failureSmokeTestPath) { - // A smoke test that fails on purpose. Skip. - continue; - } - - if (path.split(testFilePath.relativeToWebUi).contains('golden_tests')) { - screenshotTestFiles.add(testFilePath); - } else { - unitTestFiles.add(testFilePath); - } - } - - // This test returns a non-zero exit code on purpose. Run it separately. - if (io.Platform.environment['CIRRUS_CI'] != 'true') { - await _runTestBatch( - [failureSmokeTestPath], - concurrency: 1, - expectFailure: true, - ); - _checkExitCode(); - } - - // Run all unit-tests as a single batch. - await _runTestBatch(unitTestFiles, concurrency: 10, expectFailure: false); - _checkExitCode(); - - // Run screenshot tests one at a time. - for (FilePath testFilePath in screenshotTestFiles) { - await _runTestBatch( - [testFilePath], - concurrency: 1, - expectFailure: false, - ); - _checkExitCode(); - } - } else { - final List unitTestFiles = []; - for (io.File testFile - in testDir.listSync(recursive: true).whereType()) { - final FilePath testFilePath = FilePath.fromCwd(testFile.path); - if (!testFilePath.absolute.endsWith('_test.dart')) { - // Not a test file at all. Skip. - continue; - } - if (!path - .split(testFilePath.relativeToWebUi) - .contains('golden_tests')) { - unitTestFiles.add(testFilePath); - } - } - // Run all unit-tests as a single batch. - await _runTestBatch(unitTestFiles, concurrency: 10, expectFailure: false); - _checkExitCode(); - } - } - - void _checkExitCode() { - if (io.exitCode != 0) { - throw ToolException('Process exited with exit code ${io.exitCode}.'); - } - } - - Future _runPubGet() async { - final int exitCode = await runProcess( - environment.pubExecutable, - [ - 'get', - ], - workingDirectory: environment.webUiRootDir.path, - ); - - if (exitCode != 0) { - throw ToolException( - 'Failed to run pub get. Exited with exit code $exitCode'); - } - } - - Future _buildHostPage() async { - final String hostDartPath = path.join('lib', 'static', 'host.dart'); - final io.File hostDartFile = io.File(path.join( - environment.webEngineTesterRootDir.path, - hostDartPath, - )); - final io.File timestampFile = io.File(path.join( - environment.webEngineTesterRootDir.path, - '$hostDartPath.js.timestamp', - )); - - final String timestamp = - hostDartFile.statSync().modified.millisecondsSinceEpoch.toString(); - if (timestampFile.existsSync()) { - final String lastBuildTimestamp = timestampFile.readAsStringSync(); - if (lastBuildTimestamp == timestamp) { - // The file is still fresh. No need to rebuild. - return; - } else { - // Record new timestamp, but don't return. We need to rebuild. - print('${hostDartFile.path} timestamp changed. Rebuilding.'); - } - } else { - print('Building ${hostDartFile.path}.'); - } - - final int exitCode = await runProcess( - environment.dart2jsExecutable, - [ - hostDartPath, - '-o', - '$hostDartPath.js', - ], - workingDirectory: environment.webEngineTesterRootDir.path, - ); - - if (exitCode != 0) { - throw ToolException('Failed to compile ${hostDartFile.path}. Compiler ' - 'exited with exit code $exitCode'); - } - - // Record the timestamp to avoid rebuilding unless the file changes. - timestampFile.writeAsStringSync(timestamp); - } - - Future _buildTestsInParallel( - {List targets, bool forCanvasKit = false}) async { - final List buildInputs = targets - .map((FilePath f) => TestBuildInput(f, forCanvasKit: forCanvasKit)) - .toList(); - - final results = _pool.forEach( - buildInputs, - _buildTest, - ); - await for (final bool isSuccess in results) { - if (!isSuccess) { - throw ToolException('Failed to compile tests.'); - } - } - } + /// Whether to fetch the goldens repo prior to running tests. + bool get skipGoldensRepoFetch => boolArg('skip-goldens-repo-fetch'); - /// Builds the specific test [targets]. - /// - /// [targets] must not be null. - /// - /// Uses `dart2js` for building the test. - /// - /// When building for CanvasKit we have to use extra argument - /// `DFLUTTER_WEB_USE_SKIA=true`. - /// - /// Dart2js creates the following outputs: - /// - target.browser_test.dart.js - /// - target.browser_test.dart.js.deps - /// - target.browser_test.dart.js.maps - /// under the same directory with test file. If all these files are not in - /// the same directory, Chrome dev tools cannot load the source code during - /// debug. - /// - /// All the files under test already copied from /test directory to /build - /// directory before test are build. See [_copyFilesFromTestToBuid]. - /// - /// Later the extra files will be deleted in [_cleanupExtraFilesUnderTestDir]. - Future _buildTest(TestBuildInput input) async { - final targetFileName = '${input.path.relativeToWebUi}.browser_test.dart.js'; - - final io.Directory directoryToTarget = io.Directory(path.join( - environment.webUiBuildDir.path, - path.dirname(input.path.relativeToWebUi))); - - if (!directoryToTarget.existsSync()) { - directoryToTarget.createSync(recursive: true); - } - - List arguments = [ - '--no-minify', - '--disable-inlining', - '--enable-asserts', - '--enable-experiment=non-nullable', - '--no-sound-null-safety', - if (input.forCanvasKit) '-DFLUTTER_WEB_USE_SKIA=true', - '-O2', - '-o', - targetFileName, // target path. - '${input.path.relativeToWebUi}', // current path. - ]; - - final int exitCode = await runProcess( - environment.dart2jsExecutable, - arguments, - workingDirectory: environment.webUiRootDir.path, - ); - - if (exitCode != 0) { - io.stderr.writeln('ERROR: Failed to compile test ${input.path}. ' - 'Dart2js exited with exit code $exitCode'); - return false; - } else { - return true; + @override + Future run() async { + final BrowserEnvironment browserEnvironment = getBrowserEnvironment(browser); + + final List testFiles = runAllTests + ? findAllTests() + : targetFiles; + + final Pipeline testPipeline = Pipeline(steps: [ + if (isWatchMode) ClearTerminalScreenStep(), + CompileTestsStep( + skipGoldensRepoFetch: skipGoldensRepoFetch, + testFiles: testFiles, + ), + RunTestsStep( + browserEnvironment: browserEnvironment, + testFiles: testFiles, + isDebug: isDebug, + doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, + ), + ]); + await testPipeline.run(); + + if (isWatchMode) { + final FilePath dir = FilePath.fromWebUi(''); + print(''); + print('Initial test run is done!'); + print( + 'Watching ${dir.relativeToCwd}/lib and ${dir.relativeToCwd}/test to re-run tests'); + print(''); + await PipelineWatcher( + dir: dir.absolute, + pipeline: testPipeline, + ignore: (WatchEvent event) { + // Ignore font files that are copied whenever tests run. + if (event.path.endsWith('.ttf')) { + return true; + } + + // React to changes in lib/ and test/ folders. + final String relativePath = + path.relative(event.path, from: dir.absolute); + if (path.isWithin('lib', relativePath) || + path.isWithin('test', relativePath)) { + return false; + } + + // Ignore anything else. + return true; + }).start(); } + return true; } +} - /// Runs a batch of tests. - /// - /// Unless [expectFailure] is set to false, sets [io.exitCode] to a non-zero value if any tests fail. - Future _runTestBatch( - List testFiles, { - @required int concurrency, - @required bool expectFailure, - }) async { - final List testArgs = [ - ...['-r', 'compact'], - '--concurrency=$concurrency', - if (isDebug) '--pause-after-load', - '--platform=${SupportedBrowsers.instance.supportedBrowserToPlatform[browser]}', - '--precompiled=${environment.webUiRootDir.path}/build', - SupportedBrowsers.instance.browserToConfiguration[browser], - '--', - ...testFiles.map((f) => f.relativeToWebUi).toList(), - ]; +/// Clears the terminal screen and places the cursor at the top left corner. +/// +/// This works on Linux and Mac. On Windows, it's a no-op. +class ClearTerminalScreenStep implements PipelineStep { + @override + String get description => 'clearing terminal screen'; - hack.registerPlatformPlugin([ - SupportedBrowsers.instance.supportedBrowsersToRuntimes[browser] - ], () { - return BrowserPlatform.start( - browser, - root: io.Directory.current.path, - // It doesn't make sense to update a screenshot for a test that is expected to fail. - doUpdateScreenshotGoldens: !expectFailure && doUpdateScreenshotGoldens, - ); - }); + @override + bool get isSafeToInterrupt => false; - // We want to run tests with `web_ui` as a working directory. - final dynamic backupCwd = io.Directory.current; - io.Directory.current = environment.webUiRootDir.path; - await test.main(testArgs); - io.Directory.current = backupCwd; + @override + Future interrupt() async {} - if (expectFailure) { - if (io.exitCode != 0) { - // It failed, as expected. - io.exitCode = 0; - } else { - io.stderr.writeln( - 'Tests ${testFiles.join(', ')} did not fail. Expected failure.', - ); - io.exitCode = 1; - } + @override + Future run() async { + if (!io.Platform.isWindows) { + // See: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences + print('\x1B[2J\x1B[1;2H'); } } } - -const List _kTestFonts = ['ahem.ttf', 'Roboto-Regular.ttf']; - -void _copyTestFontsIntoWebUi() { - final String fontsPath = path.join( - environment.flutterDirectory.path, - 'third_party', - 'txt', - 'third_party', - 'fonts', - ); - - for (String fontFile in _kTestFonts) { - final io.File sourceTtf = io.File(path.join(fontsPath, fontFile)); - final String destinationTtfPath = - path.join(environment.webUiRootDir.path, 'lib', 'assets', fontFile); - sourceTtf.copySync(destinationTtfPath); - } -} - -/// Used as an input message to the PoolResources that are building a test. -class TestBuildInput { - /// Test to build. - final FilePath path; - - /// Whether these tests should be build for CanvasKit. - /// - /// `-DFLUTTER_WEB_USE_SKIA=true` is passed to dart2js for CanvasKit. - final bool forCanvasKit; - - TestBuildInput(this.path, {this.forCanvasKit = false}); -} diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index 72a1b295a9e38..1dad1a754d0f9 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; +import 'dart:convert' show utf8, LineSplitter; import 'dart:io' as io; import 'package:args/command_runner.dart'; @@ -31,6 +31,9 @@ class FilePath { return other is FilePath && other._absolutePath == _absolutePath; } + @override + int get hashCode => _absolutePath.hashCode; + @override String toString() => _absolutePath; } @@ -39,75 +42,221 @@ class FilePath { Future runProcess( String executable, List arguments, { - String workingDirectory, - bool mustSucceed: false, + String? workingDirectory, + bool failureIsSuccess = false, Map environment = const {}, }) async { - final io.Process process = await io.Process.start( + final ProcessManager manager = await startProcess( executable, arguments, workingDirectory: workingDirectory, - // Running the process in a system shell for Windows. Otherwise - // the process is not able to get Dart from path. - runInShell: io.Platform.isWindows, - mode: io.ProcessStartMode.inheritStdio, + failureIsSuccess: failureIsSuccess, environment: environment, ); - final int exitCode = await process.exitCode; - if (mustSucceed && exitCode != 0) { - throw ProcessException( - description: 'Sub-process failed.', - executable: executable, - arguments: arguments, - workingDirectory: workingDirectory, - exitCode: exitCode, - ); - } - return exitCode; + return manager.wait(); } -/// Runs [executable]. Do not follow the exit code or the output. -Future startProcess( +/// Runs the process and returns its standard output as a string. +/// +/// Standard error output is ignored (use [ProcessManager.evalStderr] for that). +/// +/// Throws an exception if the process exited with a non-zero exit code. +Future evalProcess( String executable, List arguments, { - String workingDirectory, - bool mustSucceed: false, + String? workingDirectory, + Map environment = const {}, }) async { - final io.Process process = await io.Process.start( + final ProcessManager manager = await startProcess( executable, arguments, workingDirectory: workingDirectory, - // Running the process in a system shell for Windows. Otherwise - // the process is not able to get Dart from path. - runInShell: io.Platform.isWindows, - mode: io.ProcessStartMode.inheritStdio, + environment: environment, + evalOutput: true, ); - processesToCleanUp.add(process); + return manager.evalStdout(); } -/// Runs [executable] and returns its standard output as a string. +/// Starts a process using the [executable], passing it [arguments]. /// -/// If the process fails, throws a [ProcessException]. -Future evalProcess( +/// Returns a process manager that decorates the process with extra +/// functionality. See [ProcessManager] for what it can do. +/// +/// If [workingDirectory] is not null makes it the current working directory of +/// the process. Otherwise, the process inherits this processes working +/// directory. +/// +/// If [failureIsSuccess] is set to true, the returned [ProcessManager] treats +/// non-zero exit codes as success, and zero exit code as failure. +/// +/// If [evalOutput] is set to true, collects and decodes the process' standard +/// streams into in-memory strings. +Future startProcess( String executable, List arguments, { - String workingDirectory, + String? workingDirectory, + bool failureIsSuccess = false, + bool evalOutput = false, + Map environment = const {}, }) async { - final io.ProcessResult result = await io.Process.run( + final io.Process process = await io.Process.start( executable, arguments, workingDirectory: workingDirectory, + // Running the process in a system shell for Windows. Otherwise + // the process is not able to get Dart from path. + runInShell: io.Platform.isWindows, + // When [evalOutput] is false, we don't need to intercept the stdout of the + // sub-process. In this case, it's better to run the sub-process in the + // `inheritStdio` mode which lets it print directly to the terminal. + // This allows sub-processes such as `ninja` to use all kinds of terminal + // features like printing colors, printing progress on the same line, etc. + mode: evalOutput ? io.ProcessStartMode.normal : io.ProcessStartMode.inheritStdio, + environment: environment, ); - if (result.exitCode != 0) { + processesToCleanUp.add(process); + + return ProcessManager._( + executable: executable, + arguments: arguments, + workingDirectory: workingDirectory, + process: process, + evalOutput: evalOutput, + failureIsSuccess: failureIsSuccess, + ); +} + +/// Manages a process running outside `felt`. +class ProcessManager { + /// Creates a process manager that manages [process]. + ProcessManager._({ + required this.executable, + required this.arguments, + required this.workingDirectory, + required this.process, + required bool evalOutput, + required bool failureIsSuccess, + }) : _evalOutput = evalOutput, _failureIsSuccess = failureIsSuccess { + if (_evalOutput) { + _forwardStream(process.stdout, _stdout); + _forwardStream(process.stderr, _stderr); + } + } + + /// The executable, from which the process was spawned. + final String executable; + + /// The arguments passed to the prcess. + final List arguments; + + /// The current working directory (CWD) of the child process. + /// + /// If null, the child process inherits `felt`'s CWD. + final String? workingDirectory; + + /// The process being managed by this manager. + final io.Process process; + + /// Whether the standard output and standard error should be decoded into + /// strings while running the process. + final bool _evalOutput; + + /// Whether non-zero exit code is considered successful completion of the + /// process. + /// + /// See also [wait]. + final bool _failureIsSuccess; + + final StringBuffer _stdout = StringBuffer(); + final StringBuffer _stderr = StringBuffer(); + + void _forwardStream(Stream> stream, StringSink buffer) { + stream + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(buffer.writeln); + } + + /// Waits for the [process] to exit. Returns the exit code. + /// + /// The returned future completes successfully if: + /// + /// * [failureIsSuccess] is false and the process exited with exit code 0. + /// * [failureIsSuccess] is true and the process exited with a non-zero exit code. + /// + /// In all other cicumstances the future completes with an error. + Future wait() async { + final int exitCode = await process.exitCode; + if (!_failureIsSuccess && exitCode != 0) { + _throwProcessException( + description: 'Sub-process failed.', + exitCode: exitCode, + ); + } + return exitCode; + } + + /// If [evalOutput] is true, wait for the process to finish then returns the + /// decoded standard streams. + Future eval() async { + if (!_evalOutput) { + kill(); + _throwProcessException( + description: 'Cannot eval process output. The process was launched ' + 'with `evalOutput` set to false.', + ); + } + final int exitCode = await wait(); + return ProcessOutput( + exitCode: exitCode, + stdout: _stdout.toString(), + stderr: _stderr.toString(), + ); + } + + /// A convenience method on top of [eval] that only extracts standard output. + Future evalStdout() async { + return (await eval()).stdout; + } + + /// A convenience method on top of [eval] that only extracts standard error. + Future evalStderr() async { + return (await eval()).stderr; + } + + @alwaysThrows + void _throwProcessException({required String description, int? exitCode}) { throw ProcessException( - description: result.stderr as String, + description: description, executable: executable, arguments: arguments, workingDirectory: workingDirectory, - exitCode: result.exitCode, + exitCode: exitCode, ); } - return result.stdout as String; + + /// Kills the [process] by sending it the [signal]. + bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) { + return process.kill(signal); + } +} + +/// Stringified standard output and standard error streams from a process. +class ProcessOutput { + ProcessOutput({ + required this.exitCode, + required this.stdout, + required this.stderr, + }); + + /// The exit code of the process. + final int exitCode; + + /// Standard output of the process decoded as a string. + final String stdout; + + /// Standard error of the process decoded as a string. + final String stderr; } Future runFlutter( @@ -125,26 +274,36 @@ Future runFlutter( ); if (exitCode != 0) { - throw ToolException('ERROR: Failed to run $executable with ' - 'arguments ${arguments.toString()}. Exited with exit code $exitCode'); + throw ToolExit( + 'ERROR: Failed to run $executable with ' + 'arguments ${arguments.toString()}. Exited with exit code $exitCode', + exitCode: exitCode, + ); } } +/// An exception related to an attempt to spawn a sub-process. @immutable class ProcessException implements Exception { - ProcessException({ - @required this.description, - @required this.executable, - @required this.arguments, - @required this.workingDirectory, - @required this.exitCode, + const ProcessException({ + required this.description, + required this.executable, + required this.arguments, + required this.workingDirectory, + this.exitCode, }); final String description; final String executable; final List arguments; - final String workingDirectory; - final int exitCode; + final String? workingDirectory; + + /// The exit code of the process. + /// + /// The value is null if the exception is thrown before the process exits. + /// For example, this can happen on invalid attempts to start a process, or + /// when a process is stuck and is unable to exit. + final int? exitCode; @override String toString() { @@ -152,9 +311,10 @@ class ProcessException implements Exception { message ..writeln(description) ..writeln('Command: $executable ${arguments.join(' ')}') - ..writeln( - 'Working directory: ${workingDirectory ?? io.Directory.current.path}') - ..writeln('Exit code: $exitCode'); + ..writeln('Working directory: ${workingDirectory ?? io.Directory.current.path}'); + if (exitCode != null) { + message.writeln('Exit code: $exitCode'); + } return '$message'; } } @@ -162,39 +322,22 @@ class ProcessException implements Exception { /// Adds utility methods mixin ArgUtils on Command { /// Extracts a boolean argument from [argResults]. - bool boolArg(String name) => argResults[name] as bool; + bool boolArg(String name) => argResults![name] as bool; /// Extracts a string argument from [argResults]. - String stringArg(String name) => argResults[name] as String; - - /// Extracts a integer argument from [argResults]. - /// - /// If the argument value cannot be parsed as [int] throws an [ArgumentError]. - int intArg(String name) { - final String rawValue = stringArg(name); - if (rawValue == null) { - return null; - } - final int value = int.tryParse(rawValue); - if (value == null) { - throw ArgumentError( - 'Argument $name should be an integer value but was "$rawValue"', - ); - } - return value; - } + String stringArg(String name) => argResults![name] as String; } /// There might be proccesses started during the tests. /// /// Use this list to store those Processes, for cleaning up before shutdown. -final List processesToCleanUp = List(); +final List processesToCleanUp = []; /// There might be temporary directories created during the tests. /// /// Use this list to store those directories and for deleteing them before /// shutdown. -final List temporaryDirectories = List(); +final List temporaryDirectories = []; typedef AsyncCallback = Future Function(); @@ -202,19 +345,19 @@ typedef AsyncCallback = Future Function(); /// /// Add these operations here to make sure that they will run before felt /// exit. -final List cleanupCallbacks = List(); +final List cleanupCallbacks = []; /// Cleanup the remaning processes, close open browsers, delete temp files. -void cleanup() async { +Future cleanup() async { // Cleanup remaining processes if any. - if (processesToCleanUp.length > 0) { - for (io.Process process in processesToCleanUp) { + if (processesToCleanUp.isNotEmpty) { + for (final io.Process process in processesToCleanUp) { process.kill(); } } // Delete temporary directories. - if (temporaryDirectories.length > 0) { - for (io.Directory directory in temporaryDirectories) { + if (temporaryDirectories.isNotEmpty) { + for (final io.Directory directory in temporaryDirectories) { if (!directory.existsSync()) { directory.deleteSync(recursive: true); } @@ -225,3 +368,14 @@ void cleanup() async { await callback(); } } + +/// Scans the test/ directory for test files and returns them. +List findAllTests() { + return environment.webUiTestDir + .listSync(recursive: true) + .whereType() + .where((io.File f) => f.path.endsWith('_test.dart')) + .map((io.File f) => FilePath.fromWebUi( + path.relative(f.path, from: environment.webUiRootDir.path))) + .toList(); +} diff --git a/lib/web_ui/dev/web_engine_analysis.sh b/lib/web_ui/dev/web_engine_analysis.sh index 1ea919f499d18..d8f8c8456d760 100755 --- a/lib/web_ui/dev/web_engine_analysis.sh +++ b/lib/web_ui/dev/web_engine_analysis.sh @@ -2,7 +2,7 @@ set -e set -x -# web_analysis: a command-line utility for running dart analyzer on Flutter Web +# web_analysis: a command-line utility for running 'dart analyze' on Flutter Web # Engine. Used/Called by LUCI recipes: # # See: https://flutter.googlesource.com/recipes/+/refs/heads/master/recipes/web_engine.py @@ -10,11 +10,10 @@ set -x echo "Engine path $ENGINE_PATH" DART_SDK_DIR="${ENGINE_PATH}/src/out/host_debug_unopt/dart-sdk" -PUB_PATH="$DART_SDK_DIR/bin/pub" -DART_ANALYZER_PATH="$DART_SDK_DIR/bin/dartanalyzer" +DART_PATH="$DART_SDK_DIR/bin/dart" -echo "Running \`pub get\` in 'engine/src/flutter/lib/web_ui'" -(cd "$WEB_UI_DIR"; $PUB_PATH get) +echo "Running \`dart pub get\` in 'engine/src/flutter/lib/web_ui'" +(cd "$WEB_UI_DIR"; $DART_PATH pub get) -echo "Running \`dartanalyzer\` in 'engine/src/flutter/lib/web_ui'" -(cd "$WEB_UI_DIR"; $DART_ANALYZER_PATH --enable-experiment=non-nullable --fatal-warnings --fatal-hints dev/ lib/ test/ tool/) +echo "Running \`dart analyze\` in 'engine/src/flutter/lib/web_ui'" +(cd "$WEB_UI_DIR"; $DART_PATH analyze --fatal-infos) diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index eabb13c498959..50cc107c91e1a 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -2,143 +2,345 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 @JS() library engine; +// This file is transformed during the build process in order to make it a +// single library. Some notable transformations: +// +// 1. Imports of engine/* files are stripped out. +// 2. Exports of engine/* files are replaced with a part directive. +// +// The code that performs the transformations lives in: +// - https://github.com/flutter/engine/blob/master/web_sdk/sdk_rewriter.dart + import 'dart:async'; +// Some of these names are used in services/buffers.dart for example. +// ignore: unused_import import 'dart:collection' show ListBase, IterableBase, DoubleLinkedQueue, DoubleLinkedQueueEntry; +// ignore: unused_import import 'dart:convert' hide Codec; import 'dart:developer' as developer; import 'dart:html' as html; +// ignore: unused_import import 'dart:js' as js; +// ignore: unused_import import 'dart:js_util' as js_util; +// ignore: unused_import import 'dart:math' as math; import 'dart:typed_data'; import 'package:js/js.dart'; +// ignore: unused_import import 'package:meta/meta.dart'; import '../ui.dart' as ui; -part 'engine/alarm_clock.dart'; -part 'engine/assets.dart'; -part 'engine/bitmap_canvas.dart'; -part 'engine/browser_detection.dart'; -part 'engine/browser_location.dart'; -part 'engine/canvaskit/canvas.dart'; -part 'engine/canvaskit/canvaskit_canvas.dart'; -part 'engine/canvaskit/canvaskit_api.dart'; -part 'engine/canvaskit/color_filter.dart'; -part 'engine/canvaskit/embedded_views.dart'; -part 'engine/canvaskit/fonts.dart'; -part 'engine/canvaskit/image.dart'; -part 'engine/canvaskit/image_filter.dart'; -part 'engine/canvaskit/initialization.dart'; -part 'engine/canvaskit/layer.dart'; -part 'engine/canvaskit/layer_scene_builder.dart'; -part 'engine/canvaskit/layer_tree.dart'; -part 'engine/canvaskit/mask_filter.dart'; -part 'engine/canvaskit/n_way_canvas.dart'; -part 'engine/canvaskit/path.dart'; -part 'engine/canvaskit/painting.dart'; -part 'engine/canvaskit/path_metrics.dart'; -part 'engine/canvaskit/picture.dart'; -part 'engine/canvaskit/picture_recorder.dart'; -part 'engine/canvaskit/platform_message.dart'; -part 'engine/canvaskit/raster_cache.dart'; -part 'engine/canvaskit/rasterizer.dart'; -part 'engine/canvaskit/shader.dart'; -part 'engine/canvaskit/skia_object_cache.dart'; -part 'engine/canvaskit/surface.dart'; -part 'engine/canvaskit/text.dart'; -part 'engine/canvaskit/util.dart'; -part 'engine/canvaskit/vertices.dart'; -part 'engine/canvaskit/viewport_metrics.dart'; -part 'engine/canvas_pool.dart'; -part 'engine/clipboard.dart'; -part 'engine/color_filter.dart'; -part 'engine/dom_canvas.dart'; -part 'engine/dom_renderer.dart'; -part 'engine/engine_canvas.dart'; -part 'engine/frame_reference.dart'; -part 'engine/history.dart'; -part 'engine/html/backdrop_filter.dart'; -part 'engine/html/canvas.dart'; -part 'engine/html/clip.dart'; -part 'engine/html/color_filter.dart'; -part 'engine/html/debug_canvas_reuse_overlay.dart'; -part 'engine/html/image_filter.dart'; -part 'engine/html/offset.dart'; -part 'engine/html/opacity.dart'; -part 'engine/html/painting.dart'; -part 'engine/html/path/conic.dart'; -part 'engine/html/path/cubic.dart'; -part 'engine/html/path/path.dart'; -part 'engine/html/path/path_metrics.dart'; -part 'engine/html/path/path_ref.dart'; -part 'engine/html/path/path_to_svg.dart'; -part 'engine/html/path/path_utils.dart'; -part 'engine/html/path/path_windings.dart'; -part 'engine/html/path/tangent.dart'; -part 'engine/html/picture.dart'; -part 'engine/html/platform_view.dart'; -part 'engine/html/recording_canvas.dart'; -part 'engine/html/render_vertices.dart'; -part 'engine/html/scene.dart'; -part 'engine/html/scene_builder.dart'; -part 'engine/html/shader.dart'; -part 'engine/html/surface.dart'; -part 'engine/html/surface_stats.dart'; -part 'engine/html/transform.dart'; -part 'engine/html_image_codec.dart'; -part 'engine/keyboard.dart'; -part 'engine/mouse_cursor.dart'; -part 'engine/onscreen_logging.dart'; -part 'engine/picture.dart'; -part 'engine/platform_views.dart'; -part 'engine/plugins.dart'; -part 'engine/pointer_binding.dart'; -part 'engine/pointer_converter.dart'; -part 'engine/profiler.dart'; -part 'engine/rrect_renderer.dart'; -part 'engine/semantics/accessibility.dart'; -part 'engine/semantics/checkable.dart'; -part 'engine/semantics/image.dart'; -part 'engine/semantics/incrementable.dart'; -part 'engine/semantics/label_and_value.dart'; -part 'engine/semantics/live_region.dart'; -part 'engine/semantics/scrollable.dart'; -part 'engine/semantics/semantics.dart'; -part 'engine/semantics/semantics_helper.dart'; -part 'engine/semantics/tappable.dart'; -part 'engine/semantics/text_field.dart'; -part 'engine/services/buffers.dart'; -part 'engine/services/message_codec.dart'; -part 'engine/services/message_codecs.dart'; -part 'engine/services/serialization.dart'; -part 'engine/shadow.dart'; -part 'engine/test_embedding.dart'; -part 'engine/text/font_collection.dart'; -part 'engine/text/line_break_properties.dart'; -part 'engine/text/line_breaker.dart'; -part 'engine/text/measurement.dart'; -part 'engine/text/paragraph.dart'; -part 'engine/text/ruler.dart'; -part 'engine/text/unicode_range.dart'; -part 'engine/text/word_break_properties.dart'; -part 'engine/text/word_breaker.dart'; -part 'engine/text_editing/autofill_hint.dart'; -part 'engine/text_editing/input_type.dart'; -part 'engine/text_editing/text_capitalization.dart'; -part 'engine/text_editing/text_editing.dart'; -part 'engine/ulps.dart'; -part 'engine/util.dart'; -part 'engine/validators.dart'; -part 'engine/vector_math.dart'; -part 'engine/web_experiments.dart'; -part 'engine/window.dart'; +import 'engine/dom_renderer.dart'; +import 'engine/keyboard.dart'; +import 'engine/mouse_cursor.dart'; +import 'engine/navigation/js_url_strategy.dart'; +import 'engine/navigation/url_strategy.dart'; +import 'engine/platform_dispatcher.dart'; +import 'engine/platform_views/content_manager.dart'; +import 'engine/profiler.dart'; +import 'engine/web_experiments.dart'; +import 'engine/window.dart'; + +export 'engine/alarm_clock.dart'; + +export 'engine/assets.dart'; + +export 'engine/browser_detection.dart'; + +export 'engine/canvas_pool.dart'; + +export 'engine/canvaskit/canvas.dart'; + +export 'engine/canvaskit/canvaskit_api.dart'; + +export 'engine/canvaskit/canvaskit_canvas.dart'; + +export 'engine/canvaskit/color_filter.dart'; + +export 'engine/canvaskit/embedded_views.dart'; + +export 'engine/canvaskit/font_fallbacks.dart'; + +export 'engine/canvaskit/fonts.dart'; + +export 'engine/canvaskit/image.dart'; + +export 'engine/canvaskit/image_filter.dart'; + +export 'engine/canvaskit/initialization.dart'; + +export 'engine/canvaskit/interval_tree.dart'; + +export 'engine/canvaskit/layer.dart'; + +export 'engine/canvaskit/layer_scene_builder.dart'; + +export 'engine/canvaskit/layer_tree.dart'; + +export 'engine/canvaskit/mask_filter.dart'; + +export 'engine/canvaskit/n_way_canvas.dart'; + +export 'engine/canvaskit/painting.dart'; + +export 'engine/canvaskit/path.dart'; + +export 'engine/canvaskit/path_metrics.dart'; + +export 'engine/canvaskit/picture.dart'; + +export 'engine/canvaskit/picture_recorder.dart'; + +export 'engine/canvaskit/raster_cache.dart'; + +export 'engine/canvaskit/rasterizer.dart'; + +export 'engine/canvaskit/shader.dart'; + +export 'engine/canvaskit/skia_object_cache.dart'; + +export 'engine/canvaskit/surface.dart'; + +export 'engine/canvaskit/surface_factory.dart'; + +export 'engine/canvaskit/text.dart'; + +export 'engine/canvaskit/util.dart'; + +export 'engine/canvaskit/vertices.dart'; + +export 'engine/clipboard.dart'; + +export 'engine/color_filter.dart'; + +export 'engine/dom_renderer.dart'; + +export 'engine/engine_canvas.dart'; + +export 'engine/font_change_util.dart'; + +export 'engine/frame_reference.dart'; + +export 'engine/host_node.dart'; + +export 'engine/html/backdrop_filter.dart'; + +export 'engine/html/bitmap_canvas.dart'; + +export 'engine/html/canvas.dart'; + +export 'engine/html/clip.dart'; + +export 'engine/html/color_filter.dart'; + +export 'engine/html/debug_canvas_reuse_overlay.dart'; + +export 'engine/html/dom_canvas.dart'; + +export 'engine/html/image_filter.dart'; + +export 'engine/html/offscreen_canvas.dart'; + +export 'engine/html/offset.dart'; + +export 'engine/html/opacity.dart'; + +export 'engine/html/painting.dart'; + +export 'engine/html/path/conic.dart'; + +export 'engine/html/path/cubic.dart'; + +export 'engine/html/path/path.dart'; + +export 'engine/html/path/path_iterator.dart'; + +export 'engine/html/path/path_metrics.dart'; + +export 'engine/html/path/path_ref.dart'; + +export 'engine/html/path/path_to_svg.dart'; + +export 'engine/html/path/path_utils.dart'; + +export 'engine/html/path/path_windings.dart'; + +export 'engine/html/path/tangent.dart'; + +export 'engine/html/path_to_svg_clip.dart'; + +export 'engine/html/picture.dart'; + +export 'engine/html/platform_view.dart'; + +export 'engine/html/recording_canvas.dart'; + +export 'engine/html/render_vertices.dart'; + +export 'engine/html/scene.dart'; + +export 'engine/html/scene_builder.dart'; + +export 'engine/html/shader_mask.dart'; + +export 'engine/html/shaders/image_shader.dart'; + +export 'engine/html/shaders/normalized_gradient.dart'; + +export 'engine/html/shaders/shader.dart'; + +export 'engine/html/shaders/shader_builder.dart'; + +export 'engine/html/shaders/vertex_shaders.dart'; + +export 'engine/html/shaders/webgl_context.dart'; + +export 'engine/html/surface.dart'; + +export 'engine/html/surface_stats.dart'; + +export 'engine/html/transform.dart'; + +export 'engine/html_image_codec.dart'; + +export 'engine/key_map.dart'; + +export 'engine/keyboard.dart'; + +export 'engine/keyboard_binding.dart'; + +export 'engine/mouse_cursor.dart'; + +export 'engine/navigation/history.dart'; + +export 'engine/navigation/js_url_strategy.dart'; + +export 'engine/navigation/url_strategy.dart'; + +export 'engine/onscreen_logging.dart'; + +export 'engine/picture.dart'; + +export 'engine/platform_dispatcher.dart'; + +export 'engine/platform_views.dart'; + +export 'engine/platform_views/content_manager.dart'; + +export 'engine/platform_views/message_handler.dart'; + +export 'engine/platform_views/slots.dart'; + +export 'engine/plugins.dart'; + +export 'engine/pointer_binding.dart'; + +export 'engine/pointer_converter.dart'; + +export 'engine/profiler.dart'; + +export 'engine/rrect_renderer.dart'; + +export 'engine/semantics/accessibility.dart'; + +export 'engine/semantics/checkable.dart'; + +export 'engine/semantics/image.dart'; + +export 'engine/semantics/incrementable.dart'; + +export 'engine/semantics/label_and_value.dart'; + +export 'engine/semantics/live_region.dart'; + +export 'engine/semantics/scrollable.dart'; + +export 'engine/semantics/semantics.dart'; + +export 'engine/semantics/semantics_helper.dart'; + +export 'engine/semantics/tappable.dart'; + +export 'engine/semantics/text_field.dart'; + +export 'engine/services/buffers.dart'; + +export 'engine/services/message_codec.dart'; + +export 'engine/services/message_codecs.dart'; + +export 'engine/services/serialization.dart'; + +export 'engine/shadow.dart'; + +export 'engine/test_embedding.dart'; + +export 'engine/text/canvas_paragraph.dart'; + +export 'engine/text/font_collection.dart'; + +export 'engine/text/layout_service.dart'; + +export 'engine/text/line_break_properties.dart'; + +export 'engine/text/line_breaker.dart'; + +export 'engine/text/measurement.dart'; + +export 'engine/text/paint_service.dart'; + +export 'engine/text/paragraph.dart'; + +export 'engine/text/ruler.dart'; + +export 'engine/text/text_direction.dart'; + +export 'engine/text/unicode_range.dart'; + +export 'engine/text/word_break_properties.dart'; + +export 'engine/text/word_breaker.dart'; + +export 'engine/text_editing/autofill_hint.dart'; + +export 'engine/text_editing/input_type.dart'; + +export 'engine/text_editing/text_capitalization.dart'; + +export 'engine/text_editing/text_editing.dart'; + +export 'engine/util.dart'; + +export 'engine/validators.dart'; + +export 'engine/vector_math.dart'; + +export 'engine/web_experiments.dart'; + +export 'engine/window.dart'; + +/// The mode the app is running in. +/// Keep these in sync with the same constants on the framework-side under foundation/constants.dart. +const bool kReleaseMode = + bool.fromEnvironment('dart.vm.product', defaultValue: false); +/// A constant that is true if the application was compiled in profile mode. +const bool kProfileMode = + bool.fromEnvironment('dart.vm.profile', defaultValue: false); +/// A constant that is true if the application was compiled in debug mode. +const bool kDebugMode = !kReleaseMode && !kProfileMode; +/// Returns mode of the app is running in as a string. +String get buildMode => kReleaseMode + ? 'release' + : kProfileMode + ? 'profile' + : 'debug'; /// A benchmark metric that includes frame-related computations prior to /// submitting layer and picture operations to the underlying renderer, such as @@ -164,13 +366,15 @@ void registerHotRestartListener(ui.VoidCallback listener) { /// /// This is only available on the Web, as native Flutter configures the /// environment in the native embedder. -// TODO(yjbanov): we should refactor the code such that the framework does not -// call this method directly. void initializeEngine() { if (_engineInitialized) { return; } + // Setup the hook that allows users to customize URL strategy before running + // the app. + _addUrlStrategyListener(); + // Called by the Web runtime just before hot restarting the app. // // This extension cleans up resources that are registered with browser's @@ -179,7 +383,7 @@ void initializeEngine() { // This extension does not need to clean-up Dart statics. Those are cleaned // up by the compiler. developer.registerExtension('ext.flutter.disassemble', (_, __) { - for (ui.VoidCallback listener in _hotRestartListeners) { + for (final ui.VoidCallback listener in _hotRestartListeners) { listener(); } return Future.value( @@ -188,9 +392,8 @@ void initializeEngine() { _engineInitialized = true; - // Calling this getter to force the DOM renderer to initialize before we - // initialize framework bindings. - domRenderer; + // Initialize the DomRenderer before initializing framework bindings. + ensureDomRendererInitialized(); WebExperiments.ensureInitialized(); @@ -205,6 +408,8 @@ void initializeEngine() { if (!waitingForAnimation) { waitingForAnimation = true; html.window.requestAnimationFrame((num highResTime) { + frameTimingsOnVsync(); + // Reset immediately, because `frameHandler` can schedule more frames. waitingForAnimation = false; @@ -215,17 +420,24 @@ void initializeEngine() { // microsecond precision, and only then convert to `int`. final int highResTimeMicroseconds = (1000 * highResTime).toInt(); - if (window._onBeginFrame != null) { - window.invokeOnBeginFrame( + // In Flutter terminology "building a frame" consists of "beginning + // frame" and "drawing frame". + // + // We do not call `frameTimingsOnBuildFinish` from here because + // part of the rasterization process, particularly in the HTML + // renderer, takes place in the `SceneBuilder.build()`. + frameTimingsOnBuildStart(); + if (EnginePlatformDispatcher.instance.onBeginFrame != null) { + EnginePlatformDispatcher.instance.invokeOnBeginFrame( Duration(microseconds: highResTimeMicroseconds)); } - if (window._onDrawFrame != null) { + if (EnginePlatformDispatcher.instance.onDrawFrame != null) { // TODO(yjbanov): technically Flutter flushes microtasks between // onBeginFrame and onDrawFrame. We don't, which hasn't // been an issue yet, but eventually we'll have to // implement it properly. - window.invokeOnDrawFrame(); + EnginePlatformDispatcher.instance.invokeOnDrawFrame(); } }); } @@ -235,11 +447,28 @@ void initializeEngine() { MouseCursor.initialize(); } -class _NullTreeSanitizer implements html.NodeTreeSanitizer { +void _addUrlStrategyListener() { + jsSetUrlStrategy = allowInterop((JsUrlStrategy? jsStrategy) { + customUrlStrategy = + jsStrategy == null ? null : CustomUrlStrategy.fromJs(jsStrategy); + }); + registerHotRestartListener(() { + jsSetUrlStrategy = null; + }); +} + +/// Sanitizer used to convert const svg filter and clippath snippets to +/// SvgElement without sanitization. +class NullTreeSanitizer implements html.NodeTreeSanitizer { @override void sanitizeTree(html.Node node) {} } +/// The shared instance of PlatformViewManager shared across the engine to handle +/// rendering of PlatformViews into the web app. +// TODO(dit): How to make this overridable from tests? +final PlatformViewManager platformViewManager = PlatformViewManager(); + /// Converts a matrix represented using [Float64List] to one represented using /// [Float32List]. /// diff --git a/lib/web_ui/lib/src/engine/alarm_clock.dart b/lib/web_ui/lib/src/engine/alarm_clock.dart index 8308d725d2e06..900286e77dcc7 100644 --- a/lib/web_ui/lib/src/engine/alarm_clock.dart +++ b/lib/web_ui/lib/src/engine/alarm_clock.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; + +import 'package:ui/ui.dart' as ui; /// A function that returns current system time. typedef TimestampFunction = DateTime Function(); @@ -17,6 +18,8 @@ typedef TimestampFunction = DateTime Function(); /// /// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Notes class AlarmClock { + + /// Initializes Alarmclock with a closure that gets called with a timestamp. AlarmClock(TimestampFunction timestampFunction) : _timestampFunction = timestampFunction; diff --git a/lib/web_ui/lib/src/engine/assets.dart b/lib/web_ui/lib/src/engine/assets.dart index 326ab952c9b96..a1bbfcc5eb938 100644 --- a/lib/web_ui/lib/src/engine/assets.dart +++ b/lib/web_ui/lib/src/engine/assets.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'text/font_collection.dart'; +import 'util.dart'; /// This class downloads assets over the network. /// @@ -15,6 +19,7 @@ class AssetManager { /// The directory containing the assets. final String assetsDir; + /// Initializes [AssetManager] with path to assets relative to baseUrl. const AssetManager({this.assetsDir = _defaultAssetsDir}); String? get _baseUrl { @@ -49,35 +54,39 @@ class AssetManager { return Uri.encodeFull((_baseUrl ?? '') + '$assetsDir/$asset'); } + /// Loads an asset using an [html.HttpRequest] and returns data as [ByteData]. Future load(String asset) async { final String url = getAssetUrl(asset); try { final html.HttpRequest request = await html.HttpRequest.request(url, responseType: 'arraybuffer'); - final ByteBuffer response = request.response; + final ByteBuffer response = request.response as ByteBuffer; return response.asByteData(); } on html.ProgressEvent catch (e) { final html.EventTarget? target = e.target; if (target is html.HttpRequest) { if (target.status == 404 && asset == 'AssetManifest.json') { - html.window.console - .warn('Asset manifest does not exist at `$url` – ignoring.'); + printWarning('Asset manifest does not exist at `$url` – ignoring.'); return Uint8List.fromList(utf8.encode('{}')).buffer.asByteData(); } throw AssetManagerException(url, target.status!); } - html.window.console.warn('Caught ProgressEvent with target: $target'); + printWarning('Caught ProgressEvent with target: $target'); rethrow; } } } +/// Thrown to indicate http failure during asset loading. class AssetManagerException implements Exception { + /// Http request url for asset. final String url; + /// Http status of of response. final int httpStatus; + /// Initializes exception with request url and http status. AssetManagerException(this.url, this.httpStatus); @override @@ -86,9 +95,22 @@ class AssetManagerException implements Exception { /// An asset manager that gives fake empty responses for assets. class WebOnlyMockAssetManager implements AssetManager { + /// Mock asset directory relative to base url. String defaultAssetsDir = ''; + /// Mock empty asset manifest. String defaultAssetManifest = '{}'; - String defaultFontManifest = '[]'; + /// Mock font manifest overridable for unit testing. + String defaultFontManifest = ''' + [ + { + "family":"$robotoFontFamily", + "fonts":[{"asset":"$robotoTestFontUrl"}] + }, + { + "family":"$ahemFontFamily", + "fonts":[{"asset":"$ahemFontUrl"}] + } + ]'''; @override String get assetsDir => defaultAssetsDir; @@ -97,7 +119,7 @@ class WebOnlyMockAssetManager implements AssetManager { String get _baseUrl => ''; @override - String getAssetUrl(String asset) => '$asset'; + String getAssetUrl(String asset) => asset; @override Future load(String asset) { diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart deleted file mode 100644 index 0f054559b18a0..0000000000000 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ /dev/null @@ -1,968 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.10 -part of engine; - -/// A raw HTML canvas that is directly written to. -class BitmapCanvas extends EngineCanvas { - /// The rectangle positioned relative to the parent layer's coordinate - /// system's origin, within which this canvas paints. - /// - /// Painting outside these bounds will result in cropping. - ui.Rect get bounds => _bounds; - set bounds(ui.Rect newValue) { - assert(newValue != null); // ignore: unnecessary_null_comparison - _bounds = newValue; - final int newCanvasPositionX = _bounds.left.floor() - kPaddingPixels; - final int newCanvasPositionY = _bounds.top.floor() - kPaddingPixels; - if (_canvasPositionX != newCanvasPositionX || - _canvasPositionY != newCanvasPositionY) { - _canvasPositionX = newCanvasPositionX; - _canvasPositionY = newCanvasPositionY; - _updateRootElementTransform(); - } - } - - ui.Rect _bounds; - CrossFrameCache? _elementCache; - - /// The amount of padding to add around the edges of this canvas to - /// ensure that anti-aliased arcs are not clipped. - static const int kPaddingPixels = 1; - - @override - final html.Element rootElement = html.Element.tag('flt-canvas'); - - final _CanvasPool _canvasPool; - - /// The size of the paint [bounds]. - ui.Size get size => _bounds.size; - - /// The last paragraph style is cached to optimize the case where the style - /// hasn't changed. - ParagraphGeometricStyle? _cachedLastStyle; - - /// List of extra sibling elements created for paragraphs and clipping. - final List _children = []; - - /// The number of pixels along the width of the bitmap that the canvas element - /// renders into. - /// - /// These pixels are different from the logical CSS pixels. Here a pixel - /// literally means 1 point with a RGBA color. - final int _widthInBitmapPixels; - - /// The number of pixels along the width of the bitmap that the canvas element - /// renders into. - /// - /// These pixels are different from the logical CSS pixels. Here a pixel - /// literally means 1 point with a RGBA color. - final int _heightInBitmapPixels; - - /// The number of pixels in the bitmap that the canvas element renders into. - /// - /// These pixels are different from the logical CSS pixels. Here a pixel - /// literally means 1 point with a RGBA color. - int get bitmapPixelCount => _widthInBitmapPixels * _heightInBitmapPixels; - - int _saveCount = 0; - - /// Keeps track of what device pixel ratio was used when this [BitmapCanvas] - /// was created. - final double _devicePixelRatio = EngineWindow.browserDevicePixelRatio; - - // Compensation for [_initializeViewport] snapping canvas position to 1 pixel. - int? _canvasPositionX, _canvasPositionY; - - // Indicates the instructions following drawImage or drawParagraph that - // a child element was created to paint. - // TODO(flutter_web): When childElements are created by - // drawImage/drawParagraph commands, compositing order is not correctly - // handled when we interleave these with other paint commands. - // To solve this, recording canvas will have to check the paint queue - // and send a hint to EngineCanvas that additional canvas layers need - // to be used to composite correctly. In practice this is very rare - // with Widgets but CustomPainter(s) can hit this code path. - bool _childOverdraw = false; - - /// Forces text to be drawn using HTML rather than bitmap. - /// - /// Use this for tests only. - set debugChildOverdraw(bool value) { - _childOverdraw = value; - } - - /// Allocates a canvas with enough memory to paint a picture within the given - /// [bounds]. - /// - /// This canvas can be reused by pictures with different paint bounds as long - /// as the [Rect.size] of the bounds fully fit within the size used to - /// initialize this canvas. - BitmapCanvas(this._bounds) - : assert(_bounds != null), // ignore: unnecessary_null_comparison - _widthInBitmapPixels = _widthToPhysical(_bounds.width), - _heightInBitmapPixels = _heightToPhysical(_bounds.height), - _canvasPool = _CanvasPool(_widthToPhysical(_bounds.width), - _heightToPhysical(_bounds.height)) { - rootElement.style.position = 'absolute'; - // Adds one extra pixel to the requested size. This is to compensate for - // _initializeViewport() snapping canvas position to 1 pixel, causing - // painting to overflow by at most 1 pixel. - _canvasPositionX = _bounds.left.floor() - kPaddingPixels; - _canvasPositionY = _bounds.top.floor() - kPaddingPixels; - _updateRootElementTransform(); - _canvasPool.allocateCanvas(rootElement as html.HtmlElement); - _setupInitialTransform(); - } - - /// Setup cache for reusing DOM elements across frames. - void setElementCache(CrossFrameCache cache) { - _elementCache = cache; - } - - void _updateRootElementTransform() { - // Flutter emits paint operations positioned relative to the parent layer's - // coordinate system. However, canvas' coordinate system's origin is always - // in the top-left corner of the canvas. We therefore need to inject an - // initial translation so the paint operations are positioned as expected. - // - // The flooring of the value is to ensure that canvas' top-left corner - // lands on the physical pixel. TODO: !This is not accurate if there are - // transforms higher up in the stack. - rootElement.style.transform = - 'translate(${_canvasPositionX}px, ${_canvasPositionY}px)'; - } - - void _setupInitialTransform() { - final double canvasPositionCorrectionX = _bounds.left - - BitmapCanvas.kPaddingPixels - - _canvasPositionX!.toDouble(); - final double canvasPositionCorrectionY = - _bounds.top - BitmapCanvas.kPaddingPixels - _canvasPositionY!.toDouble(); - // This compensates for the translate on the `rootElement`. - _canvasPool.initialTransform = ui.Offset( - -_bounds.left + canvasPositionCorrectionX + BitmapCanvas.kPaddingPixels, - -_bounds.top + canvasPositionCorrectionY + BitmapCanvas.kPaddingPixels, - ); - } - - static int _widthToPhysical(double width) { - final double boundsWidth = width + 1; - return (boundsWidth * EngineWindow.browserDevicePixelRatio).ceil() + - 2 * kPaddingPixels; - } - - static int _heightToPhysical(double height) { - final double boundsHeight = height + 1; - return (boundsHeight * EngineWindow.browserDevicePixelRatio).ceil() + - 2 * kPaddingPixels; - } - - // Used by picture to assess if canvas is large enough to reuse as is. - bool doesFitBounds(ui.Rect newBounds) { - assert(newBounds != null); // ignore: unnecessary_null_comparison - return _widthInBitmapPixels >= _widthToPhysical(newBounds.width) && - _heightInBitmapPixels >= _heightToPhysical(newBounds.height); - } - - @override - void dispose() { - _canvasPool.dispose(); - } - - /// Prepare to reuse this canvas by clearing it's current contents. - @override - void clear() { - _canvasPool.clear(); - final int len = _children.length; - for (int i = 0; i < len; i++) { - html.Element child = _children[i]; - // Don't remove children that have been reused by CrossFrameCache. - if (child.parent == rootElement) { - child.remove(); - } - } - _children.clear(); - _cachedLastStyle = null; - _setupInitialTransform(); - } - - /// Checks whether this [BitmapCanvas] can still be recycled and reused. - /// - /// See also: - /// - /// * [PersistedPicture._applyBitmapPaint] which uses this method to - /// decide whether to reuse this canvas or not. - /// * [PersistedPicture._recycleCanvas] which also uses this method - /// for the same reason. - bool isReusable() { - return _devicePixelRatio == EngineWindow.browserDevicePixelRatio; - } - - /// Returns a "data://" URI containing a representation of the image in this - /// canvas in PNG format. - String toDataUrl() { - return _canvasPool.toDataUrl(); - } - - /// Sets the global paint styles to correspond to [paint]. - void _setUpPaint(SurfacePaintData paint) { - _canvasPool.contextHandle.setUpPaint(paint); - } - - void _tearDownPaint() { - _canvasPool.contextHandle.tearDownPaint(); - } - - @override - int save() { - _canvasPool.save(); - return _saveCount++; - } - - void saveLayer(ui.Rect bounds, ui.Paint paint) { - save(); - } - - @override - void restore() { - _canvasPool.restore(); - _saveCount--; - _cachedLastStyle = null; - } - - // TODO(yjbanov): not sure what this is attempting to do, but it is probably - // wrong because some clips and transforms are expressed using - // HTML DOM elements. - void restoreToCount(int count) { - assert(_saveCount >= count); - final int restores = _saveCount - count; - for (int i = 0; i < restores; i++) { - _canvasPool.restore(); - } - _saveCount = count; - } - - @override - void translate(double dx, double dy) { - _canvasPool.translate(dx, dy); - } - - @override - void scale(double sx, double sy) { - _canvasPool.scale(sx, sy); - } - - @override - void rotate(double radians) { - _canvasPool.rotate(radians); - } - - @override - void skew(double sx, double sy) { - _canvasPool.skew(sx, sy); - } - - @override - void transform(Float32List matrix4) { - _canvasPool.transform(matrix4); - } - - @override - void clipRect(ui.Rect rect) { - _canvasPool.clipRect(rect); - } - - @override - void clipRRect(ui.RRect rrect) { - _canvasPool.clipRRect(rrect); - } - - @override - void clipPath(ui.Path path) { - _canvasPool.clipPath(path); - } - - @override - void drawColor(ui.Color color, ui.BlendMode blendMode) { - _canvasPool.drawColor(color, blendMode); - } - - @override - void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.strokeLine(p1, p2); - _tearDownPaint(); - } - - @override - void drawPaint(SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.fill(); - _tearDownPaint(); - } - - @override - void drawRect(ui.Rect rect, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.drawRect(rect, paint.style); - _tearDownPaint(); - } - - @override - void drawRRect(ui.RRect rrect, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.drawRRect(rrect, paint.style); - _tearDownPaint(); - } - - @override - void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.drawDRRect(outer, inner, paint.style); - _tearDownPaint(); - } - - @override - void drawOval(ui.Rect rect, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.drawOval(rect, paint.style); - _tearDownPaint(); - } - - @override - void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.drawCircle(c, radius, paint.style); - _tearDownPaint(); - } - - @override - void drawPath(ui.Path path, SurfacePaintData paint) { - _setUpPaint(paint); - _canvasPool.drawPath(path, paint.style); - _tearDownPaint(); - } - - @override - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { - _canvasPool.drawShadow(path, color, elevation, transparentOccluder); - } - - @override - void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { - final html.HtmlElement imageElement = _drawImage(image, p, paint); - if (paint.colorFilter != null) { - _applyTargetSize(imageElement, image.width.toDouble(), - image.height.toDouble()); - } - _childOverdraw = true; - _canvasPool.closeCurrentCanvas(); - _cachedLastStyle = null; - } - - html.ImageElement _reuseOrCreateImage(HtmlImage htmlImage) { - final String cacheKey = htmlImage.imgElement.src!; - if (_elementCache != null) { - html.ImageElement? imageElement = _elementCache!.reuse(cacheKey) as html.ImageElement?; - if (imageElement != null) { - return imageElement; - } - } - // Can't reuse, create new instance. - html.ImageElement newImageElement = htmlImage.cloneImageElement(); - if (_elementCache != null) { - _elementCache!.cache(cacheKey, newImageElement, _onEvictElement); - } - return newImageElement; - } - - static void _onEvictElement(html.HtmlElement element) { - element.remove(); - } - - html.HtmlElement _drawImage( - ui.Image image, ui.Offset p, SurfacePaintData paint) { - final HtmlImage htmlImage = image as HtmlImage; - final ui.BlendMode? blendMode = paint.blendMode; - final EngineColorFilter? colorFilter = paint.colorFilter as EngineColorFilter?; - final ui.BlendMode? colorFilterBlendMode = colorFilter?._blendMode; - html.HtmlElement imgElement; - if (colorFilterBlendMode == null) { - // No Blending, create an image by cloning original loaded image. - imgElement = _reuseOrCreateImage(htmlImage); - } else { - switch (colorFilterBlendMode) { - case ui.BlendMode.colorBurn: - case ui.BlendMode.colorDodge: - case ui.BlendMode.hue: - case ui.BlendMode.modulate: - case ui.BlendMode.overlay: - case ui.BlendMode.plus: - case ui.BlendMode.srcIn: - case ui.BlendMode.srcATop: - case ui.BlendMode.srcOut: - case ui.BlendMode.saturation: - case ui.BlendMode.color: - case ui.BlendMode.luminosity: - case ui.BlendMode.xor: - imgElement = _createImageElementWithSvgFilter(image, - colorFilter!._color, colorFilterBlendMode, paint); - break; - default: - imgElement = _createBackgroundImageWithBlend(image, - colorFilter!._color, colorFilterBlendMode, paint); - break; - } - } - imgElement.style.mixBlendMode = _stringForBlendMode(blendMode) ?? ''; - if (_canvasPool.isClipped) { - // Reset width/height since they may have been previously set. - imgElement.style - ..removeProperty('width') - ..removeProperty('height'); - final List clipElements = _clipContent( - _canvasPool._clipStack!, imgElement, p, _canvasPool.currentTransform); - for (html.Element clipElement in clipElements) { - rootElement.append(clipElement); - _children.add(clipElement); - } - } else { - final String cssTransform = float64ListToCssTransform( - transformWithOffset(_canvasPool.currentTransform, p).storage); - imgElement.style - ..transformOrigin = '0 0 0' - ..transform = cssTransform - // Reset width/height since they may have been previously set. - ..removeProperty('width') - ..removeProperty('height'); - rootElement.append(imgElement); - _children.add(imgElement); - } - return imgElement; - } - - @override - void drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { - final bool requiresClipping = src.left != 0 || - src.top != 0 || - src.width != image.width || - src.height != image.height; - // If source and destination sizes are identical, we can skip the longer - // code path that sets the size of the element and clips. - // - // If there is a color filter set however, we maybe using background-image - // to render therefore we have to explicitely set width/height of the - // element for blending to work with background-color. - if (dst.width == image.width && - dst.height == image.height && - !requiresClipping && - paint.colorFilter == null) { - _drawImage(image, dst.topLeft, paint); - } else { - if (requiresClipping) { - save(); - clipRect(dst); - } - double targetLeft = dst.left; - double targetTop = dst.top; - if (requiresClipping) { - if (src.width != image.width) { - double leftMargin = -src.left * (dst.width / src.width); - targetLeft += leftMargin; - } - if (src.height != image.height) { - double topMargin = -src.top * (dst.height / src.height); - targetTop += topMargin; - } - } - - final html.Element imgElement = - _drawImage(image, ui.Offset(targetLeft, targetTop), paint); - // To scale set width / height on destination image. - // For clipping we need to scale according to - // clipped-width/full image width and shift it according to left/top of - // source rectangle. - double targetWidth = dst.width; - double targetHeight = dst.height; - if (requiresClipping) { - targetWidth *= image.width / src.width; - targetHeight *= image.height / src.height; - } - _applyTargetSize(imgElement as html.HtmlElement, targetWidth, targetHeight); - if (requiresClipping) { - restore(); - } - } - _closeCurrentCanvas(); - } - - void _applyTargetSize(html.HtmlElement imageElement, double targetWidth, - double targetHeight) { - final html.CssStyleDeclaration imageStyle = imageElement.style; - final String widthPx = '${targetWidth.toStringAsFixed(2)}px'; - final String heightPx = '${targetHeight.toStringAsFixed(2)}px'; - imageStyle - // left,top are set to 0 (although position is absolute) because - // Chrome will glitch if you leave them out, reproducable with - // canvas_image_blend_test on row 6, MacOS / Chrome 81.04. - ..left = "0px" - ..top = "0px" - ..width = widthPx - ..height = heightPx; - if (imageElement is! html.ImageElement) { - imageElement.style.backgroundSize = '$widthPx $heightPx'; - } - } - - // Creates a Div element to render an image using background-image css - // attribute to be able to use background blend mode(s) when possible. - // - // Example:
- // - // Special cases: - // For clear,dstOut it generates a blank element. - // For src,srcOver it only sets background-color attribute. - // For dst,dstIn , it only sets source not background color. - html.HtmlElement _createBackgroundImageWithBlend(HtmlImage image, - ui.Color? filterColor, ui.BlendMode colorFilterBlendMode, - SurfacePaintData paint) { - // When blending with color we can't use an image element. - // Instead use a div element with background image, color and - // background blend mode. - final html.HtmlElement imgElement = html.DivElement(); - final html.CssStyleDeclaration style = imgElement.style; - switch (colorFilterBlendMode) { - case ui.BlendMode.clear: - case ui.BlendMode.dstOut: - style.position = 'absolute'; - break; - case ui.BlendMode.src: - case ui.BlendMode.srcOver: - style - ..position = 'absolute' - ..backgroundColor = colorToCssString(filterColor); - break; - case ui.BlendMode.dst: - case ui.BlendMode.dstIn: - style - ..position = 'absolute' - ..backgroundImage = "url('${image.imgElement.src}')"; - break; - default: - style - ..position = 'absolute' - ..backgroundImage = "url('${image.imgElement.src}')" - ..backgroundBlendMode = _stringForBlendMode(colorFilterBlendMode) ?? '' - ..backgroundColor = colorToCssString(filterColor); - break; - } - return imgElement; - } - - // Creates an image element and an svg filter to apply on the element. - html.HtmlElement _createImageElementWithSvgFilter(HtmlImage image, - ui.Color? filterColor, ui.BlendMode colorFilterBlendMode, - SurfacePaintData paint) { - // For srcIn blendMode, we use an svg filter to apply to image element. - String? svgFilter = svgFilterFromBlendMode(filterColor, - colorFilterBlendMode); - final html.Element filterElement = - html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer()); - rootElement.append(filterElement); - _children.add(filterElement); - final html.HtmlElement imgElement = _reuseOrCreateImage(image); - imgElement.style.filter = 'url(#_fcf${_filterIdCounter})'; - if (colorFilterBlendMode == ui.BlendMode.saturation) { - imgElement.style.backgroundColor = colorToCssString(filterColor); - } - return imgElement; - } - - // Should be called when we add new html elements into rootElement so that - // paint order is preserved. - // - // For example if we draw a path and then a paragraph and image: - // - rootElement - // |--- - // |---

- // |--- - // Any drawing operations after these tags should allocate a new canvas, - // instead of drawing into earlier canvas. - void _closeCurrentCanvas() { - _canvasPool.closeCurrentCanvas(); - _childOverdraw = true; - } - - void _drawTextLine( - ParagraphGeometricStyle style, - EngineLineMetrics line, - double x, - double y, - ) { - html.CanvasRenderingContext2D ctx = _canvasPool.context; - x += line.left; - final double? letterSpacing = style.letterSpacing; - if (letterSpacing == null || letterSpacing == 0.0) { - ctx.fillText(line.displayText!, x, y); - } else { - // When letter-spacing is set, we go through a more expensive code path - // that renders each character separately with the correct spacing - // between them. - // - // We are drawing letter spacing like the web does it, by adding the - // spacing after each letter. This is different from Flutter which puts - // the spacing around each letter i.e. for a 10px letter spacing, Flutter - // would put 5px before each letter and 5px after it, but on the web, we - // put no spacing before the letter and 10px after it. This is how the DOM - // does it. - final int len = line.displayText!.length; - for (int i = 0; i < len; i++) { - final String char = line.displayText![i]; - ctx.fillText(char, x, y); - x += letterSpacing + ctx.measureText(char).width!; - } - } - } - - @override - void drawParagraph(EngineParagraph paragraph, ui.Offset offset) { - assert(paragraph._isLaidOut); - 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; - if (backgroundPaint != null) { - final ui.Rect rect = ui.Rect.fromLTWH( - offset.dx, offset.dy, paragraph.width, paragraph.height); - drawRect(rect, backgroundPaint); - } - - if (style != _cachedLastStyle) { - html.CanvasRenderingContext2D ctx = _canvasPool.context; - ctx.font = style.cssFontString; - _cachedLastStyle = style; - } - _setUpPaint(paragraph._paint!.paintData); - double y = offset.dy + paragraph.alphabeticBaseline; - final int len = lines.length; - for (int i = 0; i < len; i++) { - _drawTextLine(style, lines[i], offset.dx, y); - y += paragraph._lineHeight; - } - _tearDownPaint(); - - return; - } - - final html.Element paragraphElement = - _drawParagraphElement(paragraph, offset); - if (_canvasPool.isClipped) { - final List clipElements = _clipContent( - _canvasPool._clipStack!, - paragraphElement as html.HtmlElement, - offset, - _canvasPool.currentTransform); - for (html.Element clipElement in clipElements) { - rootElement.append(clipElement); - _children.add(clipElement); - } - } else { - setElementTransform( - paragraphElement, - transformWithOffset(_canvasPool.currentTransform, offset).storage, - ); - rootElement.append(paragraphElement); - } - _children.add(paragraphElement); - // If there is a prior sibling such as img prevent left/top shift. - paragraphElement.style - ..left = "0px" - ..top = "0px"; - _closeCurrentCanvas(); - } - - /// Paints the [picture] into this canvas. - void drawPicture(ui.Picture picture) { - final EnginePicture enginePicture = picture as EnginePicture; - enginePicture.recordingCanvas!.apply(this, bounds); - } - - /// Draws vertices on a gl context. - /// - /// If both colors and textures is specified in paint data, - /// for [BlendMode.source] we skip colors and use textures, - /// for [BlendMode.dst] we only use colors and ignore textures. - /// We also skip paint shader when no texture is specified. - /// - /// If no colors or textures are specified, stroke hairlines with - /// [Paint.color]. - /// - /// If colors is specified, convert colors to premultiplied (alpha) colors - /// and use a SkTriColorShader to render. - @override - void drawVertices( - SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) { - // TODO(flutter_web): Implement shaders for [Paint.shader] and - // blendMode. https://github.com/flutter/flutter/issues/40096 - // Move rendering to OffscreenCanvas so that transform is preserved - // as well. - assert(paint.shader == null, - 'Linear/Radial/SweepGradient and ImageShader not supported yet'); - final Int32List? colors = vertices._colors; - final ui.VertexMode mode = vertices._mode; - html.CanvasRenderingContext2D? ctx = _canvasPool.context; - if (colors == null) { - final Float32List positions = mode == ui.VertexMode.triangles - ? vertices._positions - : _convertVertexPositions(mode, vertices._positions); - // Draw hairline for vertices if no vertex colors are specified. - save(); - final ui.Color color = paint.color ?? ui.Color(0xFF000000); - _canvasPool.contextHandle - ..fillStyle = null - ..strokeStyle = colorToCssString(color); - _glRenderer!.drawHairline(ctx, positions); - restore(); - return; - } - _glRenderer!.drawVertices(ctx, _widthInBitmapPixels, _heightInBitmapPixels, - _canvasPool.currentTransform, vertices, blendMode, paint); - } - - /// Stores paint data used by [drawPoints]. We cannot use the original paint - /// data object because painting style is determined by [ui.PointMode] and - /// not by [SurfacePointData.style]. - static SurfacePaintData _drawPointsPaint = SurfacePaintData() - ..strokeCap = ui.StrokeCap.round - ..strokeJoin = ui.StrokeJoin.round - ..blendMode = ui.BlendMode.srcOver; - - @override - void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { - if (pointMode == ui.PointMode.points) { - _drawPointsPaint.style = ui.PaintingStyle.stroke; - } else { - _drawPointsPaint.style = ui.PaintingStyle.fill; - } - _drawPointsPaint.color = paint.color; - _drawPointsPaint.strokeWidth = paint.strokeWidth; - _drawPointsPaint.maskFilter = paint.maskFilter; - - _setUpPaint(_drawPointsPaint); - _canvasPool.drawPoints(pointMode, points, paint.strokeWidth! / 2.0); - _tearDownPaint(); - } - - @override - void endOfPaint() { - _canvasPool.endOfPaint(); - _elementCache?.commitFrame(); - } -} - -String? _stringForBlendMode(ui.BlendMode? blendMode) { - if (blendMode == null) { - return null; - } - switch (blendMode) { - case ui.BlendMode.srcOver: - return 'source-over'; - case ui.BlendMode.srcIn: - return 'source-in'; - case ui.BlendMode.srcOut: - return 'source-out'; - case ui.BlendMode.srcATop: - return 'source-atop'; - case ui.BlendMode.dstOver: - return 'destination-over'; - case ui.BlendMode.dstIn: - return 'destination-in'; - case ui.BlendMode.dstOut: - return 'destination-out'; - case ui.BlendMode.dstATop: - return 'destination-atop'; - case ui.BlendMode.plus: - return 'lighten'; - case ui.BlendMode.src: - return 'copy'; - case ui.BlendMode.xor: - return 'xor'; - case ui.BlendMode.multiply: - // Falling back to multiply, ignoring alpha channel. - // TODO(flutter_web): only used for debug, find better fallback for web. - case ui.BlendMode.modulate: - return 'multiply'; - case ui.BlendMode.screen: - return 'screen'; - case ui.BlendMode.overlay: - return 'overlay'; - case ui.BlendMode.darken: - return 'darken'; - case ui.BlendMode.lighten: - return 'lighten'; - case ui.BlendMode.colorDodge: - return 'color-dodge'; - case ui.BlendMode.colorBurn: - return 'color-burn'; - case ui.BlendMode.hardLight: - return 'hard-light'; - case ui.BlendMode.softLight: - return 'soft-light'; - case ui.BlendMode.difference: - return 'difference'; - case ui.BlendMode.exclusion: - return 'exclusion'; - case ui.BlendMode.hue: - return 'hue'; - case ui.BlendMode.saturation: - return 'saturation'; - case ui.BlendMode.color: - return 'color'; - case ui.BlendMode.luminosity: - return 'luminosity'; - default: - throw UnimplementedError( - 'Flutter Web does not support the blend mode: $blendMode'); - } -} - -String? _stringForStrokeCap(ui.StrokeCap? strokeCap) { - if (strokeCap == null) { - return null; - } - switch (strokeCap) { - case ui.StrokeCap.butt: - return 'butt'; - case ui.StrokeCap.round: - return 'round'; - case ui.StrokeCap.square: - default: - return 'square'; - } -} - -String _stringForStrokeJoin(ui.StrokeJoin strokeJoin) { - assert(strokeJoin != null); // ignore: unnecessary_null_comparison - switch (strokeJoin) { - case ui.StrokeJoin.round: - return 'round'; - case ui.StrokeJoin.bevel: - return 'bevel'; - case ui.StrokeJoin.miter: - default: - return 'miter'; - } -} - -/// Clips the content element against a stack of clip operations and returns -/// root of a tree that contains content node. -/// -/// The stack of clipping rectangles generate an element that either uses -/// overflow:hidden with bounds to clip child or sets a clip-path to clip -/// it's contents. The clipping rectangles are nested and returned together -/// with a list of svg elements that provide clip-paths. -List _clipContent(List<_SaveClipEntry> clipStack, - html.HtmlElement content, ui.Offset offset, Matrix4 currentTransform) { - html.Element? root, curElement; - final List clipDefs = []; - final int len = clipStack.length; - for (int clipIndex = 0; clipIndex < len; clipIndex++) { - final _SaveClipEntry entry = clipStack[clipIndex]; - final html.HtmlElement newElement = html.DivElement(); - newElement.style.position = 'absolute'; - applyWebkitClipFix(newElement); - if (root == null) { - root = newElement; - } else { - domRenderer.append(curElement!, newElement); - } - curElement = newElement; - final ui.Rect? rect = entry.rect; - Matrix4 newClipTransform = entry.currentTransform; - if (rect != null) { - final double clipOffsetX = rect.left; - final double clipOffsetY = rect.top; - newClipTransform = newClipTransform.clone() - ..translate(clipOffsetX, clipOffsetY); - curElement.style - ..overflow = 'hidden' - ..width = '${rect.right - clipOffsetX}px' - ..height = '${rect.bottom - clipOffsetY}px'; - setElementTransform(curElement, newClipTransform.storage); - } else if (entry.rrect != null) { - final ui.RRect roundRect = entry.rrect!; - final String borderRadius = - '${roundRect.tlRadiusX}px ${roundRect.trRadiusX}px ' - '${roundRect.brRadiusX}px ${roundRect.blRadiusX}px'; - final double clipOffsetX = roundRect.left; - final double clipOffsetY = roundRect.top; - newClipTransform = newClipTransform.clone() - ..translate(clipOffsetX, clipOffsetY); - curElement.style - ..borderRadius = borderRadius - ..overflow = 'hidden' - ..width = '${roundRect.right - clipOffsetX}px' - ..height = '${roundRect.bottom - clipOffsetY}px'; - setElementTransform(curElement, newClipTransform.storage); - } else if (entry.path != null) { - curElement.style - ..transform = matrix4ToCssTransform(newClipTransform) - ..transformOrigin = '0 0 0'; - String svgClipPath = createSvgClipDef(curElement as html.HtmlElement, entry.path!); - final html.Element clipElement = - html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer()); - clipDefs.add(clipElement); - } - // Reverse the transform of the clipping element so children can use - // effective transform to render. - // TODO(flutter_web): When we have more than a single clip element, - // reduce number of div nodes by merging (multiplying transforms). - final html.Element reverseTransformDiv = html.DivElement(); - reverseTransformDiv.style.position = 'absolute'; - setElementTransform( - reverseTransformDiv, - (newClipTransform.clone()..invert()).storage, - ); - curElement.append(reverseTransformDiv); - curElement = reverseTransformDiv; - } - - root!.style.position = 'absolute'; - domRenderer.append(curElement!, content); - setElementTransform( - content, - transformWithOffset(currentTransform, offset).storage, - ); - return [root]..addAll(clipDefs); -} - -/// Converts a [maskFilter] to the value to be used on a ``. -/// -/// Only supported in non-WebKit browsers. -String _maskFilterToCanvasFilter(ui.MaskFilter? maskFilter) { - assert( - browserEngine != BrowserEngine.webkit, - 'WebKit (Safari) does not support `filter` canvas property.', - ); - if (maskFilter != null) { - // Multiply by device-pixel ratio because the canvas' pixel width and height - // are larger than its CSS width and height by device-pixel ratio. - return 'blur(${maskFilter.webOnlySigma * window.devicePixelRatio}px)'; - } else { - return 'none'; - } -} - diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index 1ada5e37ed512..f0cf2adfb28c4 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:meta/meta.dart'; /// The HTML engine used by the current browser. enum BrowserEngine { @@ -23,10 +24,21 @@ enum BrowserEngine { /// The engine that powers Internet Explorer 11. ie11, + /// The engine that powers Samsung stock browser. It is based on blink. + samsung, + /// We were unable to detect the current browser engine. unknown, } +/// html webgl version qualifier constants. +abstract class WebGLVersion { + /// WebGL 1.0 is based on OpenGL ES 2.0 / GLSL 1.00 + static const int webgl1 = 1; + /// WebGL 2.0 is based on OpenGL ES 3.0 / GLSL 3.00 + static const int webgl2 = 2; +} + /// Lazily initialized current browser engine. late final BrowserEngine _browserEngine = _detectBrowserEngine(); @@ -48,7 +60,34 @@ BrowserEngine get browserEngine { BrowserEngine _detectBrowserEngine() { final String vendor = html.window.navigator.vendor; final String agent = html.window.navigator.userAgent.toLowerCase(); + return detectBrowserEngineByVendorAgent(vendor, agent); +} + +/// Detects samsung blink variants. +/// +/// Example patterns: +/// Note 2 : GT-N7100 +/// Note 3 : SM-N900T +/// Tab 4 : SM-T330NU +/// Galaxy S4: SHV-E330S +/// Galaxy Note2: SHV-E250L +/// Note: SAMSUNG-SGH-I717 +/// SPH/SCH are very old Palm models. +bool _isSamsungBrowser(String agent) { + final RegExp exp = RegExp(r'SAMSUNG|SGH-[I|N|T]|GT-[I|N]|SM-[A|N|P|T|Z]|SHV-E|SCH-[I|J|R|S]|SPH-L'); + return exp.hasMatch(agent.toUpperCase()); +} + +/// Detects browser engine for a given vendor and agent string. +/// +/// Used for testing this library. +@visibleForTesting +BrowserEngine detectBrowserEngineByVendorAgent(String vendor, String agent) { if (vendor == 'Google Inc.') { + // Samsung browser is based on blink, check for variant. + if (_isSamsungBrowser(agent)) { + return BrowserEngine.samsung; + } return BrowserEngine.blink; } else if (vendor == 'Apple Computer, Inc.') { return BrowserEngine.webkit; @@ -65,7 +104,6 @@ BrowserEngine _detectBrowserEngine() { // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor return BrowserEngine.firefox; } - // Assume unknown otherwise, but issue a warning. print('WARNING: failed to detect current browser engine.'); return BrowserEngine.unknown; @@ -96,7 +134,7 @@ enum OperatingSystem { } /// Lazily initialized current operating system. -late final OperatingSystem _operatingSystem = _detectOperatingSystem(); +late final OperatingSystem _operatingSystem = detectOperatingSystem(); /// Returns the [OperatingSystem] the current browsers works on. /// @@ -114,11 +152,24 @@ OperatingSystem get operatingSystem { /// This is intended to be used for testing and debugging only. OperatingSystem? debugOperatingSystemOverride; -OperatingSystem _detectOperatingSystem() { - final String platform = html.window.navigator.platform!; - final String userAgent = html.window.navigator.userAgent; +/// Detects operating system using platform and UA used for unit testing. +@visibleForTesting +OperatingSystem detectOperatingSystem({ + String? overridePlatform, + String? overrideUserAgent, + int? overrideMaxTouchPoints, +}) { + final String platform = overridePlatform ?? html.window.navigator.platform!; + final String userAgent = overrideUserAgent ?? html.window.navigator.userAgent; if (platform.startsWith('Mac')) { + // iDevices requesting a "desktop site" spoof their UA so it looks like a Mac. + // This checks if we're in a touch device, or on a real mac. + final int maxTouchPoints = + overrideMaxTouchPoints ?? html.window.navigator.maxTouchPoints ?? 0; + if (maxTouchPoints > 2) { + return OperatingSystem.iOs; + } return OperatingSystem.macOs; } else if (platform.toLowerCase().contains('iphone') || platform.toLowerCase().contains('ipad') || @@ -142,7 +193,7 @@ OperatingSystem _detectOperatingSystem() { /// /// These devices tend to behave differently on many core issues such as events, /// screen readers, input devices. -const Set _desktopOperatingSystems = { +const Set _desktopOperatingSystems = { OperatingSystem.macOs, OperatingSystem.linux, OperatingSystem.windows, @@ -160,6 +211,14 @@ bool get isDesktop => _desktopOperatingSystems.contains(operatingSystem); /// See [isDesktop]. bool get isMobile => !isDesktop; +/// Whether the browser is running on macOS or iOS. +/// +/// - See [operatingSystem]. +/// - See [OperatingSystem]. +bool get isMacOrIOS => + operatingSystem == OperatingSystem.iOs || + operatingSystem == OperatingSystem.macOs; + int? _cachedWebGLVersion; /// The highest WebGL version supported by the current browser, or -1 if WebGL @@ -181,10 +240,10 @@ int _detectWebGLVersion() { height: 1, ); if (canvas.getContext('webgl2') != null) { - return 2; + return WebGLVersion.webgl2; } if (canvas.getContext('webgl') != null) { - return 1; + return WebGLVersion.webgl1; } return -1; } diff --git a/lib/web_ui/lib/src/engine/browser_location.dart b/lib/web_ui/lib/src/engine/browser_location.dart deleted file mode 100644 index a9701cd99060f..0000000000000 --- a/lib/web_ui/lib/src/engine/browser_location.dart +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.10 -part of engine; - -// TODO(mdebbar): add other strategies. - -// Some parts of this file were inspired/copied from the AngularDart router. - -/// [LocationStrategy] is responsible for representing and reading route state -/// from the browser's URL. -/// -/// At the moment, only one strategy is implemented: [HashLocationStrategy]. -/// -/// This is used by [BrowserHistory] to interact with browser history APIs. -abstract class LocationStrategy { - const LocationStrategy(); - - /// Subscribes to popstate events and returns a function that could be used to - /// unsubscribe from popstate events. - ui.VoidCallback onPopState(html.EventListener fn); - - /// The active path in the browser history. - String get path; - - /// The state of the current browser history entry. - dynamic get state; - - /// Given a path that's internal to the app, create the external url that - /// will be used in the browser. - String prepareExternalUrl(String internalUrl); - - /// Push a new history entry. - void pushState(dynamic state, String title, String url); - - /// Replace the currently active history entry. - void replaceState(dynamic state, String title, String url); - - /// Go to the previous history entry. - Future back({int count = 1}); -} - -/// This is an implementation of [LocationStrategy] that uses the browser URL's -/// [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) -/// to represent its state. -/// -/// In order to use this [LocationStrategy] for an app, it needs to be set in -/// [ui.window.locationStrategy]: -/// -/// ```dart -/// import 'package:flutter_web/material.dart'; -/// import 'package:flutter_web/ui.dart' as ui; -/// -/// void main() { -/// ui.window.locationStrategy = const ui.HashLocationStrategy(); -/// runApp(MyApp()); -/// } -/// ``` -class HashLocationStrategy extends LocationStrategy { - final PlatformLocation _platformLocation; - - const HashLocationStrategy( - [this._platformLocation = const BrowserPlatformLocation()]); - - @override - ui.VoidCallback onPopState(html.EventListener fn) { - _platformLocation.onPopState(fn); - return () => _platformLocation.offPopState(fn); - } - - @override - String get path { - // the hash value is always prefixed with a `#` - // and if it is empty then it will stay empty - String path = _platformLocation.hash ?? ''; - assert(path.isEmpty || path.startsWith('#')); - - // We don't want to return an empty string as a path. Instead we default to "/". - if (path.isEmpty || path == '#') { - return '/'; - } - // At this point, we know [path] starts with "#" and isn't empty. - return path.substring(1); - } - - @override - dynamic get state => _platformLocation.state; - - @override - String prepareExternalUrl(String internalUrl) { - // It's convention that if the hash path is empty, we omit the `#`; however, - // if the empty URL is pushed it won't replace any existing fragment. So - // when the hash path is empty, we instead return the location's path and - // query. - return internalUrl.isEmpty - ? '${_platformLocation.pathname}${_platformLocation.search}' - : '#$internalUrl'; - } - - @override - void pushState(dynamic state, String title, String url) { - _platformLocation.pushState(state, title, prepareExternalUrl(url)); - } - - @override - void replaceState(dynamic state, String title, String url) { - _platformLocation.replaceState(state, title, prepareExternalUrl(url)); - } - - @override - Future back({int count = 1}) { - _platformLocation.back(count); - return _waitForPopState(); - } - - /// Waits until the next popstate event is fired. - /// - /// This is useful for example to wait until the browser has handled the - /// `history.back` transition. - Future _waitForPopState() { - final Completer completer = Completer(); - late ui.VoidCallback unsubscribe; - unsubscribe = onPopState((_) { - unsubscribe(); - completer.complete(); - }); - return completer.future; - } -} - -/// [PlatformLocation] encapsulates all calls to DOM apis, which allows the -/// [LocationStrategy] classes to be platform agnostic and testable. -/// -/// The [PlatformLocation] class is used directly by all implementations of -/// [LocationStrategy] when they need to interact with the DOM apis like -/// pushState, popState, etc... -abstract class PlatformLocation { - const PlatformLocation(); - - void onPopState(html.EventListener fn); - void offPopState(html.EventListener fn); - - void onHashChange(html.EventListener fn); - void offHashChange(html.EventListener fn); - - String get pathname; - String get search; - String? get hash; - dynamic get state; - - void pushState(dynamic state, String title, String url); - void replaceState(dynamic state, String title, String url); - void back(int count); -} - -/// An implementation of [PlatformLocation] for the browser. -class BrowserPlatformLocation extends PlatformLocation { - html.Location get _location => html.window.location; - html.History get _history => html.window.history; - - const BrowserPlatformLocation(); - - @override - void onPopState(html.EventListener fn) { - html.window.addEventListener('popstate', fn); - } - - @override - void offPopState(html.EventListener fn) { - html.window.removeEventListener('popstate', fn); - } - - @override - void onHashChange(html.EventListener fn) { - html.window.addEventListener('hashchange', fn); - } - - @override - void offHashChange(html.EventListener fn) { - html.window.removeEventListener('hashchange', fn); - } - - @override - String get pathname => _location.pathname!; - - @override - String get search => _location.search!; - - @override - String get hash => _location.hash; - - @override - dynamic get state => _history.state; - - @override - void pushState(dynamic state, String title, String url) { - _history.pushState(state, title, url); - } - - @override - void replaceState(dynamic state, String title, String url) { - _history.replaceState(state, title, url); - } - - @override - void back(int count) { - _history.go(-count); - } -} diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index d56351124eed6..be03c284e9bc2 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -2,10 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - -/// Allocates and caches 0 or more canvas(s) for [BitmapCanvas]. +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'dom_renderer.dart'; +import 'engine_canvas.dart'; +import 'html/bitmap_canvas.dart'; +import 'html/painting.dart'; +import 'html/path/conic.dart'; +import 'html/path/path.dart'; +import 'html/path/path_ref.dart'; +import 'html/path/path_utils.dart'; +import 'html/picture.dart'; +import 'html/shaders/image_shader.dart'; +import 'html/shaders/shader.dart'; +import 'platform_dispatcher.dart'; +import 'rrect_renderer.dart'; +import 'shadow.dart'; +import 'util.dart'; +import 'vector_math.dart'; +import 'window.dart'; + +/// Renders picture to a CanvasElement by allocating and caching 0 or more +/// canvas(s) for [BitmapCanvas]. /// /// [BitmapCanvas] signals allocation of first canvas using allocateCanvas. /// When a painting command such as drawImage or drawParagraph requires @@ -13,14 +38,14 @@ part of engine; /// and adds the canvas(s) to [_activeCanvasList]. /// /// To make sure transformations and clips are preserved correctly when a new -/// canvas is allocated, [_CanvasPool] replays the current stack on the newly +/// canvas is allocated, [CanvasPool] replays the current stack on the newly /// allocated canvas. It also maintains a [_saveContextCount] so that /// the context stack can be reinitialized to default when reused in the future. /// /// On a subsequent repaint, when a Picture determines that a [BitmapCanvas] -/// can be reused, [_CanvasPool] will move canvas(s) from pool to reusablePool +/// can be reused, [CanvasPool] will move canvas(s) from pool to reusablePool /// to prevent reallocation. -class _CanvasPool extends _SaveStackTracking { +class CanvasPool extends _SaveStackTracking { html.CanvasRenderingContext2D? _context; ContextStateHandle? _contextHandle; final int _widthInBitmapPixels, _heightInBitmapPixels; @@ -33,11 +58,33 @@ class _CanvasPool extends _SaveStackTracking { html.HtmlElement? _rootElement; int _saveContextCount = 0; - // Number of elements that have been added to flt-canvas. - int _activeElementCount = 0; + final double _density; + + /// Initializes canvas pool for target size and dpi. + CanvasPool(this._widthInBitmapPixels, this._heightInBitmapPixels, + this._density); - _CanvasPool(this._widthInBitmapPixels, this._heightInBitmapPixels); + /// Initializes canvas pool to be hosted on a surface. + void mount(html.HtmlElement rootElement) { + _rootElement = rootElement; + } + /// Sets the translate transform to be applied to canvas to compensate for + /// pixel padding applied to hosting [BitmapCanvas]. + /// + /// Should be called during initialization after [CanvasPool] is mounted. + set initialTransform(ui.Offset transform) { + translate(transform.dx, transform.dy); + } + + /// Returns true if no canvas has been allocated yet. + bool get isEmpty => _canvas == null; + + /// Returns true if a canvas has been allocated for use. + bool get isNotEmpty => _canvas != null; + + + /// Returns [CanvasRenderingContext2D] api to draw into this canvas. html.CanvasRenderingContext2D get context { html.CanvasRenderingContext2D? ctx = _context; if (ctx == null) { @@ -49,6 +96,8 @@ class _CanvasPool extends _SaveStackTracking { return ctx; } + /// Returns [ContextStateHandle] API to efficiently update state of + /// drawing context. ContextStateHandle get contextHandle { if (_canvas == null) { _createCanvas(); @@ -58,11 +107,11 @@ class _CanvasPool extends _SaveStackTracking { return _contextHandle!; } - // Prevents active canvas to be used for rendering and prepares a new - // canvas allocation on next drawing request that will require one. - // - // Saves current canvas so we can dispose - // and replay the clip/transform stack on top of new canvas. + /// Prevents active canvas to be used for rendering and prepares a new + /// canvas allocation on next drawing request that will require one. + /// + /// Saves current canvas so we can dispose + /// and replay the clip/transform stack on top of new canvas. void closeCurrentCanvas() { assert(_rootElement != null); // Place clean copy of current canvas with context stack restored and paint @@ -70,23 +119,23 @@ class _CanvasPool extends _SaveStackTracking { if (_canvas != null) { _restoreContextSave(); _contextHandle!.reset(); - _activeCanvasList ??= []; + _activeCanvasList ??= []; _activeCanvasList!.add(_canvas!); _canvas = null; _context = null; _contextHandle = null; } - _activeElementCount++; - } - - void allocateCanvas(html.HtmlElement rootElement) { - _rootElement = rootElement; } void _createCanvas() { bool requiresClearRect = false; bool reused = false; - html.CanvasElement canvas; + html.CanvasElement? canvas; + if (_canvas != null) { + _canvas!.width = 0; + _canvas!.height = 0; + _canvas = null; + } if (_reusablePool != null && _reusablePool!.isNotEmpty) { canvas = _canvas = _reusablePool!.removeAt(0); requiresClearRect = true; @@ -99,13 +148,10 @@ class _CanvasPool extends _SaveStackTracking { // * To make sure that when we scale the canvas by devicePixelRatio (see // _initializeViewport below) the pixels line up. final double cssWidth = - _widthInBitmapPixels / EngineWindow.browserDevicePixelRatio; + _widthInBitmapPixels / EnginePlatformDispatcher.browserDevicePixelRatio; final double cssHeight = - _heightInBitmapPixels / EngineWindow.browserDevicePixelRatio; - canvas = html.CanvasElement( - width: _widthInBitmapPixels, - height: _heightInBitmapPixels, - ); + _heightInBitmapPixels / EnginePlatformDispatcher.browserDevicePixelRatio; + canvas = _allocCanvas(_widthInBitmapPixels, _heightInBitmapPixels); _canvas = canvas; // Why is this null check here, even though we just allocated a canvas element above? @@ -115,13 +161,10 @@ class _CanvasPool extends _SaveStackTracking { // browser more memory to allocate a new canvas. if (_canvas == null) { // Evict BitmapCanvas(s) and retry. - _reduceCanvasMemoryUsage(); - canvas = html.CanvasElement( - width: _widthInBitmapPixels, - height: _heightInBitmapPixels, - ); + reduceCanvasMemoryUsage(); + canvas = _allocCanvas(_widthInBitmapPixels, _heightInBitmapPixels); } - canvas.style + canvas!.style ..position = 'absolute' ..width = '${cssWidth}px' ..height = '${cssHeight}px'; @@ -134,58 +177,86 @@ class _CanvasPool extends _SaveStackTracking { _rootElement!.append(canvas); } - if (_activeElementCount == 0) { - canvas.style.zIndex = '-1'; - } else if (reused) { - // If a canvas is the first element we set z-index = -1 to workaround - // blink compositing bug. To make sure this does not leak when reused - // reset z-index. - canvas.style.removeProperty('z-index'); + try { + if (reused) { + // If a canvas is the first element we set z-index = -1 in [BitmapCanvas] + // endOfPaint to workaround blink compositing bug. To make sure this + // does not leak when reused reset z-index. + canvas.style.removeProperty('z-index'); + } + _context = canvas.context2D; + } catch (e) { + // Handle OOM. } - ++_activeElementCount; - - final html.CanvasRenderingContext2D context = _context = canvas.context2D; - _contextHandle = ContextStateHandle(this, context); + if (_context == null) { + reduceCanvasMemoryUsage(); + _context = canvas.context2D; + } + if (_context == null) { + /// Browser ran out of memory, try to recover current allocation + /// and bail. + _canvas?.width = 0; + _canvas?.height = 0; + _canvas = null; + return; + } + _contextHandle = ContextStateHandle(this, _context!, _density); _initializeViewport(requiresClearRect); _replayClipStack(); } + html.CanvasElement? _allocCanvas(int width, int height) { + final dynamic canvas = + js_util.callMethod(html.document, 'createElement', ['CANVAS']); + if (canvas != null) { + try { + canvas.width = (width * _density).ceil(); + canvas.height = (height * _density).ceil(); + } catch (e) { + return null; + } + return canvas as html.CanvasElement; + } + return null; + // !!! We don't use the code below since NNBD assumes it can never return + // null and optimizes out code. + // return canvas = html.CanvasElement( + // width: _widthInBitmapPixels, + // height: _heightInBitmapPixels, + // ); + } + @override void clear() { super.clear(); if (_canvas != null) { // Restore to the state where we have only applied the scaling. - html.CanvasRenderingContext2D? ctx = _context; + final html.CanvasRenderingContext2D? ctx = _context; if (ctx != null) { try { ctx.font = ''; } catch (e) { // Firefox may explode here: // https://bugzilla.mozilla.org/show_bug.cgi?id=941146 - if (!_isNsErrorFailureException(e)) { + if (!isNsErrorFailureException(e)) { rethrow; } } } } reuse(); - resetTransform(); - } - - set initialTransform(ui.Offset transform) { - translate(transform.dx, transform.dy); } int _replaySingleSaveEntry(int clipDepth, Matrix4 prevTransform, - Matrix4 transform, List<_SaveClipEntry>? clipStack) { + Matrix4 transform, List? clipStack) { final html.CanvasRenderingContext2D ctx = context; if (clipStack != null) { - for (int clipCount = clipStack.length; + for (final int clipCount = clipStack.length; clipDepth < clipCount; clipDepth++) { - _SaveClipEntry clipEntry = clipStack[clipDepth]; - Matrix4 clipTimeTransform = clipEntry.currentTransform; + final SaveClipEntry clipEntry = clipStack[clipDepth]; + final Matrix4 clipTimeTransform = clipEntry.currentTransform; // If transform for entry recording change since last element, update. // Comparing only matrix3 elements since Canvas API restricted. if (clipTimeTransform[0] != prevTransform[0] || @@ -194,7 +265,7 @@ class _CanvasPool extends _SaveStackTracking { clipTimeTransform[5] != prevTransform[5] || clipTimeTransform[12] != prevTransform[12] || clipTimeTransform[13] != prevTransform[13]) { - final double ratio = EngineWindow.browserDevicePixelRatio; + final double ratio = dpi; ctx.setTransform(ratio, 0, 0, ratio, 0, 0); ctx.transform( clipTimeTransform[0], @@ -210,8 +281,13 @@ class _CanvasPool extends _SaveStackTracking { } else if (clipEntry.rrect != null) { _clipRRect(ctx, clipEntry.rrect!); } else if (clipEntry.path != null) { - _runPath(ctx, clipEntry.path as SurfacePath); - ctx.clip(); + final SurfacePath path = clipEntry.path! as SurfacePath; + _runPath(ctx, path); + if (path.fillType == ui.PathFillType.nonZero) { + ctx.clip(); + } else { + ctx.clip('evenodd'); + } } } } @@ -223,7 +299,7 @@ class _CanvasPool extends _SaveStackTracking { transform[5] != prevTransform[5] || transform[12] != prevTransform[12] || transform[13] != prevTransform[13]) { - final double ratio = EngineWindow.browserDevicePixelRatio; + final double ratio = dpi; ctx.setTransform(ratio, 0, 0, ratio, 0, 0); ctx.transform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); @@ -233,13 +309,12 @@ class _CanvasPool extends _SaveStackTracking { void _replayClipStack() { // Replay save/clip stack on this canvas now. - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; int clipDepth = 0; Matrix4 prevTransform = Matrix4.identity(); - for (int saveStackIndex = 0, len = _saveStack.length; - saveStackIndex < len; - saveStackIndex++) { - _SaveStackEntry saveEntry = _saveStack[saveStackIndex]; + final int len = _saveStack.length; + for (int saveStackIndex = 0; saveStackIndex < len; saveStackIndex++) { + final SaveStackEntry saveEntry = _saveStack[saveStackIndex]; clipDepth = _replaySingleSaveEntry( clipDepth, prevTransform, saveEntry.transform, saveEntry.clipStack); prevTransform = saveEntry.transform; @@ -247,15 +322,15 @@ class _CanvasPool extends _SaveStackTracking { ++_saveContextCount; } _replaySingleSaveEntry( - clipDepth, prevTransform, _currentTransform, _clipStack); + clipDepth, prevTransform, _currentTransform, clipStack); } - // Marks this pool for reuse. + /// Marks this pool for reuse. void reuse() { if (_canvas != null) { _restoreContextSave(); _contextHandle!.reset(); - _activeCanvasList ??= []; + _activeCanvasList ??= []; _activeCanvasList!.add(_canvas!); _context = null; _contextHandle = null; @@ -265,12 +340,14 @@ class _CanvasPool extends _SaveStackTracking { _canvas = null; _context = null; _contextHandle = null; - _activeElementCount = 0; + _resetTransform(); } + /// Signals to canvas pool the end of drawing commands so cached resources + /// that are reused from last instance can be cleanup. void endOfPaint() { if (_reusablePool != null) { - for (html.CanvasElement e in _reusablePool!) { + for (final html.CanvasElement e in _reusablePool!) { if (browserEngine == BrowserEngine.webkit) { e.width = e.height = 0; } @@ -292,7 +369,7 @@ class _CanvasPool extends _SaveStackTracking { /// coordinate system, and the pixel ratio is applied such that CSS pixels are /// translated to bitmap pixels. void _initializeViewport(bool clearCanvas) { - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; // Save the canvas state with top-level transforms so we can undo // any clips later when we reuse the canvas. ctx.save(); @@ -302,16 +379,20 @@ class _CanvasPool extends _SaveStackTracking { // is applied on the DOM elements. ctx.setTransform(1, 0, 0, 1, 0, 0); if (clearCanvas) { - ctx.clearRect(0, 0, _widthInBitmapPixels, _heightInBitmapPixels); + ctx.clearRect(0, 0, _widthInBitmapPixels * _density, + _heightInBitmapPixels * _density); } // This scale makes sure that 1 CSS pixel is translated to the correct // number of bitmap pixels. - ctx.scale(EngineWindow.browserDevicePixelRatio, - EngineWindow.browserDevicePixelRatio); + ctx.scale(dpi, dpi); } - void resetTransform() { + /// Returns effective dpi (browser DPI and pixel density due to transform). + double get dpi => + EnginePlatformDispatcher.browserDevicePixelRatio * _density; + + void _resetTransform() { final html.CanvasElement? canvas = _canvas; if (canvas != null) { canvas.style.transformOrigin = ''; @@ -319,9 +400,9 @@ class _CanvasPool extends _SaveStackTracking { } } - // Returns a "data://" URI containing a representation of the image in this - // canvas in PNG format. - String toDataUrl() => _canvas!.toDataUrl(); + /// Returns a "data://" URI containing a representation of the image in this + /// canvas in PNG format. + String toDataUrl() => _canvas?.toDataUrl() ?? ''; @override void save() { @@ -412,6 +493,7 @@ class _CanvasPool extends _SaveStackTracking { } } + @override void clipRect(ui.Rect rect) { super.clipRect(rect); if (_canvas != null) { @@ -425,6 +507,7 @@ class _CanvasPool extends _SaveStackTracking { ctx.clip(); } + @override void clipRRect(ui.RRect rrect) { super.clipRRect(rrect); if (_canvas != null) { @@ -438,17 +521,23 @@ class _CanvasPool extends _SaveStackTracking { ctx.clip(); } + @override void clipPath(ui.Path path) { super.clipPath(path); if (_canvas != null) { - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; _runPath(ctx, path as SurfacePath); - ctx.clip(); + if (path.fillType == ui.PathFillType.nonZero) { + ctx.clip(); + } else { + ctx.clip('evenodd'); + } } } + /// Fill a virtually infinite rect with a color and optional blendMode. void drawColor(ui.Color color, ui.BlendMode blendMode) { - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; contextHandle.blendMode = blendMode; contextHandle.fillStyle = colorToCssString(color); contextHandle.strokeStyle = ''; @@ -460,31 +549,43 @@ class _CanvasPool extends _SaveStackTracking { ctx.fillRect(-10000, -10000, 20000, 20000); } - // Fill a virtually infinite rect with the color. + /// Fill a virtually infinite rect with the color. void fill() { - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; ctx.beginPath(); // We can't use (0, 0, width, height) because the current transform can // cause it to not fill the entire clip. ctx.fillRect(-10000, -10000, 20000, 20000); } + /// Draws a line from [p1] to [p2]. void strokeLine(ui.Offset p1, ui.Offset p2) { - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; ctx.beginPath(); - ctx.moveTo(p1.dx, p1.dy); - ctx.lineTo(p2.dx, p2.dy); + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + if (shaderBounds == null) { + ctx.moveTo(p1.dx, p1.dy); + ctx.lineTo(p2.dx, p2.dy); + } else { + ctx.moveTo(p1.dx - shaderBounds.left, p1.dy - shaderBounds.top); + ctx.lineTo(p2.dx - shaderBounds.left, p2.dy - shaderBounds.top); + } ctx.stroke(); } + /// Draws a set of points with given radius, lines between points or + /// a polygon. void drawPoints(ui.PointMode pointMode, Float32List points, double radius) { - html.CanvasRenderingContext2D ctx = context; + final html.CanvasRenderingContext2D ctx = context; final int len = points.length; + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + final double offsetX = shaderBounds == null ? 0 : -shaderBounds.left; + final double offsetY = shaderBounds == null ? 0 : -shaderBounds.top; switch (pointMode) { case ui.PointMode.points: for (int i = 0; i < len; i += 2) { - final double x = points[i]; - final double y = points[i + 1]; + final double x = points[i] + offsetX; + final double y = points[i + 1] + offsetY; ctx.beginPath(); ctx.arc(x, y, radius, 0, 2.0 * math.pi); ctx.fill(); @@ -493,16 +594,16 @@ class _CanvasPool extends _SaveStackTracking { case ui.PointMode.lines: ctx.beginPath(); for (int i = 0; i < (len - 2); i += 4) { - ctx.moveTo(points[i], points[i + 1]); - ctx.lineTo(points[i + 2], points[i + 3]); + ctx.moveTo(points[i] + offsetX, points[i + 1] + offsetY); + ctx.lineTo(points[i + 2] + offsetX, points[i + 3] + offsetY); ctx.stroke(); } break; case ui.PointMode.polygon: ctx.beginPath(); - ctx.moveTo(points[0], points[1]); + ctx.moveTo(points[0] + offsetX, points[1] + offsetY); for (int i = 2; i < len; i += 2) { - ctx.lineTo(points[i], points[i + 1]); + ctx.lineTo(points[i] + offsetX, points[i + 1] + offsetY); } ctx.stroke(); break; @@ -534,8 +635,8 @@ class _CanvasPool extends _SaveStackTracking { break; case SPath.kConicVerb: final double w = iter.conicWeight; - Conic conic = Conic(p[0], p[1], p[2], p[3], p[4], p[5], w); - List points = conic.toQuads(); + final Conic conic = Conic(p[0], p[1], p[2], p[3], p[4], p[5], w); + final List points = conic.toQuads(); final int len = points.length; for (int i = 1; i < len; i += 2) { final double p1x = points[i].dx; @@ -554,42 +655,129 @@ class _CanvasPool extends _SaveStackTracking { } } + /// Draws a rectangle filled or stroked based on [style]. void drawRect(ui.Rect rect, ui.PaintingStyle? style) { context.beginPath(); - context.rect(rect.left, rect.top, rect.width, rect.height); + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + if (shaderBounds == null) { + context.rect(rect.left, rect.top, rect.width, rect.height); + } else { + context.rect(rect.left - shaderBounds.left, rect.top - shaderBounds.top, + rect.width, rect.height); + } contextHandle.paint(style); } + /// Applies path to drawing context, preparing for fill and other operations. + /// + /// WARNING: Don't refactor _runPath/_runPathWithOffset. Latency sensitive + void _runPathWithOffset(html.CanvasRenderingContext2D ctx, SurfacePath path, + double offsetX, double offsetY) { + ctx.beginPath(); + final Float32List p = _runBuffer; + final PathRefIterator iter = PathRefIterator(path.pathRef); + int verb = 0; + while ((verb = iter.next(p)) != SPath.kDoneVerb) { + switch (verb) { + case SPath.kMoveVerb: + ctx.moveTo(p[0] + offsetX, p[1] + offsetY); + break; + case SPath.kLineVerb: + ctx.lineTo(p[2] + offsetX, p[3] + offsetY); + break; + case SPath.kCubicVerb: + ctx.bezierCurveTo(p[2] + offsetX, p[3] + offsetY, + p[4] + offsetX, p[5] + offsetY, p[6] + offsetX, p[7] + offsetY); + break; + case SPath.kQuadVerb: + ctx.quadraticCurveTo(p[2] + offsetX, p[3] + offsetY, + p[4] + offsetX, p[5] + offsetY); + break; + case SPath.kConicVerb: + final double w = iter.conicWeight; + final Conic conic = Conic(p[0], p[1], p[2], p[3], p[4], p[5], w); + final List points = conic.toQuads(); + final int len = points.length; + for (int i = 1; i < len; i += 2) { + final double p1x = points[i].dx; + final double p1y = points[i].dy; + final double p2x = points[i + 1].dx; + final double p2y = points[i + 1].dy; + ctx.quadraticCurveTo(p1x + offsetX, p1y + offsetY, + p2x + offsetX, p2y + offsetY); + } + break; + case SPath.kCloseVerb: + ctx.closePath(); + break; + default: + throw UnimplementedError('Unknown path verb $verb'); + } + } + } + + /// Draws a rounded rectangle filled or stroked based on [style]. void drawRRect(ui.RRect roundRect, ui.PaintingStyle? style) { - _RRectToCanvasRenderer(context).render(roundRect); + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + RRectToCanvasRenderer(context).render( + shaderBounds == null ? roundRect + : roundRect.shift(ui.Offset(-shaderBounds.left, -shaderBounds.top))); contextHandle.paint(style); } + /// Fills or strokes the area between [outer] and [inner] rounded rectangles. + /// + /// Typically used to draw a thick round border. void drawDRRect(ui.RRect outer, ui.RRect inner, ui.PaintingStyle? style) { - _RRectRenderer renderer = _RRectToCanvasRenderer(context); - renderer.render(outer); - renderer.render(inner, startNewPath: false, reverse: true); + final RRectRenderer renderer = RRectToCanvasRenderer(context); + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + if (shaderBounds == null) { + renderer.render(outer); + renderer.render(inner, startNewPath: false, reverse: true); + } else { + final ui.Offset shift = ui.Offset(-shaderBounds.left, -shaderBounds.top); + renderer.render(outer.shift(shift)); + renderer.render(inner.shift(shift), startNewPath: false, reverse: true); + } contextHandle.paint(style); } + /// Draws an axis-aligned oval that fills the given axis-aligned rectangle. void drawOval(ui.Rect rect, ui.PaintingStyle? style) { context.beginPath(); - DomRenderer.ellipse(context, rect.center.dx, rect.center.dy, rect.width / 2, + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + final double cx = shaderBounds == null ? rect.center.dx : + rect.center.dx - shaderBounds.left; + final double cy = shaderBounds == null ? rect.center.dy : + rect.center.dy - shaderBounds.top; + DomRenderer.ellipse(context, cx, cy, rect.width / 2, rect.height / 2, 0, 0, 2.0 * math.pi, false); contextHandle.paint(style); } + /// Draws a circle centered at [c] with [radius]. void drawCircle(ui.Offset c, double radius, ui.PaintingStyle? style) { context.beginPath(); - DomRenderer.ellipse(context, c.dx, c.dy, radius, radius, 0, 0, 2.0 * math.pi, false); + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + final double cx = shaderBounds == null ? c.dx : c.dx - shaderBounds.left; + final double cy = shaderBounds == null ? c.dy : c.dy - shaderBounds.top; + DomRenderer.ellipse(context, cx, cy, radius, radius, 0, 0, 2.0 * math.pi, false); contextHandle.paint(style); } + /// Draws or strokes a path based on [style] and current context state. void drawPath(ui.Path path, ui.PaintingStyle? style) { - _runPath(context, path as SurfacePath); + final ui.Rect? shaderBounds = contextHandle._shaderBounds; + if (shaderBounds == null) { + _runPath(context, path as SurfacePath); + } else { + _runPathWithOffset(context, path as SurfacePath, + -shaderBounds.left, -shaderBounds.top); + } contextHandle.paintPath(style, path.fillType); } + /// Draws a shadow for a Path representing the given material elevation. void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { final SurfaceShadowData? shadow = computeShadow(path.getBounds(), elevation); @@ -618,7 +806,7 @@ class _CanvasPool extends _SaveStackTracking { // alpha for the paint the path is painted in addition to the shadow, // which is undesirable. context.translate(shadow.offset.dx, shadow.offset.dy); - context.filter = _maskFilterToCanvasFilter( + context.filter = maskFilterToCanvasFilter( ui.MaskFilter.blur(ui.BlurStyle.normal, shadow.blurWidth)); context.strokeStyle = ''; context.fillStyle = solidColor; @@ -652,6 +840,11 @@ class _CanvasPool extends _SaveStackTracking { } } + /// Disposes html canvas element(s) used by this pool when persistent surface + /// is disposed. + /// + /// When this pool is reused, [clear] is called instead to be able to + /// draw using existing canvas elements. void dispose() { // Webkit has a threshold for the amount of canvas pixels an app can // allocate. Even though our canvases are being garbage-collected as @@ -667,7 +860,7 @@ class _CanvasPool extends _SaveStackTracking { void _clearActiveCanvasList() { if (_activeCanvasList != null) { - for (html.CanvasElement c in _activeCanvasList!) { + for (final html.CanvasElement c in _activeCanvasList!) { if (browserEngine == BrowserEngine.webkit) { c.width = c.height = 0; } @@ -678,16 +871,19 @@ class _CanvasPool extends _SaveStackTracking { } } -// Optimizes applying paint parameters to html canvas. -// -// See https://www.w3.org/TR/2dcontext/ for defaults used in this class -// to initialize current values. -// +/// Optimizes applying paint parameters to html canvas. +/// +/// See https://www.w3.org/TR/2dcontext/ for defaults used in this class +/// to initialize current values. class ContextStateHandle { + /// Associated canvas element context tracked by this context state. final html.CanvasRenderingContext2D context; - final _CanvasPool _canvasPool; + final CanvasPool _canvasPool; + /// Dpi of context. + final double density; - ContextStateHandle(this._canvasPool, this.context); + /// Initializes context state for a [CanvasPool]. + ContextStateHandle(this._canvasPool, this.context, this.density); ui.BlendMode? _currentBlendMode = ui.BlendMode.srcOver; ui.StrokeCap? _currentStrokeCap = ui.StrokeCap.butt; ui.StrokeJoin? _currentStrokeJoin = ui.StrokeJoin.miter; @@ -697,22 +893,25 @@ class ContextStateHandle { Object? _currentStrokeStyle; double _currentLineWidth = 1.0; + /// See [html.CanvasRenderingContext2D]. set blendMode(ui.BlendMode? blendMode) { if (blendMode != _currentBlendMode) { _currentBlendMode = blendMode; context.globalCompositeOperation = - _stringForBlendMode(blendMode) ?? 'source-over'; + stringForBlendMode(blendMode) ?? 'source-over'; } } + /// See [html.CanvasRenderingContext2D]. set strokeCap(ui.StrokeCap? strokeCap) { strokeCap ??= ui.StrokeCap.butt; if (strokeCap != _currentStrokeCap) { _currentStrokeCap = strokeCap; - context.lineCap = _stringForStrokeCap(strokeCap)!; + context.lineCap = stringForStrokeCap(strokeCap)!; } } + /// See [html.CanvasRenderingContext2D]. set lineWidth(double lineWidth) { if (lineWidth != _currentLineWidth) { _currentLineWidth = lineWidth; @@ -720,14 +919,16 @@ class ContextStateHandle { } } + /// See [html.CanvasRenderingContext2D]. set strokeJoin(ui.StrokeJoin? strokeJoin) { strokeJoin ??= ui.StrokeJoin.miter; if (strokeJoin != _currentStrokeJoin) { _currentStrokeJoin = strokeJoin; - context.lineJoin = _stringForStrokeJoin(strokeJoin); + context.lineJoin = stringForStrokeJoin(strokeJoin); } } + /// See [html.CanvasRenderingContext2D]. set fillStyle(Object? colorOrGradient) { if (!identical(colorOrGradient, _currentFillStyle)) { _currentFillStyle = colorOrGradient; @@ -735,6 +936,7 @@ class ContextStateHandle { } } + /// See [html.CanvasRenderingContext2D]. set strokeStyle(Object? colorOrGradient) { if (!identical(colorOrGradient, _currentStrokeStyle)) { _currentStrokeStyle = colorOrGradient; @@ -745,6 +947,14 @@ class ContextStateHandle { ui.MaskFilter? _currentFilter; SurfacePaintData? _lastUsedPaint; + /// Currently active shader bounds. + /// + /// When a paint style uses a shader that produces a pattern, the pattern + /// origin is relative to current transform. Therefore any painting operations + /// will have to reverse the transform to correctly align pattern with + /// drawing bounds. + ui.Rect? _shaderBounds; + /// The painting state. /// /// Used to validate that the [setUpPaint] and [tearDownPaint] are called in @@ -761,7 +971,7 @@ class ContextStateHandle { /// Sets paint properties on the current canvas. /// /// [tearDownPaint] must be called after calling this method. - void setUpPaint(SurfacePaintData paint) { + void setUpPaint(SurfacePaintData paint, ui.Rect? shaderBounds) { if (assertionsEnabled) { assert(!_debugIsPaintSetUp); _debugIsPaintSetUp = true; @@ -774,25 +984,43 @@ class ContextStateHandle { strokeJoin = paint.strokeJoin; if (paint.shader != null) { - final EngineGradient engineShader = paint.shader as EngineGradient; - final Object paintStyle = - engineShader.createPaintStyle(_canvasPool.context); - fillStyle = paintStyle; - strokeStyle = paintStyle; + if (paint.shader is EngineGradient) { + final EngineGradient engineShader = paint.shader! as EngineGradient; + final Object paintStyle = + engineShader.createPaintStyle(_canvasPool.context, shaderBounds, + density); + fillStyle = paintStyle; + strokeStyle = paintStyle; + _shaderBounds = shaderBounds; + // Align pattern origin to destination. + context.translate(shaderBounds!.left, shaderBounds.top); + } else if (paint.shader is EngineImageShader) { + final EngineImageShader imageShader = paint.shader! as EngineImageShader; + final Object paintStyle = + imageShader.createPaintStyle(_canvasPool.context, shaderBounds, + density); + fillStyle = paintStyle; + strokeStyle = paintStyle; + if (imageShader.requiresTileOffset) { + _shaderBounds = shaderBounds; + // Align pattern origin to destination. + context.translate(shaderBounds!.left, shaderBounds.top); + } + } } else if (paint.color != null) { final String? colorString = colorToCssString(paint.color); fillStyle = colorString; strokeStyle = colorString; } else { - fillStyle = ''; - strokeStyle = ''; + fillStyle = '#000000'; + strokeStyle = '#000000'; } final ui.MaskFilter? maskFilter = paint.maskFilter; if (!_renderMaskFilterForWebkit) { if (_currentFilter != maskFilter) { _currentFilter = maskFilter; - context.filter = _maskFilterToCanvasFilter(maskFilter); + context.filter = maskFilterToCanvasFilter(maskFilter); } } else { // WebKit does not support the `filter` property. Instead we apply a @@ -830,8 +1058,8 @@ class ContextStateHandle { final Float32List tempVector = Float32List(2); tempVector[0] = kOutsideTheBoundsOffset * window.devicePixelRatio; _canvasPool.currentTransform.transform2(tempVector); - double shadowOffsetX = tempVector[0]; - double shadowOffsetY = tempVector[1]; + final double shadowOffsetX = tempVector[0]; + final double shadowOffsetY = tempVector[1]; tempVector[0] = tempVector[1] = 0; _canvasPool.currentTransform.transform2(tempVector); @@ -861,8 +1089,13 @@ class ContextStateHandle { // shadow attributes. context.restore(); } + if (_shaderBounds != null) { + context.translate(-_shaderBounds!.left, -_shaderBounds!.top); + _shaderBounds = null; + } } + /// Fills or strokes the currently active path. void paint(ui.PaintingStyle? style) { if (style == ui.PaintingStyle.stroke) { context.stroke(); @@ -871,6 +1104,7 @@ class ContextStateHandle { } } + /// Fills or strokes the currently active path based on fill type. void paintPath(ui.PaintingStyle? style, ui.PathFillType pathFillType) { if (style == ui.PaintingStyle.stroke) { context.stroke(); @@ -883,6 +1117,8 @@ class ContextStateHandle { } } + /// Resets drawing context state to defaults for + /// [html.CanvasRenderingContext2D]. void reset() { context.fillStyle = ''; // Read back fillStyle/strokeStyle values from context so that input such @@ -903,6 +1139,7 @@ class ContextStateHandle { _currentStrokeCap = ui.StrokeCap.butt; context.lineJoin = 'miter'; _currentStrokeJoin = ui.StrokeJoin.miter; + _shaderBounds = null; } } @@ -912,21 +1149,21 @@ class _SaveStackTracking { // !Warning: this vector should not be mutated. static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0); - final List<_SaveStackEntry> _saveStack = <_SaveStackEntry>[]; + final List _saveStack = []; /// The stack that maintains clipping operations used when text is painted /// onto bitmap canvas but is composited as separate element. - List<_SaveClipEntry>? _clipStack; + List? clipStack; /// Returns whether there are active clipping regions on the canvas. - bool get isClipped => _clipStack != null; + bool get isClipped => clipStack != null; /// Empties the save stack and the element stack, and resets the transform /// and clip parameters. @mustCallSuper void clear() { _saveStack.clear(); - _clipStack = null; + clipStack = null; _currentTransform = Matrix4.identity(); } @@ -937,10 +1174,10 @@ class _SaveStackTracking { /// Saves current clip and transform on the save stack. @mustCallSuper void save() { - _saveStack.add(_SaveStackEntry( + _saveStack.add(SaveStackEntry( transform: _currentTransform.clone(), clipStack: - _clipStack == null ? null : List<_SaveClipEntry>.from(_clipStack!), + clipStack == null ? null : List.from(clipStack!), )); } @@ -950,9 +1187,9 @@ class _SaveStackTracking { if (_saveStack.isEmpty) { return; } - final _SaveStackEntry entry = _saveStack.removeLast(); + final SaveStackEntry entry = _saveStack.removeLast(); _currentTransform = entry.transform; - _clipStack = entry.clipStack; + clipStack = entry.clipStack; } /// Multiplies the [currentTransform] matrix by a translation. @@ -992,21 +1229,21 @@ class _SaveStackTracking { /// Adds a rectangle to clipping stack. @mustCallSuper void clipRect(ui.Rect rect) { - _clipStack ??= <_SaveClipEntry>[]; - _clipStack!.add(_SaveClipEntry.rect(rect, _currentTransform.clone())); + clipStack ??= []; + clipStack!.add(SaveClipEntry.rect(rect, _currentTransform.clone())); } /// Adds a round rectangle to clipping stack. @mustCallSuper void clipRRect(ui.RRect rrect) { - _clipStack ??= <_SaveClipEntry>[]; - _clipStack!.add(_SaveClipEntry.rrect(rrect, _currentTransform.clone())); + clipStack ??= []; + clipStack!.add(SaveClipEntry.rrect(rrect, _currentTransform.clone())); } /// Adds a path to clipping stack. @mustCallSuper void clipPath(ui.Path path) { - _clipStack ??= <_SaveClipEntry>[]; - _clipStack!.add(_SaveClipEntry.path(path, _currentTransform.clone())); + clipStack ??= []; + clipStack!.add(SaveClipEntry.path(path, _currentTransform.clone())); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart index 3426253473469..f5c94879121d6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart @@ -2,14 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +// ignore_for_file: public_member_api_docs +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'canvaskit_api.dart'; +import 'image.dart'; +import 'image_filter.dart'; +import 'painting.dart'; +import 'path.dart'; +import 'picture.dart'; +import 'text.dart'; +import 'util.dart'; +import 'vertices.dart'; + +/// Memoized value for ClipOp.Intersect, so we don't have to hit JS-interop +/// every time we need it. +final SkClipOp _clipOpIntersect = canvasKit.ClipOp.Intersect; /// A Dart wrapper around Skia's [SkCanvas]. /// /// This is intentionally not memory-managing the underlying [SkCanvas]. See /// the docs on [SkCanvas], which explain the reason. class CkCanvas { + // Cubic equation coefficients recommended by Mitchell & Netravali + // in their paper on cubic interpolation. + static const double _kMitchellNetravali_B = 1.0 / 3.0; + static const double _kMitchellNetravali_C = 1.0 / 3.0; + final SkCanvas skCanvas; CkCanvas(this.skCanvas); @@ -20,12 +42,9 @@ class CkCanvas { skCanvas.clear(toSharedSkColor1(color)); } - static final SkClipOp _clipOpIntersect = canvasKit.ClipOp.Intersect; - - void clipPath(ui.Path path, bool doAntiAlias) { - final CkPath ckPath = path as CkPath; + void clipPath(CkPath path, bool doAntiAlias) { skCanvas.clipPath( - ckPath._skPath, + path.skiaObject, _clipOpIntersect, doAntiAlias, ); @@ -64,17 +83,17 @@ class CkCanvas { ); } + // TODO(flar): CanvasKit does not expose sampling options available on SkCanvas.drawAtlas void drawAtlasRaw( CkPaint paint, - ui.Image atlas, + CkImage atlas, Float32List rstTransforms, Float32List rects, - List? colors, + Uint32List? colors, ui.BlendMode blendMode, ) { - final CkImage skAtlas = atlas as CkImage; skCanvas.drawAtlas( - skAtlas.skImage, + atlas.skImage, rects, rstTransforms, paint.skiaObject, @@ -107,34 +126,59 @@ class CkCanvas { ); } - void drawImage(ui.Image image, ui.Offset offset, CkPaint paint) { - final CkImage skImage = image as CkImage; - skCanvas.drawImage( - skImage.skImage, - offset.dx, - offset.dy, - paint.skiaObject, - ); + void drawImage(CkImage image, ui.Offset offset, CkPaint paint) { + final ui.FilterQuality filterQuality = paint.filterQuality; + if (filterQuality == ui.FilterQuality.high) { + skCanvas.drawImageCubic( + image.skImage, + offset.dx, + offset.dy, + _kMitchellNetravali_B, + _kMitchellNetravali_C, + paint.skiaObject, + ); + } else { + skCanvas.drawImageOptions( + image.skImage, + offset.dx, + offset.dy, + toSkFilterMode(filterQuality), + toSkMipmapMode(filterQuality), + paint.skiaObject, + ); + } } - void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, CkPaint paint) { - final CkImage skImage = image as CkImage; - skCanvas.drawImageRect( - skImage.skImage, - toSkRect(src), - toSkRect(dst), - paint.skiaObject, - false, - ); + void drawImageRect(CkImage image, ui.Rect src, ui.Rect dst, CkPaint paint) { + final ui.FilterQuality filterQuality = paint.filterQuality; + if (filterQuality == ui.FilterQuality.high) { + skCanvas.drawImageRectCubic( + image.skImage, + toSkRect(src), + toSkRect(dst), + _kMitchellNetravali_B, + _kMitchellNetravali_C, + paint.skiaObject, + ); + } else { + skCanvas.drawImageRectOptions( + image.skImage, + toSkRect(src), + toSkRect(dst), + toSkFilterMode(filterQuality), + toSkMipmapMode(filterQuality), + paint.skiaObject, + ); + } } void drawImageNine( - ui.Image image, ui.Rect center, ui.Rect dst, CkPaint paint) { - final CkImage skImage = image as CkImage; + CkImage image, ui.Rect center, ui.Rect dst, CkPaint paint) { skCanvas.drawImageNine( - skImage.skImage, + image.skImage, toSkRect(center), toSkRect(dst), + toSkFilterMode(paint.filterQuality), paint.skiaObject, ); } @@ -166,18 +210,18 @@ class CkCanvas { offset.dx, offset.dy, ); + paragraph.markUsed(); } void drawPath(CkPath path, CkPaint paint) { - skCanvas.drawPath(path._skPath, paint.skiaObject); + skCanvas.drawPath(path.skiaObject, paint.skiaObject); } void drawPicture(CkPicture picture) { - skCanvas.drawPicture(picture.skiaObject.skiaObject); + skCanvas.drawPicture(picture.skiaObject); } - void drawPoints(CkPaint paint, ui.PointMode pointMode, - Float32List points) { + void drawPoints(CkPaint paint, ui.PointMode pointMode, Float32List points) { skCanvas.drawPoints( toSkPointMode(pointMode), points, @@ -196,17 +240,16 @@ class CkCanvas { skCanvas.drawRect(toSkRect(rect), paint.skiaObject); } - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { - drawSkShadow(skCanvas, path as CkPath, color, elevation, - transparentOccluder, ui.window.devicePixelRatio); + void drawShadow( + CkPath path, ui.Color color, double elevation, bool transparentOccluder) { + drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, + ui.window.devicePixelRatio); } void drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, CkPaint paint) { - CkVertices skVertices = vertices as CkVertices; + CkVertices vertices, ui.BlendMode blendMode, CkPaint paint) { skCanvas.drawVertices( - skVertices.skiaObject, + vertices.skiaObject, toSkBlendMode(blendMode), paint.skiaObject, ); @@ -228,26 +271,28 @@ class CkCanvas { return skCanvas.save(); } - void saveLayer(ui.Rect bounds, CkPaint paint) { + void saveLayer(ui.Rect bounds, CkPaint? paint) { skCanvas.saveLayer( + paint?.skiaObject, toSkRect(bounds), - paint.skiaObject, + null, + null, ); } - void saveLayerWithoutBounds(CkPaint paint) { - final SkCanvasSaveLayerWithoutBoundsOverload override = skCanvas as SkCanvasSaveLayerWithoutBoundsOverload; - override.saveLayer(paint.skiaObject); + void saveLayerWithoutBounds(CkPaint? paint) { + skCanvas.saveLayer(paint?.skiaObject, null, null, null); } - void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { - final SkCanvasSaveLayerWithFilterOverload override = skCanvas as SkCanvasSaveLayerWithFilterOverload; - final CkImageFilter skImageFilter = filter as CkImageFilter; - return override.saveLayer( - null, - skImageFilter.skiaObject, - 0, + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, + [CkPaint? paint]) { + final CkManagedSkImageFilterConvertible convertible = + filter as CkManagedSkImageFilterConvertible; + return skCanvas.saveLayer( + paint?.skiaObject, toSkRect(bounds), + convertible.imageFilter.skiaObject, + 0, ); } @@ -267,7 +312,848 @@ class CkCanvas { skCanvas.translate(dx, dy); } - void flush() { - skCanvas.flush(); + CkPictureSnapshot? get pictureSnapshot => null; +} + +class RecordingCkCanvas extends CkCanvas { + RecordingCkCanvas(SkCanvas skCanvas, ui.Rect bounds) + : pictureSnapshot = CkPictureSnapshot(bounds), + super(skCanvas); + + @override + final CkPictureSnapshot pictureSnapshot; + + void _addCommand(CkPaintCommand command) { + pictureSnapshot._commands.add(command); + } + + @override + void clear(ui.Color color) { + super.clear(color); + _addCommand(CkClearCommand(color)); + } + + @override + void clipPath(CkPath path, bool doAntiAlias) { + super.clipPath(path, doAntiAlias); + _addCommand(CkClipPathCommand(path, doAntiAlias)); + } + + @override + void clipRRect(ui.RRect rrect, bool doAntiAlias) { + super.clipRRect(rrect, doAntiAlias); + _addCommand(CkClipRRectCommand(rrect, doAntiAlias)); + } + + @override + void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) { + super.clipRect(rect, clipOp, doAntiAlias); + _addCommand(CkClipRectCommand(rect, clipOp, doAntiAlias)); + } + + @override + void drawArc( + ui.Rect oval, + double startAngle, + double sweepAngle, + bool useCenter, + CkPaint paint, + ) { + super.drawArc(oval, startAngle, sweepAngle, useCenter, paint); + _addCommand( + CkDrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint)); + } + + @override + void drawAtlasRaw( + CkPaint paint, + CkImage atlas, + Float32List rstTransforms, + Float32List rects, + Uint32List? colors, + ui.BlendMode blendMode, + ) { + super.drawAtlasRaw(paint, atlas, rstTransforms, rects, colors, blendMode); + _addCommand(CkDrawAtlasCommand( + paint, atlas, rstTransforms, rects, colors, blendMode)); + } + + @override + void drawCircle(ui.Offset c, double radius, CkPaint paint) { + super.drawCircle(c, radius, paint); + _addCommand(CkDrawCircleCommand(c, radius, paint)); + } + + @override + void drawColor(ui.Color color, ui.BlendMode blendMode) { + super.drawColor(color, blendMode); + _addCommand(CkDrawColorCommand(color, blendMode)); + } + + @override + void drawDRRect(ui.RRect outer, ui.RRect inner, CkPaint paint) { + super.drawDRRect(outer, inner, paint); + _addCommand(CkDrawDRRectCommand(outer, inner, paint)); + } + + @override + void drawImage(CkImage image, ui.Offset offset, CkPaint paint) { + super.drawImage(image, offset, paint); + _addCommand(CkDrawImageCommand(image, offset, paint)); + } + + @override + void drawImageRect(CkImage image, ui.Rect src, ui.Rect dst, CkPaint paint) { + super.drawImageRect(image, src, dst, paint); + _addCommand(CkDrawImageRectCommand(image, src, dst, paint)); + } + + @override + void drawImageNine( + CkImage image, ui.Rect center, ui.Rect dst, CkPaint paint) { + super.drawImageNine(image, center, dst, paint); + _addCommand(CkDrawImageNineCommand(image, center, dst, paint)); + } + + @override + void drawLine(ui.Offset p1, ui.Offset p2, CkPaint paint) { + super.drawLine(p1, p2, paint); + _addCommand(CkDrawLineCommand(p1, p2, paint)); + } + + @override + void drawOval(ui.Rect rect, CkPaint paint) { + super.drawOval(rect, paint); + _addCommand(CkDrawOvalCommand(rect, paint)); + } + + @override + void drawPaint(CkPaint paint) { + super.drawPaint(paint); + _addCommand(CkDrawPaintCommand(paint)); + } + + @override + void drawParagraph(CkParagraph paragraph, ui.Offset offset) { + super.drawParagraph(paragraph, offset); + _addCommand(CkDrawParagraphCommand(paragraph, offset)); + } + + @override + void drawPath(CkPath path, CkPaint paint) { + super.drawPath(path, paint); + _addCommand(CkDrawPathCommand(path, paint)); + } + + @override + void drawPicture(CkPicture picture) { + super.drawPicture(picture); + _addCommand(CkDrawPictureCommand(picture)); + } + + @override + void drawPoints(CkPaint paint, ui.PointMode pointMode, Float32List points) { + super.drawPoints(paint, pointMode, points); + _addCommand(CkDrawPointsCommand(pointMode, points, paint)); + } + + @override + void drawRRect(ui.RRect rrect, CkPaint paint) { + super.drawRRect(rrect, paint); + _addCommand(CkDrawRRectCommand(rrect, paint)); + } + + @override + void drawRect(ui.Rect rect, CkPaint paint) { + super.drawRect(rect, paint); + _addCommand(CkDrawRectCommand(rect, paint)); + } + + @override + void drawShadow( + CkPath path, ui.Color color, double elevation, bool transparentOccluder) { + super.drawShadow(path, color, elevation, transparentOccluder); + _addCommand( + CkDrawShadowCommand(path, color, elevation, transparentOccluder)); + } + + @override + void drawVertices( + CkVertices vertices, ui.BlendMode blendMode, CkPaint paint) { + super.drawVertices(vertices, blendMode, paint); + _addCommand(CkDrawVerticesCommand(vertices, blendMode, paint)); + } + + @override + void restore() { + super.restore(); + _addCommand(const CkRestoreCommand()); + } + + @override + void restoreToCount(int count) { + super.restoreToCount(count); + _addCommand(CkRestoreToCountCommand(count)); + } + + @override + void rotate(double radians) { + super.rotate(radians); + _addCommand(CkRotateCommand(radians)); + } + + @override + int save() { + _addCommand(const CkSaveCommand()); + return super.save(); + } + + @override + void saveLayer(ui.Rect bounds, CkPaint? paint) { + super.saveLayer(bounds, paint); + _addCommand(CkSaveLayerCommand(bounds, paint)); + } + + @override + void saveLayerWithoutBounds(CkPaint? paint) { + super.saveLayerWithoutBounds(paint); + _addCommand(CkSaveLayerWithoutBoundsCommand(paint)); + } + + @override + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, + [CkPaint? paint]) { + super.saveLayerWithFilter(bounds, filter, paint); + _addCommand(CkSaveLayerWithFilterCommand(bounds, filter, paint)); + } + + @override + void scale(double sx, double sy) { + super.scale(sx, sy); + _addCommand(CkScaleCommand(sx, sy)); + } + + @override + void skew(double sx, double sy) { + super.skew(sx, sy); + _addCommand(CkSkewCommand(sx, sy)); + } + + @override + void transform(Float32List matrix4) { + super.transform(matrix4); + _addCommand(CkTransformCommand(matrix4)); + } + + @override + void translate(double dx, double dy) { + super.translate(dx, dy); + _addCommand(CkTranslateCommand(dx, dy)); + } +} + +class CkPictureSnapshot { + CkPictureSnapshot(this._bounds); + + final ui.Rect _bounds; + final List _commands = []; + + SkPicture toPicture() { + final SkPictureRecorder recorder = SkPictureRecorder(); + final Float32List skRect = toSkRect(_bounds); + final SkCanvas skCanvas = recorder.beginRecording(skRect); + for (final CkPaintCommand command in _commands) { + command.apply(skCanvas); + } + final SkPicture skPicture = recorder.finishRecordingAsPicture(); + recorder.delete(); + return skPicture; + } + + void dispose() { + for (final CkPaintCommand command in _commands) { + command.dispose(); + } + } +} + +/// A paint command recorded by [RecordingCkCanvas]. +/// +/// # Special rules when drawing images +/// +/// A command painting an image must clone the original image to bump the ref +/// count. Otherwise when the framework decides it doesn't need the image any +/// more it will bump the ref count down and delete the underlying Skia object, +/// leaving the picture that recorded this paint command with a dangling +/// pointer. If we attempt to resurrect the picture we'll hit a use-after-free +/// error. The command must call [CkImage.dispose] in its [dispose] +/// implementation. +abstract class CkPaintCommand { + const CkPaintCommand(); + + /// Applies the command onto the [canvas]. + void apply(SkCanvas canvas); + + /// Frees resources associated with the command. + void dispose() {} +} + +class CkClearCommand extends CkPaintCommand { + const CkClearCommand(this.color); + + final ui.Color color; + + @override + void apply(SkCanvas canvas) { + canvas.clear(toSharedSkColor1(color)); + } +} + +class CkSaveCommand extends CkPaintCommand { + const CkSaveCommand(); + + @override + void apply(SkCanvas canvas) { + canvas.save(); + } +} + +class CkRestoreCommand extends CkPaintCommand { + const CkRestoreCommand(); + + @override + void apply(SkCanvas canvas) { + canvas.restore(); + } +} + +class CkRestoreToCountCommand extends CkPaintCommand { + const CkRestoreToCountCommand(this.count); + + final int count; + + @override + void apply(SkCanvas canvas) { + canvas.restoreToCount(count); + } +} + +class CkTranslateCommand extends CkPaintCommand { + final double dx; + final double dy; + + CkTranslateCommand(this.dx, this.dy); + + @override + void apply(SkCanvas canvas) { + canvas.translate(dx, dy); + } +} + +class CkScaleCommand extends CkPaintCommand { + final double sx; + final double sy; + + CkScaleCommand(this.sx, this.sy); + + @override + void apply(SkCanvas canvas) { + canvas.scale(sx, sy); + } +} + +class CkRotateCommand extends CkPaintCommand { + final double radians; + + CkRotateCommand(this.radians); + + @override + void apply(SkCanvas canvas) { + canvas.rotate(radians * 180.0 / math.pi, 0.0, 0.0); + } +} + +class CkTransformCommand extends CkPaintCommand { + final Float32List matrix4; + + CkTransformCommand(this.matrix4); + + @override + void apply(SkCanvas canvas) { + canvas.concat(toSkMatrixFromFloat32(matrix4)); + } +} + +class CkSkewCommand extends CkPaintCommand { + final double sx; + final double sy; + + CkSkewCommand(this.sx, this.sy); + + @override + void apply(SkCanvas canvas) { + canvas.skew(sx, sy); + } +} + +class CkClipRectCommand extends CkPaintCommand { + final ui.Rect rect; + final ui.ClipOp clipOp; + final bool doAntiAlias; + + CkClipRectCommand(this.rect, this.clipOp, this.doAntiAlias); + + @override + void apply(SkCanvas canvas) { + canvas.clipRect( + toSkRect(rect), + toSkClipOp(clipOp), + doAntiAlias, + ); + } +} + +class CkDrawArcCommand extends CkPaintCommand { + CkDrawArcCommand( + this.oval, this.startAngle, this.sweepAngle, this.useCenter, this.paint); + + final ui.Rect oval; + final double startAngle; + final double sweepAngle; + final bool useCenter; + final CkPaint paint; + + @override + void apply(SkCanvas canvas) { + const double toDegrees = 180 / math.pi; + canvas.drawArc( + toSkRect(oval), + startAngle * toDegrees, + sweepAngle * toDegrees, + useCenter, + paint.skiaObject, + ); + } +} + +class CkDrawAtlasCommand extends CkPaintCommand { + CkDrawAtlasCommand(this.paint, this.atlas, this.rstTransforms, this.rects, + this.colors, this.blendMode); + + final CkPaint paint; + final CkImage atlas; + final Float32List rstTransforms; + final Float32List rects; + final Uint32List? colors; + final ui.BlendMode blendMode; + + @override + void apply(SkCanvas canvas) { + canvas.drawAtlas( + atlas.skImage, + rects, + rstTransforms, + paint.skiaObject, + toSkBlendMode(blendMode), + colors, + ); + } +} + +class CkClipRRectCommand extends CkPaintCommand { + final ui.RRect rrect; + final bool doAntiAlias; + + CkClipRRectCommand(this.rrect, this.doAntiAlias); + + @override + void apply(SkCanvas canvas) { + canvas.clipRRect( + toSkRRect(rrect), + _clipOpIntersect, + doAntiAlias, + ); + } +} + +class CkClipPathCommand extends CkPaintCommand { + final CkPath path; + final bool doAntiAlias; + + CkClipPathCommand(this.path, this.doAntiAlias); + + @override + void apply(SkCanvas canvas) { + canvas.clipPath( + path.skiaObject, + _clipOpIntersect, + doAntiAlias, + ); + } +} + +class CkDrawColorCommand extends CkPaintCommand { + final ui.Color color; + final ui.BlendMode blendMode; + + CkDrawColorCommand(this.color, this.blendMode); + + @override + void apply(SkCanvas canvas) { + canvas.drawColorInt( + color.value, + toSkBlendMode(blendMode), + ); + } +} + +class CkDrawLineCommand extends CkPaintCommand { + final ui.Offset p1; + final ui.Offset p2; + final CkPaint paint; + + CkDrawLineCommand(this.p1, this.p2, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawLine( + p1.dx, + p1.dy, + p2.dx, + p2.dy, + paint.skiaObject, + ); + } +} + +class CkDrawPaintCommand extends CkPaintCommand { + final CkPaint paint; + + CkDrawPaintCommand(this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawPaint(paint.skiaObject); + } +} + +class CkDrawVerticesCommand extends CkPaintCommand { + final CkVertices vertices; + final ui.BlendMode blendMode; + final CkPaint paint; + CkDrawVerticesCommand(this.vertices, this.blendMode, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawVertices( + vertices.skiaObject, + toSkBlendMode(blendMode), + paint.skiaObject, + ); + } +} + +class CkDrawPointsCommand extends CkPaintCommand { + final Float32List points; + final ui.PointMode pointMode; + final CkPaint paint; + CkDrawPointsCommand(this.pointMode, this.points, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawPoints( + toSkPointMode(pointMode), + points, + paint.skiaObject, + ); + } +} + +class CkDrawRectCommand extends CkPaintCommand { + final ui.Rect rect; + final CkPaint paint; + + CkDrawRectCommand(this.rect, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawRect(toSkRect(rect), paint.skiaObject); + } +} + +class CkDrawRRectCommand extends CkPaintCommand { + final ui.RRect rrect; + final CkPaint paint; + + CkDrawRRectCommand(this.rrect, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawRRect( + toSkRRect(rrect), + paint.skiaObject, + ); + } +} + +class CkDrawDRRectCommand extends CkPaintCommand { + final ui.RRect outer; + final ui.RRect inner; + final CkPaint paint; + + CkDrawDRRectCommand(this.outer, this.inner, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawDRRect( + toSkRRect(outer), + toSkRRect(inner), + paint.skiaObject, + ); + } +} + +class CkDrawOvalCommand extends CkPaintCommand { + final ui.Rect rect; + final CkPaint paint; + + CkDrawOvalCommand(this.rect, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawOval( + toSkRect(rect), + paint.skiaObject, + ); + } +} + +class CkDrawCircleCommand extends CkPaintCommand { + final ui.Offset c; + final double radius; + final CkPaint paint; + + CkDrawCircleCommand(this.c, this.radius, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawCircle( + c.dx, + c.dy, + radius, + paint.skiaObject, + ); + } +} + +class CkDrawPathCommand extends CkPaintCommand { + final CkPath path; + final CkPaint paint; + + CkDrawPathCommand(this.path, this.paint); + + @override + void apply(SkCanvas canvas) { + canvas.drawPath(path.skiaObject, paint.skiaObject); + } +} + +class CkDrawShadowCommand extends CkPaintCommand { + CkDrawShadowCommand( + this.path, this.color, this.elevation, this.transparentOccluder); + + final CkPath path; + final ui.Color color; + final double elevation; + final bool transparentOccluder; + + @override + void apply(SkCanvas canvas) { + drawSkShadow(canvas, path, color, elevation, transparentOccluder, + ui.window.devicePixelRatio); + } +} + +class CkDrawImageCommand extends CkPaintCommand { + final CkImage image; + final ui.Offset offset; + final CkPaint paint; + + CkDrawImageCommand(CkImage ckImage, this.offset, this.paint) + : image = ckImage.clone(); + + @override + void apply(SkCanvas canvas) { + final ui.FilterQuality filterQuality = paint.filterQuality; + if (filterQuality == ui.FilterQuality.high) { + canvas.drawImageCubic( + image.skImage, + offset.dx, + offset.dy, + CkCanvas._kMitchellNetravali_B, + CkCanvas._kMitchellNetravali_C, + paint.skiaObject, + ); + } else { + canvas.drawImageOptions( + image.skImage, + offset.dx, + offset.dy, + toSkFilterMode(filterQuality), + toSkMipmapMode(filterQuality), + paint.skiaObject, + ); + } + } + + @override + void dispose() { + image.dispose(); + } +} + +class CkDrawImageRectCommand extends CkPaintCommand { + final CkImage image; + final ui.Rect src; + final ui.Rect dst; + final CkPaint paint; + + CkDrawImageRectCommand(CkImage ckImage, this.src, this.dst, this.paint) + : image = ckImage.clone(); + + @override + void apply(SkCanvas canvas) { + final ui.FilterQuality filterQuality = paint.filterQuality; + if (filterQuality == ui.FilterQuality.high) { + canvas.drawImageRectCubic( + image.skImage, + toSkRect(src), + toSkRect(dst), + CkCanvas._kMitchellNetravali_B, + CkCanvas._kMitchellNetravali_C, + paint.skiaObject, + ); + } else { + canvas.drawImageRectOptions( + image.skImage, + toSkRect(src), + toSkRect(dst), + toSkFilterMode(filterQuality), + toSkMipmapMode(filterQuality), + paint.skiaObject, + ); + } + } + + @override + void dispose() { + image.dispose(); + } +} + +class CkDrawImageNineCommand extends CkPaintCommand { + CkDrawImageNineCommand(CkImage ckImage, this.center, this.dst, this.paint) + : image = ckImage.clone(); + + final CkImage image; + final ui.Rect center; + final ui.Rect dst; + final CkPaint paint; + + @override + void apply(SkCanvas canvas) { + canvas.drawImageNine( + image.skImage, + toSkRect(center), + toSkRect(dst), + toSkFilterMode(paint.filterQuality), + paint.skiaObject, + ); + } + + @override + void dispose() { + image.dispose(); + } +} + +class CkDrawParagraphCommand extends CkPaintCommand { + final CkParagraph paragraph; + final ui.Offset offset; + + CkDrawParagraphCommand(this.paragraph, this.offset); + + @override + void apply(SkCanvas canvas) { + canvas.drawParagraph( + paragraph.skiaObject, + offset.dx, + offset.dy, + ); + paragraph.markUsed(); + } +} + +class CkDrawPictureCommand extends CkPaintCommand { + CkDrawPictureCommand(this.picture); + + final CkPicture picture; + + @override + void apply(SkCanvas canvas) { + canvas.drawPicture(picture.skiaObject); + } +} + +class CkSaveLayerCommand extends CkPaintCommand { + CkSaveLayerCommand(this.bounds, this.paint); + + final ui.Rect bounds; + final CkPaint? paint; + + @override + void apply(SkCanvas canvas) { + canvas.saveLayer( + paint?.skiaObject, + toSkRect(bounds), + null, + null, + ); + } +} + +class CkSaveLayerWithoutBoundsCommand extends CkPaintCommand { + CkSaveLayerWithoutBoundsCommand(this.paint); + + final CkPaint? paint; + + @override + void apply(SkCanvas canvas) { + canvas.saveLayer( + paint?.skiaObject, + null, + null, + null, + ); + } +} + +class CkSaveLayerWithFilterCommand extends CkPaintCommand { + CkSaveLayerWithFilterCommand(this.bounds, this.filter, this.paint); + + final ui.Rect bounds; + final ui.ImageFilter filter; + final CkPaint? paint; + + @override + void apply(SkCanvas canvas) { + final CkManagedSkImageFilterConvertible convertible = + filter as CkManagedSkImageFilterConvertible; + return canvas.saveLayer( + paint?.skiaObject, + toSkRect(bounds), + convertible.imageFilter.skiaObject, + 0, + ); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index bca5a9b995472..61c391daeb924 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -6,9 +6,21 @@ /// /// Prefer keeping the original CanvasKit names so it is easier to locate /// the API behind these bindings in the Skia source code. +// ignore_for_file: non_constant_identifier_names -// @dart = 2.10 -part of engine; +// ignore_for_file: public_member_api_docs +@JS() +library canvaskit_api; + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js' as js; +import 'dart:typed_data'; + +import 'package:js/js.dart'; +import 'package:ui/ui.dart' as ui; + +import '../profiler.dart'; /// Entrypoint into the CanvasKit API. late CanvasKit canvasKit; @@ -17,8 +29,15 @@ late CanvasKit canvasKit; /// static APIs. /// /// See, e.g. [SkPaint]. +/// +/// This also acts as a cache of an initialized CanvasKit instance. We can use +/// this, for example, to perform a hot restart without needing to redownload +/// and reinitialize CanvasKit. +@JS('window.flutterCanvasKit') +external set windowFlutterCanvasKit(CanvasKit? value); + @JS('window.flutterCanvasKit') -external set windowFlutterCanvasKit(CanvasKit value); +external CanvasKit? get windowFlutterCanvasKit; @JS() @anonymous @@ -30,6 +49,8 @@ class CanvasKit { external SkFilterQualityEnum get FilterQuality; external SkBlurStyleEnum get BlurStyle; external SkTileModeEnum get TileMode; + external SkFilterModeEnum get FilterMode; + external SkMipmapModeEnum get MipmapMode; external SkFillTypeEnum get FillType; external SkAlphaTypeEnum get AlphaType; external SkColorTypeEnum get ColorType; @@ -41,23 +62,22 @@ class CanvasKit { external SkRectWidthStyleEnum get RectWidthStyle; external SkAffinityEnum get Affinity; external SkTextAlignEnum get TextAlign; + external SkTextHeightBehaviorEnum get TextHeightBehavior; external SkTextDirectionEnum get TextDirection; external SkFontWeightEnum get FontWeight; external SkFontSlantEnum get FontSlant; - external SkAnimatedImage MakeAnimatedImageFromEncoded(Uint8List imageData); - external SkShaderNamespace get SkShader; - external SkMaskFilter MakeBlurMaskFilter( - SkBlurStyle blurStyle, double sigma, bool respectCTM); - external SkColorFilterNamespace get SkColorFilter; - external SkImageFilterNamespace get SkImageFilter; - external SkPath MakePathFromOp(SkPath path1, SkPath path2, SkPathOp pathOp); + external SkAnimatedImage? MakeAnimatedImageFromEncoded(Uint8List imageData); + external SkShaderNamespace get Shader; + external SkMaskFilterNamespace get MaskFilter; + external SkColorFilterNamespace get ColorFilter; + external SkImageFilterNamespace get ImageFilter; + external SkPathNamespace get Path; external SkTonalColors computeTonalColors(SkTonalColors inTonalColors); - external SkVertices MakeSkVertices( + external SkVertices MakeVertices( SkVertexMode mode, - List positions, - List? textureCoordinates, - // TODO(yjbanov): make this Uint32Array when CanvasKit supports it. - List? colors, + Float32List positions, + Float32List? textureCoordinates, + Uint32List? colors, Uint16List? indices, ); external SkParagraphBuilderNamespace get ParagraphBuilder; @@ -68,7 +88,7 @@ class CanvasKit { int width, int height, ); - external Uint8List getSkDataBytes( + external Uint8List getDataBytes( SkData skData, ); @@ -79,7 +99,11 @@ class CanvasKit { external int get LineThroughDecoration; // End of text decoration enum. - external SkFontMgrNamespace get SkFontMgr; + external SkTextDecorationStyleEnum get DecorationStyle; + external SkTextBaselineEnum get TextBaseline; + external SkPlaceholderAlignmentEnum get PlaceholderAlignment; + + external SkFontMgrNamespace get FontMgr; external TypefaceFontProviderNamespace get TypefaceFontProvider; external int GetWebGLContext( html.CanvasElement canvas, SkWebGLContextOptions options); @@ -88,10 +112,23 @@ class CanvasKit { SkGrContext grContext, int width, int height, - SkColorSpace colorSpace, + ColorSpace colorSpace, ); external SkSurface MakeSWCanvasSurface(html.CanvasElement canvas); external void setCurrentContext(int glContext); + + /// Creates an image from decoded pixels represented as a list of bytes. + /// + /// The pixel data must be encoded according to the image info in [info]. + /// + /// Typically pixel data is obtained using [SkImage.readPixels]. The + /// parameters specified in [SkImageInfo] passed [SkImage.readPixels] must + /// match [info]. + external SkImage MakeImage( + SkImageInfo info, + Uint8List pixels, + int bytesPerRow, + ); } @JS('window.CanvasKitInit') @@ -114,23 +151,23 @@ class CanvasKitInitPromise { external void then(CanvasKitInitCallback callback); } -@JS('window.flutterCanvasKit.SkColorSpace.SRGB') -external SkColorSpace get SkColorSpaceSRGB; +@JS('window.flutterCanvasKit.ColorSpace.SRGB') +external ColorSpace get SkColorSpaceSRGB; @JS() -class SkColorSpace {} +class ColorSpace {} @JS() @anonymous class SkWebGLContextOptions { external factory SkWebGLContextOptions({ - required int anitalias, + required int antialias, // WebGL version: 1 or 2. required int majorVersion, }); } -@JS() +@JS('window.flutterCanvasKit.Surface') class SkSurface { external SkCanvas getCanvas(); external void flush(); @@ -149,12 +186,13 @@ class SkGrContext { } @JS() +@anonymous class SkFontSlantEnum { external SkFontSlant get Upright; external SkFontSlant get Italic; } -@JS() +@JS('window.flutterCanvasKit.FontSlant') class SkFontSlant { external int get value; } @@ -169,6 +207,7 @@ SkFontSlant toSkFontSlant(ui.FontStyle style) { } @JS() +@anonymous class SkFontWeightEnum { external SkFontWeight get Thin; external SkFontWeight get ExtraLight; @@ -272,11 +311,41 @@ SkTextAlign toSkTextAlign(ui.TextAlign align) { return _skTextAligns[align.index]; } +@JS() +class SkTextHeightBehaviorEnum { + external SkTextHeightBehavior get All; + external SkTextHeightBehavior get DisableFirstAscent; + external SkTextHeightBehavior get DisableLastDescent; + external SkTextHeightBehavior get DisableAll; +} + +@JS() +class SkTextHeightBehavior { + external int get value; +} + +final List _skTextHeightBehaviors = + [ + canvasKit.TextHeightBehavior.All, + canvasKit.TextHeightBehavior.DisableFirstAscent, + canvasKit.TextHeightBehavior.DisableLastDescent, + canvasKit.TextHeightBehavior.DisableAll, +]; + +SkTextHeightBehavior toSkTextHeightBehavior(ui.TextHeightBehavior behavior) { + final int index = (behavior.applyHeightToFirstAscent ? 0 : 1 << 0) | + (behavior.applyHeightToLastDescent ? 0 : 1 << 1); + return _skTextHeightBehaviors[index]; +} + @JS() class SkRectHeightStyleEnum { - // TODO(yjbanov): support all styles external SkRectHeightStyle get Tight; external SkRectHeightStyle get Max; + external SkRectHeightStyle get IncludeLineSpacingMiddle; + external SkRectHeightStyle get IncludeLineSpacingTop; + external SkRectHeightStyle get IncludeLineSpacingBottom; + external SkRectHeightStyle get Strut; } @JS() @@ -287,11 +356,14 @@ class SkRectHeightStyle { final List _skRectHeightStyles = [ canvasKit.RectHeightStyle.Tight, canvasKit.RectHeightStyle.Max, + canvasKit.RectHeightStyle.IncludeLineSpacingMiddle, + canvasKit.RectHeightStyle.IncludeLineSpacingTop, + canvasKit.RectHeightStyle.IncludeLineSpacingBottom, + canvasKit.RectHeightStyle.Strut, ]; SkRectHeightStyle toSkRectHeightStyle(ui.BoxHeightStyle style) { - final int index = style.index; - return _skRectHeightStyles[index < 2 ? index : 0]; + return _skRectHeightStyles[style.index]; } @JS() @@ -616,6 +688,7 @@ class SkTileModeEnum { external SkTileMode get Clamp; external SkTileMode get Repeat; external SkTileMode get Mirror; + external SkTileMode get Decal; } @JS() @@ -627,12 +700,48 @@ final List _skTileModes = [ canvasKit.TileMode.Clamp, canvasKit.TileMode.Repeat, canvasKit.TileMode.Mirror, + canvasKit.TileMode.Decal, ]; SkTileMode toSkTileMode(ui.TileMode mode) { return _skTileModes[mode.index]; } +@JS() +class SkFilterModeEnum { + external SkFilterMode get Nearest; + external SkFilterMode get Linear; +} + +@JS() +class SkFilterMode { + external int get value; +} + +SkFilterMode toSkFilterMode(ui.FilterQuality filterQuality) { + return filterQuality == ui.FilterQuality.none + ? canvasKit.FilterMode.Nearest + : canvasKit.FilterMode.Linear; +} + +@JS() +class SkMipmapModeEnum { + external SkMipmapMode get None; + external SkMipmapMode get Nearest; + external SkMipmapMode get Linear; +} + +@JS() +class SkMipmapMode { + external int get value; +} + +SkMipmapMode toSkMipmapMode(ui.FilterQuality filterQuality) { + return filterQuality == ui.FilterQuality.medium + ? canvasKit.MipmapMode.Linear + : canvasKit.MipmapMode.None; +} + @JS() class SkAlphaTypeEnum { external SkAlphaType get Opaque; @@ -673,16 +782,15 @@ class SkAnimatedImage { /// Returns duration in milliseconds. external int getRepetitionCount(); external int decodeNextFrame(); - external SkImage getCurrentFrame(); + external SkImage makeImageAtCurrentFrame(); external int width(); external int height(); - external Uint8List readPixels(SkImageInfo imageInfo, int srcX, int srcY); - external SkData encodeToData(); /// Deletes the C++ object. /// /// This object is no longer usable after calling this method. external void delete(); + external bool isDeleted(); } @JS() @@ -691,13 +799,24 @@ class SkImage { external void delete(); external int width(); external int height(); - external SkShader makeShader( + external SkShader makeShaderCubic( SkTileMode tileModeX, SkTileMode tileModeY, + double B, + double C, Float32List? matrix, // 3x3 matrix ); - external Uint8List readPixels(SkImageInfo imageInfo, int srcX, int srcY); - external SkData encodeToData(); + external SkShader makeShaderOptions( + SkTileMode tileModeX, + SkTileMode tileModeY, + SkFilterMode filterMode, + SkMipmapMode mipmapMode, + Float32List? matrix, // 3x3 matrix + ); + external Uint8List readPixels(int srcX, int srcY, SkImageInfo imageInfo); + external Uint8List? encodeToBytes(); + external bool isAliasOf(SkImage other); + external bool isDeleted(); } @JS() @@ -705,15 +824,16 @@ class SkShaderNamespace { external SkShader MakeLinearGradient( Float32List from, // 2-element array Float32List to, // 2-element array - List colors, + Uint32List colors, Float32List colorStops, SkTileMode tileMode, + Float32List? matrix, ); external SkShader MakeRadialGradient( Float32List center, // 2-element array double radius, - List colors, + Uint32List colors, Float32List colorStops, SkTileMode tileMode, Float32List? matrix, // 3x3 matrix @@ -725,7 +845,7 @@ class SkShaderNamespace { double focalRadius, Float32List center, double radius, - List colors, + Uint32List colors, Float32List colorStops, SkTileMode tileMode, Float32List? matrix, // 3x3 matrix @@ -735,7 +855,7 @@ class SkShaderNamespace { external SkShader MakeSweepGradient( double cx, double cy, - List colors, + Uint32List colors, Float32List colorStops, SkTileMode tileMode, Float32List? matrix, // 3x3 matrix @@ -751,11 +871,20 @@ class SkShader { external void delete(); } +@JS() +class SkMaskFilterNamespace { + // Creates a blur MaskFilter. + // + // Returns `null` if [sigma] is 0 or infinite. + external SkMaskFilter? MakeBlur( + SkBlurStyle blurStyle, double sigma, bool respectCTM); +} + // This needs to be bound to top-level because SkPaint is initialized // with `new`. Also in Dart you can't write this: // // external SkPaint SkPaint(); -@JS('window.flutterCanvasKit.SkPaint') +@JS('window.flutterCanvasKit.Paint') class SkPaint { // TODO(yjbanov): implement invertColors, see paint.cc external SkPaint(); @@ -768,7 +897,6 @@ class SkPaint { external void setColorInt(int color); external void setShader(SkShader? shader); external void setMaskFilter(SkMaskFilter? maskFilter); - external void setFilterQuality(SkFilterQuality filterQuality); external void setColorFilter(SkColorFilter? colorFilter); external void setStrokeMiter(double miterLimit); external void setImageFilter(SkImageFilter? imageFilter); @@ -803,13 +931,23 @@ class SkImageFilterNamespace { double sigmaX, double sigmaY, SkTileMode tileMode, - Null input, // we don't use this yet + void input, // we don't use this yet ); external SkImageFilter MakeMatrixTransform( Float32List matrix, // 3x3 matrix SkFilterQuality filterQuality, - Null input, // we don't use this yet + void input, // we don't use this yet + ); + + external SkImageFilter MakeColorFilter( + SkColorFilter colorFilter, + void input, // we don't use this yet + ); + + external SkImageFilter MakeCompose( + SkImageFilter outer, + SkImageFilter inner, ); } @@ -819,6 +957,15 @@ class SkImageFilter { external void delete(); } +@JS() +class SkPathNamespace { + /// Creates an [SkPath] using commands obtained from [SkPath.toCmds]. + external SkPath MakeFromCmds(List pathCommands); + + /// Creates an [SkPath] by combining [path1] and [path2] using [pathOp]. + external SkPath MakeFromOp(SkPath path1, SkPath path2, SkPathOp pathOp); +} + // Mappings from SkMatrix-index to input-index. const List _skMatrixIndexToMatrix4Index = [ 0, 4, 12, // Row 1 @@ -973,50 +1120,17 @@ Float32List toSharedSkColor3(ui.Color color) { final SkFloat32List _sharedSkColor3 = mallocFloat32List(4); -Uint32List toSkIntColorList(List colors) { - final int len = colors.length; - final Uint32List result = Uint32List(len); - for (int i = 0; i < len; i++) { - result[i] = colors[i].value; - } - return result; -} - -List toSkFloatColorList(List colors) { - final int len = colors.length; - final List result = []; - for (int i = 0; i < len; i++) { - final Float32List array = Float32List(4); - final ui.Color color = colors[i]; - array[0] = color.red / 255.0; - array[1] = color.green / 255.0; - array[2] = color.blue / 255.0; - array[3] = color.alpha / 255.0; - result.add(array); - } - return result; -} - -List encodeRawColorList(Int32List rawColors) { - final int colorCount = rawColors.length; - final List colors = []; - for (int i = 0; i < colorCount; ++i) { - colors.add(ui.Color(rawColors[i])); - } - return toSkFloatColorList(colors); -} - -@JS('window.flutterCanvasKit.SkPath') +@JS('window.flutterCanvasKit.Path') class SkPath { external SkPath([SkPath? other]); external void setFillType(SkFillType fillType); external void addArc( - SkRect oval, + Float32List oval, double startAngleDegrees, double sweepAngleDegrees, ); external void addOval( - SkRect oval, + Float32List oval, bool counterClockWise, int startIndex, ); @@ -1037,16 +1151,15 @@ class SkPath { Float32List points, bool close, ); - external void addRoundRect( - SkRect outerRect, - Float32List radii, + external void addRRect( + Float32List rrect, bool counterClockWise, ); external void addRect( - SkRect rect, + Float32List rect, ); external void arcToOval( - SkRect oval, + Float32List oval, double startAngleDegrees, double sweepAngleDegrees, bool forceMoveTo, @@ -1080,7 +1193,7 @@ class SkPath { double x3, double y3, ); - external SkRect getBounds(); + external Float32List getBounds(); external void lineTo(double x, double y); external void moveTo(double x, double y); external void quadTo( @@ -1136,12 +1249,21 @@ class SkPath { double pers1, double pers2, ); + + /// Serializes the path into a list of commands. + /// + /// The list can be used to create a new [SkPath] using + /// [CanvasKit.Path.MakeFromCmds]. + external List toCmds(); + + external void delete(); } -@JS('window.flutterCanvasKit.SkContourMeasureIter') +@JS('window.flutterCanvasKit.ContourMeasureIter') class SkContourMeasureIter { - external SkContourMeasureIter(SkPath path, bool forceClosed, int startIndex); + external SkContourMeasureIter(SkPath path, bool forceClosed, double resScale); external SkContourMeasure? next(); + external void delete(); } @JS() @@ -1150,90 +1272,49 @@ class SkContourMeasure { external Float32List getPosTan(double distance); external bool isClosed(); external double length(); + external void delete(); } -@JS() -@anonymous -class SkRect { - external factory SkRect({ - required double fLeft, - required double fTop, - required double fRight, - required double fBottom, - }); - external double get fLeft; - external double get fTop; - external double get fRight; - external double get fBottom; -} - -extension SkRectExtensions on SkRect { - ui.Rect toRect() { - return ui.Rect.fromLTRB( - this.fLeft, - this.fTop, - this.fRight, - this.fBottom, - ); - } -} - -SkRect toSkRect(ui.Rect rect) { - return SkRect( - fLeft: rect.left, - fTop: rect.top, - fRight: rect.right, - fBottom: rect.bottom, - ); -} - -@JS() -@anonymous -class SkRRect { - external factory SkRRect({ - required SkRect rect, - required double rx1, - required double ry1, - required double rx2, - required double ry2, - required double rx3, - required double ry3, - required double rx4, - required double ry4, - }); - - external SkRect get rect; - external double get rx1; - external double get ry1; - external double get rx2; - external double get ry2; - external double get rx3; - external double get ry3; - external double get rx4; - external double get ry4; -} - -SkRRect toSkRRect(ui.RRect rrect) { - return SkRRect( - rect: toOuterSkRect(rrect), - rx1: rrect.tlRadiusX, - ry1: rrect.tlRadiusY, - rx2: rrect.trRadiusX, - ry2: rrect.trRadiusY, - rx3: rrect.brRadiusX, - ry3: rrect.brRadiusY, - rx4: rrect.blRadiusX, - ry4: rrect.blRadiusY, - ); -} - -SkRect toOuterSkRect(ui.RRect rrect) { - return SkRect( - fLeft: rrect.left, - fTop: rrect.top, - fRight: rrect.right, - fBottom: rrect.bottom, - ); +// TODO(hterkelsen): Use a shared malloc'ed array for performance. +Float32List toSkRect(ui.Rect rect) { + final Float32List skRect = Float32List(4); + skRect[0] = rect.left; + skRect[1] = rect.top; + skRect[2] = rect.right; + skRect[3] = rect.bottom; + return skRect; +} + +ui.Rect fromSkRect(Float32List skRect) { + return ui.Rect.fromLTRB(skRect[0], skRect[1], skRect[2], skRect[3]); +} + +// TODO(hterkelsen): Use a shared malloc'ed array for performance. +Float32List toSkRRect(ui.RRect rrect) { + final Float32List skRRect = Float32List(12); + skRRect[0] = rrect.left; + skRRect[1] = rrect.top; + skRRect[2] = rrect.right; + skRRect[3] = rrect.bottom; + skRRect[4] = rrect.tlRadiusX; + skRRect[5] = rrect.tlRadiusY; + skRRect[6] = rrect.trRadiusX; + skRRect[7] = rrect.trRadiusY; + skRRect[8] = rrect.brRadiusX; + skRRect[9] = rrect.brRadiusY; + skRRect[10] = rrect.blRadiusX; + skRRect[11] = rrect.blRadiusY; + return skRRect; +} + +// TODO(hterkelsen): Use a shared malloc'ed array for performance. +Float32List toOuterSkRect(ui.RRect rrect) { + final Float32List skRect = Float32List(4); + skRect[0] = rrect.left; + skRect[1] = rrect.top; + skRect[2] = rrect.right; + skRect[3] = rrect.bottom; + return skRect; } /// Encodes a list of offsets to CanvasKit-compatible point array. @@ -1253,32 +1334,23 @@ SkFloat32List toMallocedSkPoints(List points) { return skPoints; } -// TODO(yjbanov): this is inefficient. We should be able to pass points -// as Float32List without a conversion. -List rawPointsToSkPoints2d(Float32List points) { - assert(points.length % 2 == 0); - final int pointLength = points.length ~/ 2; - final List result = []; - for (var i = 0; i < pointLength; i++) { - var x = i * 2; - var y = x + 1; - final Float32List skPoint = Float32List(2); - skPoint[0] = points[x]; - skPoint[1] = points[y]; - result.add(skPoint); +/// Converts a list of [ui.Offset] into a flat list of points. +Float32List toFlatSkPoints(List points) { + final int len = points.length; + final Float32List result = Float32List(len * 2); + for (int i = 0; i < len; i++) { + result[2 * i] = points[i].dx; + result[2 * i + 1] = points[i].dy; } return result; } -List toSkPoints2d(List offsets) { - final int len = offsets.length; - final List result = []; - for (var i = 0; i < len; i++) { - final ui.Offset offset = offsets[i]; - final Float32List skPoint = Float32List(2); - skPoint[0] = offset.dx; - skPoint[1] = offset.dy; - result.add(skPoint); +/// Converts a list of [ui.Color] into a flat list of ints. +Uint32List toFlatColors(List colors) { + final int len = colors.length; + final Uint32List result = Uint32List(len); + for (int i = 0; i < len; i++) { + result[i] = colors[i].value; } return result; } @@ -1292,10 +1364,10 @@ Uint16List toUint16List(List ints) { return result; } -@JS('window.flutterCanvasKit.SkPictureRecorder') +@JS('window.flutterCanvasKit.PictureRecorder') class SkPictureRecorder { external SkPictureRecorder(); - external SkCanvas beginRecording(SkRect bounds); + external SkCanvas beginRecording(Float32List bounds); external SkPicture finishRecordingAsPicture(); external void delete(); } @@ -1315,17 +1387,17 @@ class SkCanvas { bool doAntiAlias, ); external void clipRRect( - SkRRect rrect, + Float32List rrect, SkClipOp clipOp, bool doAntiAlias, ); external void clipRect( - SkRect rrect, + Float32List rrect, SkClipOp clipOp, bool doAntiAlias, ); external void drawArc( - SkRect oval, + Float32List oval, double startAngleDegrees, double sweepAngleDegrees, bool useCenter, @@ -1337,7 +1409,7 @@ class SkCanvas { Float32List rstTransforms, SkPaint paint, SkBlendMode blendMode, - List? colors, + Uint32List? colors, ); external void drawCircle( double x, @@ -1350,27 +1422,47 @@ class SkCanvas { SkBlendMode blendMode, ); external void drawDRRect( - SkRRect outer, - SkRRect inner, + Float32List outer, + Float32List inner, + SkPaint paint, + ); + external void drawImageCubic( + SkImage image, + double x, + double y, + double B, + double C, SkPaint paint, ); - external void drawImage( + external void drawImageOptions( SkImage image, double x, double y, + SkFilterMode filterMode, + SkMipmapMode mipmapMode, SkPaint paint, ); - external void drawImageRect( + external void drawImageRectCubic( SkImage image, - SkRect src, - SkRect dst, + Float32List src, + Float32List dst, + double B, + double C, + SkPaint paint, + ); + external void drawImageRectOptions( + SkImage image, + Float32List src, + Float32List dst, + SkFilterMode filterMode, + SkMipmapMode mipmapMode, SkPaint paint, - bool fastSample, ); external void drawImageNine( SkImage image, - SkRect center, - SkRect dst, + Float32List center, + Float32List dst, + SkFilterMode filterMode, SkPaint paint, ); external void drawLine( @@ -1381,7 +1473,7 @@ class SkCanvas { SkPaint paint, ); external void drawOval( - SkRect rect, + Float32List rect, SkPaint paint, ); external void drawPaint( @@ -1397,11 +1489,11 @@ class SkCanvas { SkPaint paint, ); external void drawRRect( - SkRRect rrect, + Float32List rrect, SkPaint paint, ); external void drawRect( - SkRect rrect, + Float32List rrect, SkPaint paint, ); external void drawShadow( @@ -1421,8 +1513,10 @@ class SkCanvas { external int save(); external int getSaveCount(); external void saveLayer( - SkRect bounds, - SkPaint paint, + SkPaint? paint, + Float32List? bounds, + SkImageFilter? backdrop, + int? flags, ); external void restore(); external void restoreToCount(int count); @@ -1435,7 +1529,6 @@ class SkCanvas { external void skew(double x, double y); external void concat(Float32List matrix); external void translate(double x, double y); - external void flush(); external void drawPicture(SkPicture picture); external void drawParagraph( SkParagraph paragraph, @@ -1444,23 +1537,6 @@ class SkCanvas { ); } -@JS() -@anonymous -class SkCanvasSaveLayerWithoutBoundsOverload { - external void saveLayer(SkPaint paint); -} - -@JS() -@anonymous -class SkCanvasSaveLayerWithFilterOverload { - external void saveLayer( - SkPaint? paint, - SkImageFilter? imageFilter, - int flags, - SkRect rect, - ); -} - @JS() @anonymous class SkPicture { @@ -1489,6 +1565,13 @@ class SkParagraphBuilder { external void pushPaintStyle( SkTextStyle textStyle, SkPaint foreground, SkPaint background); external void pop(); + external void addPlaceholder( + double width, + double height, + SkPlaceholderAlignment alignment, + SkTextBaseline baseline, + double offset, + ); external SkParagraph build(); external void delete(); } @@ -1500,74 +1583,172 @@ class SkParagraphStyle {} @JS() @anonymous class SkParagraphStyleProperties { - external SkTextAlign? get textAlign; external set textAlign(SkTextAlign? value); - - external SkTextDirection? get textDirection; external set textDirection(SkTextDirection? value); - - external double? get heightMultiplier; external set heightMultiplier(double? value); - - external int? get textHeightBehavior; - external set textHeightBehavior(int? value); - - external int? get maxLines; + external set textHeightBehavior(SkTextHeightBehavior? value); external set maxLines(int? value); - - external String? get ellipsis; external set ellipsis(String? value); - - external SkTextStyleProperties? get textStyle; external set textStyle(SkTextStyleProperties? value); + external set strutStyle(SkStrutStyleProperties? strutStyle); } @JS() class SkTextStyle {} +@JS() +class SkTextDecorationStyleEnum { + external SkTextDecorationStyle get Solid; + external SkTextDecorationStyle get Double; + external SkTextDecorationStyle get Dotted; + external SkTextDecorationStyle get Dashed; + external SkTextDecorationStyle get Wavy; +} + +@JS() +class SkTextDecorationStyle { + external int get value; +} + +final List _skTextDecorationStyles = + [ + canvasKit.DecorationStyle.Solid, + canvasKit.DecorationStyle.Double, + canvasKit.DecorationStyle.Dotted, + canvasKit.DecorationStyle.Dashed, + canvasKit.DecorationStyle.Wavy, +]; + +SkTextDecorationStyle toSkTextDecorationStyle(ui.TextDecorationStyle style) { + return _skTextDecorationStyles[style.index]; +} + +@JS() +class SkTextBaselineEnum { + external SkTextBaseline get Alphabetic; + external SkTextBaseline get Ideographic; +} + +@JS() +class SkTextBaseline { + external int get value; +} + +final List _skTextBaselines = [ + canvasKit.TextBaseline.Alphabetic, + canvasKit.TextBaseline.Ideographic, +]; + +SkTextBaseline toSkTextBaseline(ui.TextBaseline baseline) { + return _skTextBaselines[baseline.index]; +} + +@JS() +class SkPlaceholderAlignmentEnum { + external SkPlaceholderAlignment get Baseline; + external SkPlaceholderAlignment get AboveBaseline; + external SkPlaceholderAlignment get BelowBaseline; + external SkPlaceholderAlignment get Top; + external SkPlaceholderAlignment get Bottom; + external SkPlaceholderAlignment get Middle; +} + +@JS() +class SkPlaceholderAlignment { + external int get value; +} + +final List _skPlaceholderAlignments = + [ + canvasKit.PlaceholderAlignment.Baseline, + canvasKit.PlaceholderAlignment.AboveBaseline, + canvasKit.PlaceholderAlignment.BelowBaseline, + canvasKit.PlaceholderAlignment.Top, + canvasKit.PlaceholderAlignment.Bottom, + canvasKit.PlaceholderAlignment.Middle, +]; + +SkPlaceholderAlignment toSkPlaceholderAlignment( + ui.PlaceholderAlignment alignment) { + return _skPlaceholderAlignments[alignment.index]; +} + @JS() @anonymous class SkTextStyleProperties { - external Float32List? get backgroundColor; external set backgroundColor(Float32List? value); - - external Float32List? get color; external set color(Float32List? value); - - external Float32List? get foregroundColor; external set foregroundColor(Float32List? value); - - external int? get decoration; external set decoration(int? value); - - external double? get decorationThickness; external set decorationThickness(double? value); - - external double? get fontSize; + external set decorationColor(Float32List? value); + external set decorationStyle(SkTextDecorationStyle? value); + external set textBaseline(SkTextBaseline? value); external set fontSize(double? value); - - external List? get fontFamilies; + external set letterSpacing(double? value); + external set wordSpacing(double? value); + external set heightMultiplier(double? value); + external set halfLeading(bool? value); + external set locale(String? value); external set fontFamilies(List? value); + external set fontStyle(SkFontStyle? value); + external set shadows(List? value); + external set fontFeatures(List? value); +} - external SkFontStyle? get fontStyle; +@JS() +@anonymous +class SkStrutStyleProperties { + external set fontFamilies(List? value); external set fontStyle(SkFontStyle? value); + external set fontSize(double? value); + external set heightMultiplier(double? value); + external set halfLeading(bool? value); + external set leading(double? value); + external set strutEnabled(bool? value); + external set forceStrutHeight(bool? value); } @JS() @anonymous class SkFontStyle { - external SkFontWeight? get weight; external set weight(SkFontWeight? value); - - external SkFontSlant? get slant; external set slant(SkFontSlant? value); } +@JS() +@anonymous +class SkTextShadow { + external set color(Float32List? value); + external set offset(Float32List? value); + external set blurRadius(double? value); +} + +@JS() +@anonymous +class SkFontFeature { + external set name(String? value); + external set value(int? value); +} + +@JS() +@anonymous +class SkTypeface {} + +@JS('window.flutterCanvasKit.Font') +class SkFont { + external SkFont(SkTypeface typeface); + external Uint8List getGlyphIDs(String text); + external void getGlyphBounds( + List glyphs, SkPaint? paint, Uint8List? output); +} + @JS() @anonymous class SkFontMgr { external String? getFamilyName(int fontId); external void delete(); + external SkTypeface? MakeTypefaceFromData(Uint8List font); } @JS('window.flutterCanvasKit.TypefaceFontProvider') @@ -1576,6 +1757,23 @@ class TypefaceFontProvider extends SkFontMgr { external void registerFont(Uint8List font, String family); } +@JS() +@anonymous +class SkLineMetrics { + external int get startIndex; + external int get endIndex; + external int get endExcludingWhitespaces; + external int get endIncludingNewline; + external bool get isHardBreak; + external double get ascent; + external double get descent; + external double get height; + external double get width; + external double get left; + external double get baseline; + external int get lineNumber; +} + @JS() @anonymous class SkParagraph { @@ -1583,16 +1781,18 @@ class SkParagraph { external bool didExceedMaxLines(); external double getHeight(); external double getIdeographicBaseline(); + external List getLineMetrics(); external double getLongestLine(); external double getMaxIntrinsicWidth(); external double getMinIntrinsicWidth(); external double getMaxWidth(); - external List getRectsForRange( + external List getRectsForRange( int start, int end, SkRectHeightStyle heightStyle, SkRectWidthStyle widthStyle, ); + external List getRectsForPlaceholders(); external SkTextPosition getGlyphPositionAtCoordinate( double x, double y, @@ -1635,6 +1835,7 @@ class SkTonalColors { class SkFontMgrNamespace { // TODO(yjbanov): can this be made non-null? It returns null in our unit-tests right now. external SkFontMgr? FromData(List fonts); + external SkFontMgr RefDefault(); } @JS() @@ -1642,41 +1843,171 @@ class TypefaceFontProviderNamespace { external TypefaceFontProvider Make(); } -Timer? _skObjectCollector; -List _skObjectDeleteQueue = []; +/// Collects Skia objects that are no longer necessary. +abstract class Collector { + /// The production collector implementation. + static final Collector _productionInstance = ProductionCollector(); -final SkObjectFinalizationRegistry skObjectFinalizationRegistry = SkObjectFinalizationRegistry(js.allowInterop((SkDeletable deletable) { - _skObjectDeleteQueue.add(deletable); - _skObjectCollector ??= _scheduleSkObjectCollection(); -})); + /// The collector implementation currently in use. + static Collector get instance => _instance; + static Collector _instance = _productionInstance; -/// Schedules an asap timer to delete garbage-collected Skia objects. -/// -/// We use a timer for the following reasons: + /// In tests overrides the collector implementation. + static void debugOverrideCollector(Collector override) { + _instance = override; + } + + /// In tests restores the collector to the production implementation. + static void debugRestoreCollector() { + _instance = _productionInstance; + } + + /// Registers a [deletable] for collection when the [wrapper] object is + /// garbage collected. + /// + /// The [debugLabel] is used to track the origin of the deletable. + void register(Object wrapper, SkDeletable deletable); + + /// Deletes the [deletable]. + /// + /// The exact timing of the deletion is implementation-specific. For example, + /// a production implementation may want to batch deletables and schedule a + /// timer to collect them instead of deleting right away. + /// + /// A test implementation may want a collection strategy that's less efficient + /// but more predictable. + void collect(SkDeletable deletable); +} + +/// Uses the browser's real `FinalizationRegistry` to collect objects. /// -/// - Deleting the object immediately may lead to dangling pointer as the Skia -/// object may still be used by a function in the current frame. For example, -/// a `CkPaint` + `SkPaint` pair may be created by the framework, passed to -/// the engine, and the `CkPaint` dropped immediately. Because GC can kick in -/// any time, including in the middle of the event, we may delete `SkPaint` -/// prematurely. -/// - A microtask, while solves the problem above, would prevent the event from -/// yielding to the graphics system to render the frame on the screen if there -/// is a large number of objects to delete, causing jank. -Timer _scheduleSkObjectCollection() => Timer(Duration.zero, () { - html.window.performance.mark('SkObject collection-start'); - final int length = _skObjectDeleteQueue.length; - for (int i = 0; i < length; i++) { - _skObjectDeleteQueue[i].delete(); +/// Uses timers to delete objects in batches and outside the animation frame. +class ProductionCollector implements Collector { + ProductionCollector() { + _skObjectFinalizationRegistry = + SkObjectFinalizationRegistry(js.allowInterop((SkDeletable deletable) { + // This is called when GC decides to collect the wrapper object and + // notify us, which may happen after the object is already deleted + // explicitly, e.g. when its ref count drops to zero. When that happens + // skip collection of this object. + if (!deletable.isDeleted()) { + collect(deletable); + } + })); + } + + late final SkObjectFinalizationRegistry _skObjectFinalizationRegistry; + List _skiaObjectCollectionQueue = []; + Timer? _skiaObjectCollectionTimer; + + @override + void register(Object wrapper, SkDeletable deletable) { + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${deletable.constructor.name} registered', + ); + } + _skObjectFinalizationRegistry.register(wrapper, deletable); } - _skObjectDeleteQueue = []; - // Null out the timer so we can schedule a new one next time objects are - // scheduled for deletion. - _skObjectCollector = null; - html.window.performance.mark('SkObject collection-end'); - html.window.performance.measure('SkObject collection', 'SkObject collection-start', 'SkObject collection-end'); -}); + /// Schedules a Skia object for deletion in an asap timer. + /// + /// A timer is used for the following reasons: + /// + /// - Deleting the object immediately may lead to dangling pointer as the Skia + /// object may still be used by a function in the current frame. For example, + /// a `CkPaint` + `SkPaint` pair may be created by the framework, passed to + /// the engine, and the `CkPaint` dropped immediately. Because GC can kick in + /// any time, including in the middle of the event, we may delete `SkPaint` + /// prematurely. + /// - A microtask, while solves the problem above, would prevent the event from + /// yielding to the graphics system to render the frame on the screen if there + /// is a large number of objects to delete, causing jank. + /// + /// Because scheduling a timer is expensive, the timer is shared by all objects + /// deleted this frame. No timer is created if no objects were scheduled for + /// deletion. + @override + void collect(SkDeletable deletable) { + assert( + !deletable.isDeleted(), + 'Attempted to delete an already deleted Skia object.', + ); + _skiaObjectCollectionQueue.add(deletable); + + _skiaObjectCollectionTimer ??= Timer(Duration.zero, () { + // Null out the timer so we can schedule a new one next time objects are + // scheduled for deletion. + _skiaObjectCollectionTimer = null; + collectSkiaObjectsNow(); + }); + } + + /// Deletes all Skia objects pending deletion synchronously. + /// + /// After calling this method [_skiaObjectCollectionQueue] is empty. + /// + /// Throws a [SkiaObjectCollectionError] if CanvasKit fails to delete at least + /// one object. The error is populated with information about the first failed + /// object. Upon an error the collection continues and the collection queue is + /// emptied out to prevent memory leaks. This may happen, for example, when the + /// same object is deleted more than once. + void collectSkiaObjectsNow() { + html.window.performance.mark('SkObject collection-start'); + final int length = _skiaObjectCollectionQueue.length; + dynamic firstError; + StackTrace? firstStackTrace; + for (int i = 0; i < length; i++) { + final SkDeletable deletable = _skiaObjectCollectionQueue[i]; + if (deletable.isDeleted()) { + // Some Skia objects are ref counted and are deleted before GC and/or + // the collection timer begins collecting them. So we have to check + // again if the objects is worth collecting. + continue; + } + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${deletable.constructor.name} deleted', + ); + } + try { + deletable.delete(); + } catch (error, stackTrace) { + // Remember the error, but keep going. If for some reason CanvasKit fails + // to delete an object we still want to delete other objects and empty + // out the queue. Otherwise, the queue will never be flushed and keep + // accumulating objects, a.k.a. memory leak. + if (firstError == null) { + firstError = error; + firstStackTrace = stackTrace; + } + } + } + _skiaObjectCollectionQueue = []; + + html.window.performance.mark('SkObject collection-end'); + html.window.performance.measure('SkObject collection', + 'SkObject collection-start', 'SkObject collection-end'); + + // It's safe to throw the error here, now that we've processed the queue. + if (firstError != null) { + throw SkiaObjectCollectionError(firstError, firstStackTrace); + } + } +} + +/// Thrown by [ProductionCollector] when Skia object collection fails. +class SkiaObjectCollectionError implements Error { + SkiaObjectCollectionError(this.error, this.stackTrace); + + final dynamic error; + + @override + final StackTrace? stackTrace; + + @override + String toString() => 'SkiaObjectCollectionError: $error\n$stackTrace'; +} /// Any Skia object that has a `delete` method. @JS() @@ -1684,6 +2015,24 @@ Timer _scheduleSkObjectCollection() => Timer(Duration.zero, () { class SkDeletable { /// Deletes the C++ side object. external void delete(); + + /// Returns whether the correcponding C++ object has been deleted. + external bool isDeleted(); + + /// Returns the JavaScript constructor for this object. + /// + /// This is useful for debugging. + external JsConstructor get constructor; +} + +@JS() +@anonymous +class JsConstructor { + /// The name of the "constructor", typically the function name called with + /// the `new` keyword, or the ES6 class name. + /// + /// This is useful for debugging. + external String get name; } /// Attaches a weakly referenced object to another object and calls a finalizer @@ -1712,13 +2061,21 @@ class SkObjectFinalizationRegistry { external Object? get _finalizationRegistryConstructor; /// Whether the current browser supports `FinalizationRegistry`. -bool browserSupportsFinalizationRegistry = _finalizationRegistryConstructor != null; +bool browserSupportsFinalizationRegistry = + _finalizationRegistryConstructor != null; + +/// Sets the value of [browserSupportsFinalizationRegistry] to its true value. +void debugResetBrowserSupportsFinalizationRegistry() { + browserSupportsFinalizationRegistry = + _finalizationRegistryConstructor != null; +} @JS() class SkData { external int size(); external bool isEmpty(); external Uint8List bytes(); + external void delete(); } @JS() @@ -1728,19 +2085,19 @@ class SkImageInfo { required int width, required int height, SkAlphaType alphaType, - SkColorSpace colorSpace, + ColorSpace colorSpace, SkColorType colorType, }); external SkAlphaType get alphaType; - external SkColorSpace get colorSpace; + external ColorSpace get colorSpace; external SkColorType get colorType; external int get height; external bool get isEmpty; external bool get isOpaque; - external SkRect get bounds; + external Float32List get bounds; external int get width; external SkImageInfo makeAlphaType(SkAlphaType alphaType); - external SkImageInfo makeColorSpace(SkColorSpace colorSpace); + external SkImageInfo makeColorSpace(ColorSpace colorSpace); external SkImageInfo makeColorType(SkColorType colorType); external SkImageInfo makeWH(int width, int height); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart index 26f534c386c7f..724d038beddc8 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart @@ -2,8 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show toMatrix32; +import '../validators.dart'; +import 'canvas.dart'; +import 'canvaskit_api.dart'; +import 'image.dart'; +import 'painting.dart'; +import 'path.dart'; +import 'picture.dart'; +import 'picture_recorder.dart'; +import 'text.dart'; +import 'vertices.dart'; /// An implementation of [ui.Canvas] that is backed by a CanvasKit canvas. class CanvasKitCanvas implements ui.Canvas { @@ -16,8 +29,8 @@ class CanvasKitCanvas implements ui.Canvas { '"recorder" must not already be associated with another Canvas.'); } cullRect ??= ui.Rect.largest; - final CkPictureRecorder skRecorder = recorder as CkPictureRecorder; - return CanvasKitCanvas._(skRecorder.beginRecording(cullRect)); + final CkPictureRecorder ckRecorder = recorder as CkPictureRecorder; + return CanvasKitCanvas._(ckRecorder.beginRecording(cullRect)); } CanvasKitCanvas._(this._canvas); @@ -120,11 +133,7 @@ class CanvasKitCanvas implements ui.Canvas { // ignore: unnecessary_null_comparison assert(path != null); // path is checked on the engine side assert(doAntiAlias != null); // ignore: unnecessary_null_comparison - _clipPath(path, doAntiAlias); - } - - void _clipPath(ui.Path path, bool doAntiAlias) { - _canvas.clipPath(path, doAntiAlias); + _canvas.clipPath(path as CkPath, doAntiAlias); } @override @@ -140,8 +149,8 @@ class CanvasKitCanvas implements ui.Canvas { @override void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) { - assert(_offsetIsValid(p1)); - assert(_offsetIsValid(p2)); + assert(offsetIsValid(p1)); + assert(offsetIsValid(p2)); assert(paint != null); // ignore: unnecessary_null_comparison _drawLine(p1, p2, paint); } @@ -207,7 +216,7 @@ class CanvasKitCanvas implements ui.Canvas { @override void drawCircle(ui.Offset c, double radius, ui.Paint paint) { - assert(_offsetIsValid(c)); + assert(offsetIsValid(c)); assert(paint != null); // ignore: unnecessary_null_comparison _drawCircle(c, radius, paint); } @@ -241,13 +250,9 @@ class CanvasKitCanvas implements ui.Canvas { void drawImage(ui.Image image, ui.Offset p, ui.Paint paint) { // ignore: unnecessary_null_comparison assert(image != null); // image is checked on the engine side - assert(_offsetIsValid(p)); + assert(offsetIsValid(p)); assert(paint != null); // ignore: unnecessary_null_comparison - _drawImage(image, p, paint); - } - - void _drawImage(ui.Image image, ui.Offset p, ui.Paint paint) { - _canvas.drawImage(image, p, paint as CkPaint); + _canvas.drawImage(image as CkImage, p, paint as CkPaint); } @override @@ -257,12 +262,7 @@ class CanvasKitCanvas implements ui.Canvas { assert(rectIsValid(src)); assert(rectIsValid(dst)); assert(paint != null); // ignore: unnecessary_null_comparison - _drawImageRect(image, src, dst, paint); - } - - void _drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { - _canvas.drawImageRect(image, src, dst, paint as CkPaint); + _canvas.drawImageRect(image as CkImage, src, dst, paint as CkPaint); } @override @@ -273,12 +273,7 @@ class CanvasKitCanvas implements ui.Canvas { assert(rectIsValid(center)); assert(rectIsValid(dst)); assert(paint != null); // ignore: unnecessary_null_comparison - _drawImageNine(image, center, dst, paint); - } - - void _drawImageNine( - ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { - _canvas.drawImageNine(image, center, dst, paint as CkPaint); + _canvas.drawImageNine(image as CkImage, center, dst, paint as CkPaint); } @override @@ -291,7 +286,7 @@ class CanvasKitCanvas implements ui.Canvas { @override void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { assert(paragraph != null); // ignore: unnecessary_null_comparison - assert(_offsetIsValid(offset)); + assert(offsetIsValid(offset)); _drawParagraph(paragraph, offset); } @@ -337,12 +332,7 @@ class CanvasKitCanvas implements ui.Canvas { assert(vertices != null); // vertices is checked on the engine side assert(paint != null); // ignore: unnecessary_null_comparison assert(blendMode != null); // ignore: unnecessary_null_comparison - _drawVertices(vertices, blendMode, paint); - } - - void _drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { - _canvas.drawVertices(vertices, blendMode, paint as CkPaint); + _canvas.drawVertices(vertices as CkVertices, blendMode, paint as CkPaint); } @override @@ -391,11 +381,11 @@ class CanvasKitCanvas implements ui.Canvas { rectBuffer[index3] = rect.bottom; } - final List? colorBuffer = - (colors == null || colors.isEmpty) ? null : toSkFloatColorList(colors); + final Uint32List? colorBuffer = + (colors == null || colors.isEmpty) ? null : toFlatColors(colors); - _drawAtlas( - paint, atlas, rstTransformBuffer, rectBuffer, colorBuffer, blendMode ?? ui.BlendMode.src); + _drawAtlas(paint, atlas, rstTransformBuffer, rectBuffer, colorBuffer, + blendMode ?? ui.BlendMode.src); } @override @@ -424,9 +414,8 @@ class CanvasKitCanvas implements ui.Canvas { throw ArgumentError( 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".'); - final List? colorBuffer = colors == null ? null : encodeRawColorList(colors); - - _drawAtlas(paint, atlas, rstTransforms, rects, colorBuffer, blendMode ?? ui.BlendMode.src); + _drawAtlas(paint, atlas, rstTransforms, rects, + colors?.buffer.asUint32List(), blendMode ?? ui.BlendMode.src); } // TODO(hterkelsen): Pass a cull_rect once CanvasKit supports that. @@ -435,10 +424,17 @@ class CanvasKitCanvas implements ui.Canvas { ui.Image atlas, Float32List rstTransforms, Float32List rects, - List? colors, + Uint32List? colors, ui.BlendMode blendMode, ) { - _canvas.drawAtlasRaw(paint as CkPaint, atlas, rstTransforms, rects, colors, blendMode); + _canvas.drawAtlasRaw( + paint as CkPaint, + atlas as CkImage, + rstTransforms, + rects, + colors, + blendMode, + ); } @override @@ -453,6 +449,6 @@ class CanvasKitCanvas implements ui.Canvas { void _drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { - _canvas.drawShadow(path, color, elevation, transparentOccluder); + _canvas.drawShadow(path as CkPath, color, elevation, transparentOccluder); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart index 2c219b4b19957..e8579d01175f5 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart @@ -2,65 +2,174 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - -/// A [ui.ColorFilter] backed by Skia's [CkColorFilter]. -class CkColorFilter extends ManagedSkiaObject { - final EngineColorFilter _engineFilter; - - CkColorFilter.mode(EngineColorFilter filter) : _engineFilter = filter; - - CkColorFilter.matrix(EngineColorFilter filter) : _engineFilter = filter; - - CkColorFilter.linearToSrgbGamma(EngineColorFilter filter) - : _engineFilter = filter; - - CkColorFilter.srgbToLinearGamma(EngineColorFilter filter) - : _engineFilter = filter; - - SkColorFilter _createSkiaObjectFromFilter() { - SkColorFilter skColorFilter; - switch (_engineFilter._type) { - case EngineColorFilter._TypeMode: - skColorFilter = canvasKit.SkColorFilter.MakeBlend( - toSharedSkColor1(_engineFilter._color!), - toSkBlendMode(_engineFilter._blendMode!), - ); - break; - case EngineColorFilter._TypeMatrix: - final Float32List colorMatrix = Float32List(20); - final List matrix = _engineFilter._matrix!; - for (int i = 0; i < 20; i++) { - colorMatrix[i] = matrix[i]; - } - skColorFilter = canvasKit.SkColorFilter.MakeMatrix(colorMatrix); - break; - case EngineColorFilter._TypeLinearToSrgbGamma: - skColorFilter = canvasKit.SkColorFilter.MakeLinearToSRGBGamma(); - break; - case EngineColorFilter._TypeSrgbToLinearGamma: - skColorFilter = canvasKit.SkColorFilter.MakeSRGBToLinearGamma(); - break; - default: - throw StateError( - 'Unknown mode ${_engineFilter._type} for ColorFilter.'); +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../color_filter.dart'; +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'image_filter.dart'; +import 'skia_object_cache.dart'; + +/// A concrete [ManagedSkiaObject] subclass that owns a [SkColorFilter] and +/// manages its lifecycle. +/// +/// Seealso: +/// +/// * [CkPaint.colorFilter], which uses a [ManagedSkColorFilter] to manage +/// the lifecycle of its [SkColorFilter]. +class ManagedSkColorFilter extends ManagedSkiaObject { + ManagedSkColorFilter(CkColorFilter ckColorFilter) + : colorFilter = ckColorFilter; + + final CkColorFilter colorFilter; + + @override + SkColorFilter createDefault() => colorFilter._initRawColorFilter(); + + @override + SkColorFilter resurrect() => colorFilter._initRawColorFilter(); + + @override + void delete() { + rawSkiaObject?.delete(); + } + + @override + int get hashCode => colorFilter.hashCode; + + @override + bool operator ==(Object other) { + if (runtimeType != other.runtimeType) { + return false; } - return skColorFilter; + return other is ManagedSkColorFilter && + other.colorFilter == colorFilter; } @override - SkColorFilter createDefault() { - return _createSkiaObjectFromFilter(); + String toString() => colorFilter.toString(); +} + +/// A [ui.ColorFilter] backed by Skia's [SkColorFilter]. +/// +/// Additionally, this class provides the interface for converting itself to a +/// [ManagedSkiaObject] that manages a skia image filter. +abstract class CkColorFilter + implements + CkManagedSkImageFilterConvertible, + EngineColorFilter { + const CkColorFilter(); + + /// Called by [ManagedSkiaObject.createDefault] and + /// [ManagedSkiaObject.resurrect] to create a new [SKImageFilter], when this + /// filter is used as an [ImageFilter]. + SkImageFilter initRawImageFilter() => + canvasKit.ImageFilter.MakeColorFilter(_initRawColorFilter(), null); + + /// Called by [ManagedSkiaObject.createDefault] and + /// [ManagedSkiaObject.resurrect] to create a new [SKColorFilter], when this + /// filter is used as a [ColorFilter]. + SkColorFilter _initRawColorFilter(); + + @override + ManagedSkiaObject get imageFilter => + CkColorFilterImageFilter(colorFilter: this); +} + +class CkBlendModeColorFilter extends CkColorFilter { + const CkBlendModeColorFilter(this.color, this.blendMode); + + final ui.Color color; + final ui.BlendMode blendMode; + + @override + SkColorFilter _initRawColorFilter() { + return canvasKit.ColorFilter.MakeBlend( + toSharedSkColor1(color), + toSkBlendMode(blendMode), + ); } @override - SkColorFilter resurrect() { - return _createSkiaObjectFromFilter(); + int get hashCode => ui.hashValues(color, blendMode); + + @override + bool operator ==(Object other) { + if (runtimeType != other.runtimeType) { + return false; + } + return other is CkBlendModeColorFilter && + other.color == color && + other.blendMode == blendMode; } @override - void delete() { - rawSkiaObject?.delete(); + String toString() => 'ColorFilter.mode($color, $blendMode)'; +} + +class CkMatrixColorFilter extends CkColorFilter { + const CkMatrixColorFilter(this.matrix); + + final List matrix; + + @override + SkColorFilter _initRawColorFilter() { + assert(this.matrix.length == 20, 'Color Matrix must have 20 entries.'); + final List matrix = this.matrix; + if (matrix is Float32List) { + return canvasKit.ColorFilter.MakeMatrix(matrix); + } + final Float32List float32Matrix = Float32List(20); + for (int i = 0; i < 20; i++) { + float32Matrix[i] = matrix[i]; + } + return canvasKit.ColorFilter.MakeMatrix(float32Matrix); + } + + @override + int get hashCode => ui.hashList(matrix); + + @override + bool operator ==(Object other) { + return runtimeType == other.runtimeType && + other is CkMatrixColorFilter && + listEquals(matrix, other.matrix); } + + @override + String toString() => 'ColorFilter.matrix($matrix)'; +} + +class CkLinearToSrgbGammaColorFilter extends CkColorFilter { + const CkLinearToSrgbGammaColorFilter(); + @override + SkColorFilter _initRawColorFilter() => + canvasKit.ColorFilter.MakeLinearToSRGBGamma(); + + @override + bool operator ==(Object other) => runtimeType == other.runtimeType; + + @override + int get hashCode => runtimeType.hashCode; + + @override + String toString() => 'ColorFilter.linearToSrgbGamma()'; +} + +class CkSrgbToLinearGammaColorFilter extends CkColorFilter { + const CkSrgbToLinearGammaColorFilter(); + @override + SkColorFilter _initRawColorFilter() => + canvasKit.ColorFilter.MakeSRGBToLinearGamma(); + + @override + bool operator ==(Object other) => runtimeType == other.runtimeType; + + @override + int get hashCode => runtimeType.hashCode; + + @override + String toString() => 'ColorFilter.srgbToLinearGamma()'; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index 5f8c4a9b8e73c..702ffe01e25f5 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -2,11 +2,43 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show NullTreeSanitizer, platformViewManager; +import '../html/path_to_svg_clip.dart'; +import '../platform_views/slots.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import '../window.dart'; +import 'canvas.dart'; +import 'initialization.dart'; +import 'path.dart'; +import 'picture_recorder.dart'; +import 'surface.dart'; +import 'surface_factory.dart'; /// This composites HTML views into the [ui.Scene]. class HtmlViewEmbedder { + /// The [HtmlViewEmbedder] singleton. + static HtmlViewEmbedder instance = HtmlViewEmbedder._(); + + HtmlViewEmbedder._(); + + /// The maximum number of overlay surfaces that can be live at once. + static const int maximumOverlaySurfaces = int.fromEnvironment( + 'FLUTTER_WEB_MAXIMUM_OVERLAYS', + defaultValue: 8, + ); + + /// The picture recorder shared by all platform views which paint to the + /// backup surface. + CkPictureRecorder? _backupPictureRecorder; + + /// The set of platform views using the backup surface. + final Set _viewsUsingBackupSurface = {}; + /// A picture recorder associated with a view id. /// /// When we composite in the platform view, we need to create a new canvas @@ -24,124 +56,61 @@ class HtmlViewEmbedder { final Map _currentCompositionParams = {}; - /// The HTML element associated with the given view id. - final Map _views = {}; - - /// The root view in the stack of mutator elements for the view id. - final Map _rootViews = {}; + /// The clip chain for a view Id. + /// + /// This contains: + /// * The root view in the stack of mutator elements for the view id. + /// * The slot view in the stack (what shows the actual platform view contents). + /// * The number of clipping elements used last time the view was composited. + final Map _viewClipChains = {}; - /// The overlay for the view id. - final Map _overlays = {}; + /// Surfaces used to draw on top of platform views, keyed by platform view ID. + /// + /// These surfaces are cached in the [OverlayCache] and reused. + final Map _overlays = {}; /// The views that need to be recomposited into the scene on the next frame. final Set _viewsToRecomposite = {}; - /// The views that need to be disposed of on the next frame. - final Set _viewsToDispose = {}; - /// The list of view ids that should be composited, in order. List _compositionOrder = []; /// The most recent composition order. List _activeCompositionOrder = []; - /// The number of clipping elements used last time the view was composited. - Map _clipCount = {}; - /// The size of the frame, in physical pixels. ui.Size _frameSize = ui.window.physicalSize; - void set frameSize(ui.Size size) { - if (_frameSize == size) { - return; - } - _activeCompositionOrder.clear(); + set frameSize(ui.Size size) { _frameSize = size; } - void handlePlatformViewCall( - ByteData? data, - ui.PlatformMessageResponseCallback? callback, - ) { - const MethodCodec codec = StandardMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - - switch (decoded.method) { - case 'create': - _create(decoded, callback); - return; - case 'dispose': - _dispose(decoded, callback!); - return; - } - callback!(null); - } - - void _create( - MethodCall methodCall, ui.PlatformMessageResponseCallback? callback) { - final Map args = methodCall.arguments; - final int? viewId = args['id']; - final String? viewType = args['viewType']; - const MethodCodec codec = StandardMethodCodec(); - - if (_views[viewId] != null) { - callback!(codec.encodeErrorEnvelope( - code: 'recreating_view', - message: 'trying to create an already created view', - details: 'view id: $viewId', - )); - return; - } - - final ui.PlatformViewFactory? factory = - ui.platformViewRegistry.registeredFactories[viewType]; - if (factory == null) { - callback!(codec.encodeErrorEnvelope( - code: 'unregistered_view_type', - message: 'trying to create a view with an unregistered type', - details: 'unregistered view type: $viewType', - )); - return; - } - - // TODO(het): Support creation parameters. - html.Element embeddedView = factory(viewId!); - _views[viewId] = embeddedView; - - _rootViews[viewId] = embeddedView; - - callback!(codec.encodeSuccessEnvelope(null)); - } - - void _dispose( - MethodCall methodCall, ui.PlatformMessageResponseCallback callback) { - int? viewId = methodCall.arguments; - const MethodCodec codec = StandardMethodCodec(); - if (!_views.containsKey(viewId)) { - callback(codec.encodeErrorEnvelope( - code: 'unknown_view', - message: 'trying to dispose an unknown view', - details: 'view id: $viewId', - )); - } - _viewsToDispose.add(viewId); - callback(codec.encodeSuccessEnvelope(null)); - } - - List getCurrentCanvases() { - final List canvases = []; + List getCurrentCanvases() { + final Set canvases = {}; for (int i = 0; i < _compositionOrder.length; i++) { final int viewId = _compositionOrder[i]; - canvases.add(_pictureRecorders[viewId]!.recordingCanvas); + canvases.add(_pictureRecorders[viewId]!.recordingCanvas!); } - return canvases; + return canvases.toList(); } void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) { - final pictureRecorder = CkPictureRecorder(); - pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); - pictureRecorder.recordingCanvas!.clear(ui.Color(0x00000000)); - _pictureRecorders[viewId] = pictureRecorder; + _ensureOverlayInitialized(viewId); + if (_viewsUsingBackupSurface.contains(viewId)) { + if (_backupPictureRecorder == null) { + // Only initialize the picture recorder for the backup surface once. + final CkPictureRecorder pictureRecorder = CkPictureRecorder(); + pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); + pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000)); + _backupPictureRecorder = pictureRecorder; + } + _pictureRecorders[viewId] = _backupPictureRecorder!; + } else { + final CkPictureRecorder pictureRecorder = CkPictureRecorder(); + pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); + pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000)); + _pictureRecorders[viewId] = pictureRecorder; + } _compositionOrder.add(viewId); // Do nothing if the params didn't change. @@ -163,24 +132,39 @@ class HtmlViewEmbedder { } void _compositeWithParams(int viewId, EmbeddedViewParams params) { - final html.Element platformView = _views[viewId]!; - platformView.style.width = '${params.size.width}px'; - platformView.style.height = '${params.size.height}px'; - platformView.style.position = 'absolute'; + // If we haven't seen this viewId yet, cache it for clips/transforms. + final ViewClipChain clipChain = _viewClipChains.putIfAbsent(viewId, () { + return ViewClipChain(view: createPlatformViewSlot(viewId)); + }); + final html.Element slot = clipChain.slot; + + // See `apply()` in the PersistedPlatformView class for the HTML version + // of this code. + slot.style + ..width = '${params.size.width}px' + ..height = '${params.size.height}px' + ..position = 'absolute'; + + // Recompute the position in the DOM of the `slot` element... final int currentClippingCount = _countClips(params.mutators); - final int? previousClippingCount = _clipCount[viewId]; + final int previousClippingCount = clipChain.clipCount; if (currentClippingCount != previousClippingCount) { - _clipCount[viewId] = currentClippingCount; - html.Element oldPlatformViewRoot = _rootViews[viewId]!; - html.Element? newPlatformViewRoot = _reconstructClipViewsChain( + final html.Element oldPlatformViewRoot = clipChain.root; + final html.Element newPlatformViewRoot = _reconstructClipViewsChain( currentClippingCount, - platformView, + slot, oldPlatformViewRoot, ); - _rootViews[viewId] = newPlatformViewRoot; + // Store the updated root element, and clip count + clipChain.updateClipChain( + root: newPlatformViewRoot, + clipCount: currentClippingCount, + ); } - _applyMutators(params.mutators, platformView); + + // Apply mutators to the slot + _applyMutators(params.mutators, slot, viewId); } int _countClips(MutatorsStack mutators) { @@ -193,7 +177,7 @@ class HtmlViewEmbedder { return clipCount; } - html.Element? _reconstructClipViewsChain( + html.Element _reconstructClipViewsChain( int numClips, html.Element platformView, html.Element headClipView, @@ -203,21 +187,21 @@ class HtmlViewEmbedder { indexInFlutterView = skiaSceneHost!.children.indexOf(headClipView); headClipView.remove(); } - html.Element? head = platformView; + html.Element head = platformView; int clipIndex = 0; // Re-use as much existing clip views as needed. while (head != headClipView && clipIndex < numClips) { - head = head!.parent; + head = head.parent!; clipIndex++; } // If there weren't enough existing clip views, add more. while (clipIndex < numClips) { - html.Element clippingView = html.Element.tag('flt-clip'); - clippingView.append(head!); + final html.Element clippingView = html.Element.tag('flt-clip'); + clippingView.append(head); head = clippingView; clipIndex++; } - head!.remove(); + head.remove(); // If the chain was previously attached, attach it to the same position. if (indexInFlutterView > -1) { @@ -226,23 +210,45 @@ class HtmlViewEmbedder { return head; } - void _applyMutators(MutatorsStack mutators, html.Element embeddedView) { + /// Clean up the old SVG clip definitions, as this platform view is about to + /// be recomposited. + void _cleanUpClipDefs(int viewId) { + if (_svgClipDefs.containsKey(viewId)) { + final html.Element clipDefs = + _svgPathDefs!.querySelector('#sk_path_defs')!; + final List nodesToRemove = []; + final Set oldDefs = _svgClipDefs[viewId]!; + for (final html.Element child in clipDefs.children) { + if (oldDefs.contains(child.id)) { + nodesToRemove.add(child); + } + } + for (final html.Element node in nodesToRemove) { + node.remove(); + } + _svgClipDefs[viewId]!.clear(); + } + } + + void _applyMutators( + MutatorsStack mutators, html.Element embeddedView, int viewId) { html.Element head = embeddedView; Matrix4 headTransform = Matrix4.identity(); double embeddedOpacity = 1.0; _resetAnchor(head); + _cleanUpClipDefs(viewId); for (final Mutator mutator in mutators) { switch (mutator.type) { case MutatorType.transform: - headTransform.multiply(mutator.matrix!); + headTransform = mutator.matrix!.multiplied(headTransform); head.style.transform = float64ListToCssTransform(headTransform.storage); break; case MutatorType.clipRect: case MutatorType.clipRRect: case MutatorType.clipPath: - html.Element clipView = head.parent!; + final html.Element clipView = head.parent!; clipView.style.clip = ''; clipView.style.clipPath = ''; headTransform = Matrix4.identity(); @@ -255,27 +261,41 @@ class HtmlViewEmbedder { final CkPath path = CkPath(); path.addRRect(mutator.rrect!); _ensureSvgPathDefs(); - html.Element pathDefs = + final html.Element pathDefs = _svgPathDefs!.querySelector('#sk_path_defs')!; _clipPathCount += 1; - html.Element newClipPath = - html.Element.html('' - '' - ''); + final String clipId = 'svgClip$_clipPathCount'; + final html.Node newClipPath = html.DocumentFragment.svg( + '' + '' + '', + treeSanitizer: NullTreeSanitizer(), + ); pathDefs.append(newClipPath); - clipView.style.clipPath = 'url(#svgClip$_clipPathCount)'; + // Store the id of the node instead of [newClipPath] directly. For + // some reason, calling `newClipPath.remove()` doesn't remove it + // from the DOM. + _svgClipDefs.putIfAbsent(viewId, () => {}).add(clipId); + clipView.style.clipPath = 'url(#$clipId)'; } else if (mutator.path != null) { - final CkPath path = mutator.path as CkPath; + final CkPath path = mutator.path! as CkPath; _ensureSvgPathDefs(); - html.Element pathDefs = + final html.Element pathDefs = _svgPathDefs!.querySelector('#sk_path_defs')!; _clipPathCount += 1; - html.Element newClipPath = - html.Element.html('' - '' - ''); + final String clipId = 'svgClip$_clipPathCount'; + final html.Node newClipPath = html.DocumentFragment.svg( + '' + '' + '', + treeSanitizer: NullTreeSanitizer(), + ); pathDefs.append(newClipPath); - clipView.style.clipPath = 'url(#svgClip$_clipPathCount)'; + // Store the id of the node instead of [newClipPath] directly. For + // some reason, calling `newClipPath.remove()` doesn't remove it + // from the DOM. + _svgClipDefs.putIfAbsent(viewId, () => {}).add(clipId); + clipView.style.clipPath = 'url(#$clipId)'; } _resetAnchor(clipView); head = clipView; @@ -292,11 +312,11 @@ class HtmlViewEmbedder { // // HTML elements use logical (CSS) pixels, but we have been using physical // pixels, so scale down the head element to match the logical resolution. - final double scale = EngineWindow.browserDevicePixelRatio; + final double scale = window.devicePixelRatio; final double inverseScale = 1 / scale; final Matrix4 scaleMatrix = Matrix4.diagonal3Values(inverseScale, inverseScale, 1); - headTransform.multiply(scaleMatrix); + headTransform = scaleMatrix.multiplied(headTransform); head.style.transform = float64ListToCssTransform(headTransform.storage); } @@ -313,6 +333,9 @@ class HtmlViewEmbedder { html.Element? _svgPathDefs; + /// The nodes containing the SVG clip definitions needed to clip this view. + Map> _svgClipDefs = >{}; + /// Ensures we add a container of SVG path defs to the DOM so they can /// be referred to in clip-path: url(#blah). void _ensureSvgPathDefs() { @@ -320,78 +343,199 @@ class HtmlViewEmbedder { return; } _svgPathDefs = html.Element.html( - '', - treeSanitizer: _NullTreeSanitizer(), + '$kSvgResourceHeader', + treeSanitizer: NullTreeSanitizer(), ); skiaSceneHost!.append(_svgPathDefs!); } void submitFrame() { - disposeViews(); - + bool _didPaintBackupSurface = false; for (int i = 0; i < _compositionOrder.length; i++) { - int viewId = _compositionOrder[i]; - ensureOverlayInitialized(viewId); - final SurfaceFrame frame = - _overlays[viewId]!.surface.acquireFrame(_frameSize); - final CkCanvas canvas = frame.skiaCanvas; - canvas.drawPicture( - _pictureRecorders[viewId]!.endRecording() as CkPicture, - ); - frame.submit(); + final int viewId = _compositionOrder[i]; + if (_viewsUsingBackupSurface.contains(viewId)) { + // Only draw the picture to the backup surface once. + if (!_didPaintBackupSurface) { + final SurfaceFrame backupFrame = + SurfaceFactory.instance.backupSurface.acquireFrame(_frameSize); + backupFrame.skiaCanvas + .drawPicture(_backupPictureRecorder!.endRecording()); + _backupPictureRecorder = null; + backupFrame.submit(); + _didPaintBackupSurface = true; + } + } else { + final SurfaceFrame frame = _overlays[viewId]!.acquireFrame(_frameSize); + final CkCanvas canvas = frame.skiaCanvas; + canvas.drawPicture( + _pictureRecorders[viewId]!.endRecording(), + ); + frame.submit(); + } } - _pictureRecorders.clear(); - if (_listEquals(_compositionOrder, _activeCompositionOrder)) { + if (listEquals(_compositionOrder, _activeCompositionOrder)) { _compositionOrder.clear(); return; } + + SurfaceFactory.instance.removeSurfacesFromDom(); + final Set unusedViews = Set.from(_activeCompositionOrder); _activeCompositionOrder.clear(); + List? debugInvalidViewIds; for (int i = 0; i < _compositionOrder.length; i++) { - int viewId = _compositionOrder[i]; - html.Element platformViewRoot = _rootViews[viewId]!; - html.Element overlay = _overlays[viewId]!.surface.htmlElement!; + final int viewId = _compositionOrder[i]; + + if (assertionsEnabled) { + if (!platformViewManager.knowsViewId(viewId)) { + debugInvalidViewIds ??= []; + debugInvalidViewIds.add(viewId); + continue; + } + } + + unusedViews.remove(viewId); + final html.Element platformViewRoot = _viewClipChains[viewId]!.root; + final html.Element overlay = _overlays[viewId]!.htmlElement; platformViewRoot.remove(); skiaSceneHost!.append(platformViewRoot); overlay.remove(); skiaSceneHost!.append(overlay); _activeCompositionOrder.add(viewId); } + if (_didPaintBackupSurface) { + skiaSceneHost!.append(SurfaceFactory.instance.backupSurface.htmlElement); + } + _compositionOrder.clear(); - } - void disposeViews() { - if (_viewsToDispose.isEmpty) { - return; - } + disposeViews(unusedViews); + _releaseOverlays(); - for (int? viewId in _viewsToDispose) { - final html.Element rootView = _rootViews[viewId]!; - rootView.remove(); - _views.remove(viewId); - _rootViews.remove(viewId); - if (_overlays[viewId] != null) { - final Overlay overlay = _overlays[viewId]!; - overlay.surface.htmlElement?.remove(); - overlay.surface.htmlElement = null; - overlay.skSurface?.dispose(); + if (assertionsEnabled) { + if (debugInvalidViewIds != null && debugInvalidViewIds.isNotEmpty) { + throw AssertionError( + 'Cannot render platform views: ${debugInvalidViewIds.join(', ')}. ' + 'These views have not been created, or they have been deleted.', + ); } - _overlays.remove(viewId); + } + } + + void disposeViews(Set viewsToDispose) { + for (final int viewId in viewsToDispose) { + // Remove viewId from the _viewClipChains Map, and then from the DOM. + final ViewClipChain? clipChain = _viewClipChains.remove(viewId); + clipChain?.root.remove(); + // More cleanup _currentCompositionParams.remove(viewId); - _clipCount.remove(viewId); _viewsToRecomposite.remove(viewId); + _cleanUpClipDefs(viewId); + _svgClipDefs.remove(viewId); } - _viewsToDispose.clear(); } - void ensureOverlayInitialized(int viewId) { - Overlay? overlay = _overlays[viewId]; - if (overlay != null) { + void _releaseOverlays() { + _pictureRecorders.clear(); + _overlays.clear(); + _viewsUsingBackupSurface.clear(); + SurfaceFactory.instance.releaseSurfaces(); + } + + void _releaseOverlay(int viewId) { + if (_overlays[viewId] != null) { + final Surface overlay = _overlays[viewId]!; + if (overlay == SurfaceFactory.instance.backupSurface) { + assert(_viewsUsingBackupSurface.contains(viewId)); + _viewsUsingBackupSurface.remove(viewId); + _overlays.remove(viewId); + // If no views use the backup surface, then we can release it. This + // happens when the number of live platform views drops below the + // maximum overlay surfaces, so the backup surface is no longer needed. + if (_viewsUsingBackupSurface.isEmpty) { + SurfaceFactory.instance.releaseSurface(overlay); + } + } else { + SurfaceFactory.instance.releaseSurface(overlay); + _overlays.remove(viewId); + } + } + } + + void _ensureOverlayInitialized(int viewId) { + // If there's an active overlay for the view ID, continue using it. + Surface? overlay = _overlays[viewId]; + if (overlay != null && !_viewsUsingBackupSurface.contains(viewId)) { + overlay.createOrUpdateSurfaces(_frameSize); return; } - Surface surface = Surface(this); - CkSurface? skSurface = surface.acquireRenderSurface(_frameSize); - _overlays[viewId] = Overlay(surface, skSurface); + + // If this view was using the backup surface, try to release the backup + // surface and see if a non-backup surface became available. + if (_viewsUsingBackupSurface.contains(viewId)) { + _releaseOverlay(viewId); + } + + // Try reusing a cached overlay created for another platform view. + overlay = SurfaceFactory.instance.getSurface(); + if (overlay == SurfaceFactory.instance.backupSurface) { + _viewsUsingBackupSurface.add(viewId); + } + overlay.createOrUpdateSurfaces(_frameSize); + _overlays[viewId] = overlay; + } + + /// Deletes SVG clip paths, useful for tests. + void debugCleanupSvgClipPaths() { + _svgPathDefs?.children.single.children.forEach(removeElement); + _svgClipDefs.clear(); + } + + static void removeElement(html.Element element) { + element.remove(); + } + + /// Clears the state of this view embedder. Used in tests. + void debugClear() { + final Set allViews = platformViewManager.debugClear(); + disposeViews(allViews); + _backupPictureRecorder?.endRecording(); + _backupPictureRecorder = null; + _viewsUsingBackupSurface.clear(); + _pictureRecorders.clear(); + _currentCompositionParams.clear(); + debugCleanupSvgClipPaths(); + _currentCompositionParams.clear(); + _viewClipChains.clear(); + _overlays.clear(); + _viewsToRecomposite.clear(); + _activeCompositionOrder.clear(); + _compositionOrder.clear(); + } +} + +/// Represents a Clip Chain (for a view). +/// +/// Objects of this class contain: +/// * The root view in the stack of mutator elements for the view id. +/// * The slot view in the stack (the actual contents of the platform view). +/// * The number of clipping elements used last time the view was composited. +class ViewClipChain { + html.Element _root; + html.Element _slot; + int _clipCount = -1; + + ViewClipChain({required html.Element view}) + : _root = view, + _slot = view; + + html.Element get root => _root; + html.Element get slot => _slot; + int get clipCount => _clipCount; + + void updateClipChain({required html.Element root, required int clipCount}) { + _root = root; + _clipCount = clipCount; } } @@ -404,6 +548,7 @@ class EmbeddedViewParams { final ui.Size size; final MutatorsStack mutators; + @override bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -414,6 +559,7 @@ class EmbeddedViewParams { other.mutators == mutators; } + @override int get hashCode => ui.hashValues(offset, size, mutators); } @@ -461,6 +607,7 @@ class Mutator { double get alphaFloat => alpha! / 255.0; + @override bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -490,6 +637,7 @@ class Mutator { } } + @override int get hashCode => ui.hashValues(type, rect, rrect, path, matrix, alpha); } @@ -526,24 +674,18 @@ class MutatorsStack extends Iterable { _mutators.removeLast(); } + @override bool operator ==(Object other) { if (identical(other, this)) { return true; } return other is MutatorsStack && - _listEquals(other._mutators, _mutators); + listEquals(other._mutators, _mutators); } + @override int get hashCode => ui.hashList(_mutators); @override Iterator get iterator => _mutators.reversed.iterator; } - -/// Represents a surface overlaying a platform view. -class Overlay { - final Surface surface; - final CkSurface? skSurface; - - Overlay(this.surface, this.skSurface); -} diff --git a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart new file mode 100644 index 0000000000000..85dc7938e9df3 --- /dev/null +++ b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart @@ -0,0 +1,1003 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../font_change_util.dart'; +import '../platform_dispatcher.dart'; +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'fonts.dart'; +import 'initialization.dart'; +import 'interval_tree.dart'; + +/// Global static font fallback data. +class FontFallbackData { + static FontFallbackData get instance => _instance; + static FontFallbackData _instance = FontFallbackData(); + + /// Resets the fallback font data. + /// + /// After calling this method fallback fonts will be loaded from scratch. + /// + /// Used for tests. + static void debugReset() { + _instance = FontFallbackData(); + } + + /// Whether or not "Noto Sans Symbols" and "Noto Color Emoji" fonts have been + /// downloaded. We download these as fallbacks when no other font covers the + /// given code units. + bool registeredSymbolsAndEmoji = false; + + /// Code units that no known font has a glyph for. + final Set codeUnitsWithNoKnownFont = {}; + + /// Code units which are known to be covered by at least one fallback font. + final Set knownCoveredCodeUnits = {}; + + /// Index of all font families by code unit range. + final IntervalTree notoTree = createNotoFontTree(); + + static IntervalTree createNotoFontTree() { + final Map> ranges = + >{}; + + for (final NotoFont font in _notoFonts) { + // TODO(yjbanov): instead of mutating the font tree during reset, it's + // better to construct an immutable tree of resolved fonts + // pointing back to the original NotoFont objects. Then + // resetting the tree would be a matter of reconstructing + // the new resolved tree. + font.reset(); + // ignore: prefer_foreach + for (final CodeunitRange range in font.approximateUnicodeRanges) { + ranges.putIfAbsent(font, () => []).add(range); + } + } + + return IntervalTree.createFromRanges(ranges); + } + + /// Fallback fonts which have been registered and loaded. + final List registeredFallbackFonts = []; + + final List globalFontFallbacks = ['Roboto']; + + final Map fontFallbackCounts = {}; + + /// A list of code units to check against the global fallback fonts. + final Set _codeUnitsToCheckAgainstFallbackFonts = {}; + + /// This is [true] if we have scheduled a check for missing code units. + /// + /// We only do this once a frame, since checking if a font supports certain + /// code units is very expensive. + bool _scheduledCodeUnitCheck = false; + + /// Determines if the given [text] contains any code points which are not + /// supported by the current set of fonts. + void ensureFontsSupportText(String text, List fontFamilies) { + // TODO(hterkelsen): Make this faster for the common case where the text + // is supported by the given fonts. + + // If the text is ASCII, then skip this check. + bool isAscii = true; + for (int i = 0; i < text.length; i++) { + if (text.codeUnitAt(i) >= 160) { + isAscii = false; + break; + } + } + if (isAscii) { + return; + } + + // We have a cache of code units which are known to be covered by at least + // one of our fallback fonts, and a cache of code units which are known not + // to be covered by any fallback font. From the given text, construct a set + // of code units which need to be checked. + final Set runesToCheck = {}; + for (final int rune in text.runes) { + // Filter out code units which ASCII, known to be covered, or known not + // to be covered. + if (!(rune < 160 || + knownCoveredCodeUnits.contains(rune) || + codeUnitsWithNoKnownFont.contains(rune))) { + runesToCheck.add(rune); + } + } + if (runesToCheck.isEmpty) { + return; + } + + final List codeUnits = runesToCheck.toList(); + + final List fonts = []; + for (final String font in fontFamilies) { + final List? typefacesForFamily = + skiaFontCollection.familyToFontMap[font]; + if (typefacesForFamily != null) { + fonts.addAll(typefacesForFamily); + } + } + final List codeUnitsSupported = + List.filled(codeUnits.length, false); + final String testString = String.fromCharCodes(codeUnits); + for (final SkFont font in fonts) { + final Uint8List glyphs = font.getGlyphIDs(testString); + assert(glyphs.length == codeUnitsSupported.length); + for (int i = 0; i < glyphs.length; i++) { + codeUnitsSupported[i] |= glyphs[i] != 0 || _isControlCode(codeUnits[i]); + } + } + + if (codeUnitsSupported.any((bool x) => !x)) { + final List missingCodeUnits = []; + for (int i = 0; i < codeUnitsSupported.length; i++) { + if (!codeUnitsSupported[i]) { + missingCodeUnits.add(codeUnits[i]); + } + } + _codeUnitsToCheckAgainstFallbackFonts.addAll(missingCodeUnits); + if (!_scheduledCodeUnitCheck) { + _scheduledCodeUnitCheck = true; + // ignore: invalid_use_of_visible_for_testing_member + EnginePlatformDispatcher.instance.rasterizer! + .addPostFrameCallback(_ensureFallbackFonts); + } + } + } + + /// Returns [true] if [codepoint] is a Unicode control code. + bool _isControlCode(int codepoint) { + return codepoint < 32 || (codepoint > 127 && codepoint < 160); + } + + /// Checks the missing code units against the current set of fallback fonts + /// and starts downloading new fallback fonts if the current set can't cover + /// the code units. + void _ensureFallbackFonts() { + _scheduledCodeUnitCheck = false; + // We don't know if the remaining code units are covered by our fallback + // fonts. Check them and update the cache. + final List codeUnits = _codeUnitsToCheckAgainstFallbackFonts.toList(); + _codeUnitsToCheckAgainstFallbackFonts.clear(); + final List codeUnitsSupported = + List.filled(codeUnits.length, false); + final String testString = String.fromCharCodes(codeUnits); + + for (final String font in globalFontFallbacks) { + final List? fontsForFamily = + skiaFontCollection.familyToFontMap[font]; + if (fontsForFamily == null) { + printWarning('A fallback font was registered but we ' + 'cannot retrieve the typeface for it.'); + continue; + } + for (final SkFont font in fontsForFamily) { + final Uint8List glyphs = font.getGlyphIDs(testString); + assert(glyphs.length == codeUnitsSupported.length); + for (int i = 0; i < glyphs.length; i++) { + final bool codeUnitSupported = glyphs[i] != 0; + if (codeUnitSupported) { + knownCoveredCodeUnits.add(codeUnits[i]); + } + codeUnitsSupported[i] |= + codeUnitSupported || _isControlCode(codeUnits[i]); + } + } + + // Once we've checked every typeface for this family, check to see if + // every code unit has been covered in order to avoid unnecessary checks. + bool keepGoing = false; + for (final bool supported in codeUnitsSupported) { + if (!supported) { + keepGoing = true; + break; + } + } + + if (!keepGoing) { + return; + } + } + + // If we reached here, then there are some code units which aren't covered + // by the global fallback fonts. Remove the ones which were covered and + // try to find fallback fonts which cover them. + for (int i = codeUnits.length - 1; i >= 0; i--) { + if (codeUnitsSupported[i]) { + codeUnits.removeAt(i); + } + } + findFontsForMissingCodeunits(codeUnits); + } + + void registerFallbackFont(String family, Uint8List bytes) { + final SkTypeface? typeface = + canvasKit.FontMgr.RefDefault().MakeTypefaceFromData(bytes); + if (typeface == null) { + printWarning('Failed to parse fallback font $family as a font.'); + return; + } + fontFallbackCounts.putIfAbsent(family, () => 0); + final int fontFallbackTag = fontFallbackCounts[family]!; + fontFallbackCounts[family] = fontFallbackCounts[family]! + 1; + final String countedFamily = '$family $fontFallbackTag'; + // Insert emoji font before all other fallback fonts so we use the emoji + // whenever it's available. + registeredFallbackFonts.add(RegisteredFont(bytes, countedFamily, typeface)); + // Insert emoji font before all other fallback fonts so we use the emoji + // whenever it's available. + if (family == 'Noto Color Emoji Compat') { + if (globalFontFallbacks.first == 'Roboto') { + globalFontFallbacks.insert(1, countedFamily); + } else { + globalFontFallbacks.insert(0, countedFamily); + } + } else { + globalFontFallbacks.add(countedFamily); + } + } +} + +Future findFontsForMissingCodeunits(List codeUnits) async { + final FontFallbackData data = FontFallbackData.instance; + + Set fonts = {}; + final Set coveredCodeUnits = {}; + final Set missingCodeUnits = {}; + for (final int codeUnit in codeUnits) { + final List fontsForUnit = data.notoTree.intersections(codeUnit); + fonts.addAll(fontsForUnit); + if (fontsForUnit.isNotEmpty) { + coveredCodeUnits.add(codeUnit); + } else { + missingCodeUnits.add(codeUnit); + } + } + + for (final NotoFont font in fonts) { + await font.ensureResolved(); + } + + // The call to `findMinimumFontsForCodeUnits` will remove all code units that + // were matched by `fonts` from `unmatchedCodeUnits`. + final Set unmatchedCodeUnits = Set.from(coveredCodeUnits); + fonts = findMinimumFontsForCodeUnits(unmatchedCodeUnits, fonts); + + final Set<_ResolvedNotoSubset> resolvedFonts = <_ResolvedNotoSubset>{}; + for (final int codeUnit in coveredCodeUnits) { + for (final NotoFont font in fonts) { + if (font.resolvedFont == null) { + // We failed to resolve the font earlier. + continue; + } + resolvedFonts.addAll(font.resolvedFont!.tree.intersections(codeUnit)); + } + } + resolvedFonts.forEach(notoDownloadQueue.add); + + // We looked through the Noto font tree and didn't find any font families + // covering some code units, or we did find a font family, but when we + // downloaded the fonts we found that they actually didn't cover them. So + // we try looking them up in the symbols and emojis fonts. + if (missingCodeUnits.isNotEmpty || unmatchedCodeUnits.isNotEmpty) { + if (!data.registeredSymbolsAndEmoji) { + _registerSymbolsAndEmoji(); + } else { + if (!notoDownloadQueue.isPending) { + printWarning( + 'Could not find a set of Noto fonts to display all missing ' + 'characters. Please add a font asset for the missing characters.' + ' See: https://flutter.dev/docs/cookbook/design/fonts'); + data.codeUnitsWithNoKnownFont.addAll(missingCodeUnits); + } + } + } +} + +/// Parse the CSS file for a font and make a list of resolved subsets. +/// +/// A CSS file from Google Fonts looks like this: +/// +/// /* [0] */ +/// @font-face { +/// font-family: 'Noto Sans KR'; +/// font-style: normal; +/// font-weight: 400; +/// src: url(https://fonts.gstatic.com/s/notosanskr/v13/PbykFmXiEBPT4ITbgNA5Cgm20xz64px_1hVWr0wuPNGmlQNMEfD4.0.woff2) format('woff2'); +/// unicode-range: U+f9ca-fa0b, U+ff03-ff05, U+ff07, U+ff0a-ff0b, U+ff0d-ff19, U+ff1b, U+ff1d, U+ff20-ff5b, U+ff5d, U+ffe0-ffe3, U+ffe5-ffe6; +/// } +/// /* [1] */ +/// @font-face { +/// font-family: 'Noto Sans KR'; +/// font-style: normal; +/// font-weight: 400; +/// src: url(https://fonts.gstatic.com/s/notosanskr/v13/PbykFmXiEBPT4ITbgNA5Cgm20xz64px_1hVWr0wuPNGmlQNMEfD4.1.woff2) format('woff2'); +/// unicode-range: U+f92f-f980, U+f982-f9c9; +/// } +/// /* [2] */ +/// @font-face { +/// font-family: 'Noto Sans KR'; +/// font-style: normal; +/// font-weight: 400; +/// src: url(https://fonts.gstatic.com/s/notosanskr/v13/PbykFmXiEBPT4ITbgNA5Cgm20xz64px_1hVWr0wuPNGmlQNMEfD4.2.woff2) format('woff2'); +/// unicode-range: U+d723-d728, U+d72a-d733, U+d735-d748, U+d74a-d74f, U+d752-d753, U+d755-d757, U+d75a-d75f, U+d762-d764, U+d766-d768, U+d76a-d76b, U+d76d-d76f, U+d771-d787, U+d789-d78b, U+d78d-d78f, U+d791-d797, U+d79a, U+d79c, U+d79e-d7a3, U+f900-f909, U+f90b-f92e; +/// } +_ResolvedNotoFont? _makeResolvedNotoFontFromCss(String css, String name) { + final List<_ResolvedNotoSubset> subsets = <_ResolvedNotoSubset>[]; + bool resolvingFontFace = false; + String? fontFaceUrl; + List? fontFaceUnicodeRanges; + for (final String line in LineSplitter.split(css)) { + // Search for the beginning of a @font-face. + if (!resolvingFontFace) { + if (line == '@font-face {') { + resolvingFontFace = true; + } else { + continue; + } + } else { + // We are resolving a @font-face, read out the url and ranges. + if (line.startsWith(' src:')) { + final int urlStart = line.indexOf('url('); + if (urlStart == -1) { + printWarning('Unable to resolve Noto font URL: $line'); + return null; + } + final int urlEnd = line.indexOf(')'); + fontFaceUrl = line.substring(urlStart + 4, urlEnd); + } else if (line.startsWith(' unicode-range:')) { + fontFaceUnicodeRanges = []; + final String rangeString = line.substring(17, line.length - 1); + final List rawRanges = rangeString.split(', '); + for (final String rawRange in rawRanges) { + final List startEnd = rawRange.split('-'); + if (startEnd.length == 1) { + final String singleRange = startEnd.single; + assert(singleRange.startsWith('U+')); + final int rangeValue = + int.parse(singleRange.substring(2), radix: 16); + fontFaceUnicodeRanges.add(CodeunitRange(rangeValue, rangeValue)); + } else { + assert(startEnd.length == 2); + final String startRange = startEnd[0]; + final String endRange = startEnd[1]; + assert(startRange.startsWith('U+')); + final int startValue = + int.parse(startRange.substring(2), radix: 16); + final int endValue = int.parse(endRange, radix: 16); + fontFaceUnicodeRanges.add(CodeunitRange(startValue, endValue)); + } + } + } else if (line == '}') { + if (fontFaceUrl == null || fontFaceUnicodeRanges == null) { + printWarning('Unable to parse Google Fonts CSS: $css'); + return null; + } + subsets + .add(_ResolvedNotoSubset(fontFaceUrl, name, fontFaceUnicodeRanges)); + resolvingFontFace = false; + } else { + continue; + } + } + } + + if (resolvingFontFace) { + printWarning('Unable to parse Google Fonts CSS: $css'); + return null; + } + + final Map<_ResolvedNotoSubset, List> rangesMap = + <_ResolvedNotoSubset, List>{}; + for (final _ResolvedNotoSubset subset in subsets) { + // ignore: prefer_foreach + for (final CodeunitRange range in subset.ranges) { + rangesMap.putIfAbsent(subset, () => []).add(range); + } + } + + if (rangesMap.isEmpty) { + printWarning('Parsed Google Fonts CSS was empty: $css'); + return null; + } + + final IntervalTree<_ResolvedNotoSubset> tree = + IntervalTree<_ResolvedNotoSubset>.createFromRanges(rangesMap); + + return _ResolvedNotoFont(name, subsets, tree); +} + +/// In the case where none of the known Noto Fonts cover a set of code units, +/// try the Symbols and Emoji fonts. We don't know the exact range of code units +/// that are covered by these fonts, so we download them and hope for the best. +Future _registerSymbolsAndEmoji() async { + final FontFallbackData data = FontFallbackData.instance; + if (data.registeredSymbolsAndEmoji) { + return; + } + data.registeredSymbolsAndEmoji = true; + const String emojiUrl = + 'https://fonts.googleapis.com/css2?family=Noto+Color+Emoji+Compat'; + const String symbolsUrl = + 'https://fonts.googleapis.com/css2?family=Noto+Sans+Symbols'; + + final String emojiCss = + await notoDownloadQueue.downloader.downloadAsString(emojiUrl); + final String symbolsCss = + await notoDownloadQueue.downloader.downloadAsString(symbolsUrl); + + String? extractUrlFromCss(String css) { + for (final String line in LineSplitter.split(css)) { + if (line.startsWith(' src:')) { + final int urlStart = line.indexOf('url('); + if (urlStart == -1) { + printWarning('Unable to resolve Noto font URL: $line'); + return null; + } + final int urlEnd = line.indexOf(')'); + return line.substring(urlStart + 4, urlEnd); + } + } + printWarning('Unable to determine URL for Noto font'); + return null; + } + + final String? emojiFontUrl = extractUrlFromCss(emojiCss); + final String? symbolsFontUrl = extractUrlFromCss(symbolsCss); + + if (emojiFontUrl != null) { + notoDownloadQueue.add(_ResolvedNotoSubset( + emojiFontUrl, 'Noto Color Emoji Compat', const [])); + } else { + printWarning('Error parsing CSS for Noto Emoji font.'); + } + + if (symbolsFontUrl != null) { + notoDownloadQueue.add(_ResolvedNotoSubset( + symbolsFontUrl, 'Noto Sans Symbols', const [])); + } else { + printWarning('Error parsing CSS for Noto Symbols font.'); + } +} + +/// Finds the minimum set of fonts which covers all of the [codeUnits]. +/// +/// Removes all code units covered by [fonts] from [codeUnits]. The code +/// units remaining in the [codeUnits] set after calling this function do not +/// have a font that covers them and can be omitted next time to avoid +/// searching for fonts unnecessarily. +/// +/// Since set cover is NP-complete, we approximate using a greedy algorithm +/// which finds the font which covers the most code units. If multiple CJK +/// fonts match the same number of code units, we choose one based on the user's +/// locale. +Set findMinimumFontsForCodeUnits( + Set codeUnits, Set fonts) { + assert(fonts.isNotEmpty || codeUnits.isEmpty); + final Set minimumFonts = {}; + final List bestFonts = []; + + final String language = html.window.navigator.language; + + while (codeUnits.isNotEmpty) { + int maxCodeUnitsCovered = 0; + bestFonts.clear(); + for (final NotoFont font in fonts) { + int codeUnitsCovered = 0; + for (final int codeUnit in codeUnits) { + if (font.resolvedFont?.tree.containsDeep(codeUnit) == true) { + codeUnitsCovered++; + } + } + if (codeUnitsCovered > maxCodeUnitsCovered) { + bestFonts.clear(); + bestFonts.add(font); + maxCodeUnitsCovered = codeUnitsCovered; + } else if (codeUnitsCovered == maxCodeUnitsCovered) { + bestFonts.add(font); + } + } + if (maxCodeUnitsCovered == 0) { + // Fonts cannot cover remaining unmatched characters. + break; + } + // If the list of best fonts are all CJK fonts, choose the best one based + // on locale. Otherwise just choose the first font. + NotoFont bestFont = bestFonts.first; + if (bestFonts.length > 1) { + if (bestFonts.every((NotoFont font) => _cjkFonts.contains(font))) { + if (language == 'zh-Hans' || + language == 'zh-CN' || + language == 'zh-SG' || + language == 'zh-MY') { + if (bestFonts.contains(_notoSansSC)) { + bestFont = _notoSansSC; + } + } else if (language == 'zh-Hant' || + language == 'zh-TW' || + language == 'zh-MO') { + if (bestFonts.contains(_notoSansTC)) { + bestFont = _notoSansTC; + } + } else if (language == 'zh-HK') { + if (bestFonts.contains(_notoSansHK)) { + bestFont = _notoSansHK; + } + } else if (language == 'ja') { + if (bestFonts.contains(_notoSansJP)) { + bestFont = _notoSansJP; + } + } + } + } + codeUnits.removeWhere((int codeUnit) { + return bestFont.resolvedFont!.tree.containsDeep(codeUnit); + }); + minimumFonts.addAll(bestFonts); + } + return minimumFonts; +} + +class NotoFont { + final String name; + final List approximateUnicodeRanges; + + Completer? _decodingCompleter; + _ResolvedNotoFont? resolvedFont; + + NotoFont(this.name, this.approximateUnicodeRanges); + + String get googleFontsCssUrl => + 'https://fonts.googleapis.com/css2?family=${name.replaceAll(' ', '+')}'; + + Future ensureResolved() async { + if (resolvedFont == null) { + if (_decodingCompleter == null) { + _decodingCompleter = Completer(); + final String googleFontCss = await notoDownloadQueue.downloader + .downloadAsString(googleFontsCssUrl); + final _ResolvedNotoFont? googleFont = + _makeResolvedNotoFontFromCss(googleFontCss, name); + resolvedFont = googleFont; + _decodingCompleter!.complete(); + } else { + await _decodingCompleter!.future; + } + } + } + + void reset() { + resolvedFont = null; + _decodingCompleter = null; + } +} + +class CodeunitRange { + final int start; + final int end; + + const CodeunitRange(this.start, this.end); + + bool contains(int codeUnit) { + return start <= codeUnit && codeUnit <= end; + } + + @override + bool operator ==(dynamic other) { + if (other is! CodeunitRange) { + return false; + } + final CodeunitRange range = other; + return range.start == start && range.end == end; + } + + @override + int get hashCode => ui.hashValues(start, end); + + @override + String toString() => '[$start, $end]'; +} + +class _ResolvedNotoFont { + final String name; + final List<_ResolvedNotoSubset> subsets; + final IntervalTree<_ResolvedNotoSubset> tree; + + const _ResolvedNotoFont(this.name, this.subsets, this.tree); +} + +class _ResolvedNotoSubset { + final String url; + final String family; + final List ranges; + + _ResolvedNotoSubset(this.url, this.family, this.ranges); + + @override + String toString() => '_ResolvedNotoSubset($family, $url)'; +} + +NotoFont _notoSansSC = NotoFont('Noto Sans SC', [ + const CodeunitRange(12288, 12591), + const CodeunitRange(12800, 13311), + const CodeunitRange(19968, 40959), + const CodeunitRange(65072, 65135), + const CodeunitRange(65280, 65519), +]); + +NotoFont _notoSansTC = NotoFont('Noto Sans TC', [ + const CodeunitRange(12288, 12351), + const CodeunitRange(12549, 12585), + const CodeunitRange(19968, 40959), +]); + +NotoFont _notoSansHK = NotoFont('Noto Sans HK', [ + const CodeunitRange(12288, 12351), + const CodeunitRange(12549, 12585), + const CodeunitRange(19968, 40959), +]); + +NotoFont _notoSansJP = NotoFont('Noto Sans JP', [ + const CodeunitRange(12288, 12543), + const CodeunitRange(19968, 40959), + const CodeunitRange(65280, 65519), +]); + +List _cjkFonts = [ + _notoSansSC, + _notoSansTC, + _notoSansHK, + _notoSansJP, +]; + +List _notoFonts = [ + _notoSansSC, + _notoSansTC, + _notoSansHK, + _notoSansJP, + NotoFont('Noto Naskh Arabic UI', [ + const CodeunitRange(1536, 1791), + const CodeunitRange(8204, 8206), + const CodeunitRange(8208, 8209), + const CodeunitRange(8271, 8271), + const CodeunitRange(11841, 11841), + const CodeunitRange(64336, 65023), + const CodeunitRange(65132, 65276), + ]), + NotoFont('Noto Sans Armenian', [ + const CodeunitRange(1328, 1424), + const CodeunitRange(64275, 64279), + ]), + NotoFont('Noto Sans Bengali UI', [ + const CodeunitRange(2404, 2405), + const CodeunitRange(2433, 2555), + const CodeunitRange(8204, 8205), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Myanmar UI', [ + const CodeunitRange(4096, 4255), + const CodeunitRange(8204, 8205), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Egyptian Hieroglyphs', [ + const CodeunitRange(77824, 78894), + ]), + NotoFont('Noto Sans Ethiopic', [ + const CodeunitRange(4608, 5017), + const CodeunitRange(11648, 11742), + const CodeunitRange(43777, 43822), + ]), + NotoFont('Noto Sans Georgian', [ + const CodeunitRange(1417, 1417), + const CodeunitRange(4256, 4351), + const CodeunitRange(11520, 11567), + ]), + NotoFont('Noto Sans Gujarati UI', [ + const CodeunitRange(2404, 2405), + const CodeunitRange(2688, 2815), + const CodeunitRange(8204, 8205), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + const CodeunitRange(43056, 43065), + ]), + NotoFont('Noto Sans Gurmukhi UI', [ + const CodeunitRange(2404, 2405), + const CodeunitRange(2561, 2677), + const CodeunitRange(8204, 8205), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + const CodeunitRange(9772, 9772), + const CodeunitRange(43056, 43065), + ]), + NotoFont('Noto Sans Hebrew', [ + const CodeunitRange(1424, 1535), + const CodeunitRange(8362, 8362), + const CodeunitRange(9676, 9676), + const CodeunitRange(64285, 64335), + ]), + NotoFont('Noto Sans Devanagari UI', [ + const CodeunitRange(2304, 2431), + const CodeunitRange(7376, 7414), + const CodeunitRange(7416, 7417), + const CodeunitRange(8204, 8205), + const CodeunitRange(8360, 8360), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + const CodeunitRange(43056, 43065), + const CodeunitRange(43232, 43259), + ]), + NotoFont('Noto Sans Kannada UI', [ + const CodeunitRange(2404, 2405), + const CodeunitRange(3202, 3314), + const CodeunitRange(8204, 8205), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Khmer UI', [ + const CodeunitRange(6016, 6143), + const CodeunitRange(8204, 8204), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans KR', [ + const CodeunitRange(12593, 12686), + const CodeunitRange(12800, 12828), + const CodeunitRange(12896, 12923), + const CodeunitRange(44032, 55215), + ]), + NotoFont('Noto Sans Lao UI', [ + const CodeunitRange(3713, 3807), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Malayalam UI', [ + const CodeunitRange(775, 775), + const CodeunitRange(803, 803), + const CodeunitRange(2404, 2405), + const CodeunitRange(3330, 3455), + const CodeunitRange(8204, 8205), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Sinhala', [ + const CodeunitRange(2404, 2405), + const CodeunitRange(3458, 3572), + const CodeunitRange(8204, 8205), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Tamil UI', [ + const CodeunitRange(2404, 2405), + const CodeunitRange(2946, 3066), + const CodeunitRange(8204, 8205), + const CodeunitRange(8377, 8377), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Telugu UI', [ + const CodeunitRange(2385, 2386), + const CodeunitRange(2404, 2405), + const CodeunitRange(3072, 3199), + const CodeunitRange(7386, 7386), + const CodeunitRange(8204, 8205), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans Thai UI', [ + const CodeunitRange(3585, 3675), + const CodeunitRange(8204, 8205), + const CodeunitRange(9676, 9676), + ]), + NotoFont('Noto Sans', [ + const CodeunitRange(0, 255), + const CodeunitRange(305, 305), + const CodeunitRange(338, 339), + const CodeunitRange(699, 700), + const CodeunitRange(710, 710), + const CodeunitRange(730, 730), + const CodeunitRange(732, 732), + const CodeunitRange(8192, 8303), + const CodeunitRange(8308, 8308), + const CodeunitRange(8364, 8364), + const CodeunitRange(8482, 8482), + const CodeunitRange(8593, 8593), + const CodeunitRange(8595, 8595), + const CodeunitRange(8722, 8722), + const CodeunitRange(8725, 8725), + const CodeunitRange(65279, 65279), + const CodeunitRange(65533, 65533), + const CodeunitRange(1024, 1119), + const CodeunitRange(1168, 1169), + const CodeunitRange(1200, 1201), + const CodeunitRange(8470, 8470), + const CodeunitRange(1120, 1327), + const CodeunitRange(7296, 7304), + const CodeunitRange(8372, 8372), + const CodeunitRange(11744, 11775), + const CodeunitRange(42560, 42655), + const CodeunitRange(65070, 65071), + const CodeunitRange(880, 1023), + const CodeunitRange(7936, 8191), + const CodeunitRange(256, 591), + const CodeunitRange(601, 601), + const CodeunitRange(7680, 7935), + const CodeunitRange(8224, 8224), + const CodeunitRange(8352, 8363), + const CodeunitRange(8365, 8399), + const CodeunitRange(8467, 8467), + const CodeunitRange(11360, 11391), + const CodeunitRange(42784, 43007), + const CodeunitRange(258, 259), + const CodeunitRange(272, 273), + const CodeunitRange(296, 297), + const CodeunitRange(360, 361), + const CodeunitRange(416, 417), + const CodeunitRange(431, 432), + const CodeunitRange(7840, 7929), + const CodeunitRange(8363, 8363), + ]), +]; + +class FallbackFontDownloadQueue { + NotoDownloader downloader = NotoDownloader(); + + final Set<_ResolvedNotoSubset> downloadedSubsets = <_ResolvedNotoSubset>{}; + final Map pendingSubsets = + {}; + + bool get isPending => pendingSubsets.isNotEmpty || _fontsLoading != null; + + Future? _fontsLoading; + bool get debugIsLoadingFonts => _fontsLoading != null; + + Future debugWhenIdle() async { + if (assertionsEnabled) { + await Future.delayed(Duration.zero); + while (isPending) { + if (_fontsLoading != null) { + await _fontsLoading; + } + if (pendingSubsets.isNotEmpty) { + await Future.delayed(const Duration(milliseconds: 100)); + if (pendingSubsets.isEmpty) { + await Future.delayed(const Duration(milliseconds: 100)); + } + } + } + } else { + throw UnimplementedError(); + } + } + + void add(_ResolvedNotoSubset subset) { + if (downloadedSubsets.contains(subset) || + pendingSubsets.containsKey(subset.url)) { + return; + } + final bool firstInBatch = pendingSubsets.isEmpty; + pendingSubsets[subset.url] = subset; + if (firstInBatch) { + Timer.run(startDownloads); + } + } + + Future startDownloads() async { + final Map> downloads = >{}; + final Map downloadedData = {}; + for (final _ResolvedNotoSubset subset in pendingSubsets.values) { + downloads[subset.url] = Future(() async { + ByteBuffer buffer; + try { + buffer = await downloader.downloadAsBytes(subset.url, + debugDescription: subset.family); + } catch (e) { + pendingSubsets.remove(subset.url); + printWarning('Failed to load font ${subset.family} at ${subset.url}'); + printWarning(e.toString()); + return; + } + downloadedSubsets.add(subset); + downloadedData[subset.url] = buffer.asUint8List(); + }); + } + + await Future.wait(downloads.values); + + // Register fallback fonts in a predictable order. Otherwise, the fonts + // change their precedence depending on the download order causing + // visual differences between app reloads. + final List downloadOrder = + (downloadedData.keys.toList()..sort()).reversed.toList(); + for (final String url in downloadOrder) { + final _ResolvedNotoSubset subset = pendingSubsets.remove(url)!; + final Uint8List bytes = downloadedData[url]!; + FontFallbackData.instance.registerFallbackFont(subset.family, bytes); + if (pendingSubsets.isEmpty) { + _fontsLoading = skiaFontCollection.ensureFontsLoaded(); + try { + await _fontsLoading; + } finally { + _fontsLoading = null; + } + sendFontChangeMessage(); + } + } + + if (pendingSubsets.isNotEmpty) { + await startDownloads(); + } + } +} + +class NotoDownloader { + int get debugActiveDownloadCount => _debugActiveDownloadCount; + int _debugActiveDownloadCount = 0; + + /// Returns a future that resolves when there are no pending downloads. + /// + /// Useful in tests to make sure that fonts are loaded before working with + /// text. + Future debugWhenIdle() async { + if (assertionsEnabled) { + // Some downloads begin asynchronously in a microtask or in a Timer.run. + // Let those run before waiting for downloads to finish. + await Future.delayed(Duration.zero); + while (_debugActiveDownloadCount > 0) { + await Future.delayed(const Duration(milliseconds: 100)); + // If we started with a non-zero count and hit zero while waiting, wait a + // little more to make sure another download doesn't get chained after + // the last one (e.g. font file download after font CSS download). + if (_debugActiveDownloadCount == 0) { + await Future.delayed(const Duration(milliseconds: 100)); + } + } + } else { + throw UnimplementedError(); + } + } + + /// Downloads the [url] and returns it as a [ByteBuffer]. + /// + /// Override this for testing. + Future downloadAsBytes(String url, {String? debugDescription}) { + if (assertionsEnabled) { + _debugActiveDownloadCount += 1; + } + final Future result = httpFetch(url).then( + (html.Body fetchResult) => fetchResult + .arrayBuffer() + .then((dynamic x) => x as ByteBuffer)); + if (assertionsEnabled) { + result.whenComplete(() { + _debugActiveDownloadCount -= 1; + }); + } + return result; + } + + /// Downloads the [url] and returns is as a [String]. + /// + /// Override this for testing. + Future downloadAsString(String url, {String? debugDescription}) { + if (assertionsEnabled) { + _debugActiveDownloadCount += 1; + } + final Future result = httpFetch(url).then( + (html.Body response) => + response.text().then((dynamic x) => x as String)); + if (assertionsEnabled) { + result.whenComplete(() { + _debugActiveDownloadCount -= 1; + }); + } + return result; + } +} + +FallbackFontDownloadQueue notoDownloadQueue = FallbackFontDownloadQueue(); diff --git a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart index 53247214d1125..3bd5eae920365 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import '../assets.dart'; +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'font_fallbacks.dart'; // This URL was found by using the Google Fonts Developer API to find the URL // for Roboto. The API warns that this URL is not stable. In order to update @@ -13,24 +19,43 @@ part of engine; const String _robotoUrl = 'https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf'; +// URL for the Ahem font, only used in tests. +const String _ahemUrl = 'packages/ui/assets/ahem.ttf'; + /// Manages the fonts used in the Skia-based backend. class SkiaFontCollection { /// Fonts that have been registered but haven't been loaded yet. - final List> _unloadedFonts = - >[]; + final List> _unloadedFonts = + >[]; /// Fonts which have been registered and loaded. - final List<_RegisteredFont> _registeredFonts = <_RegisteredFont>[]; + final List _registeredFonts = []; - final Set registeredFamilies = {}; + final Map> familyToFontMap = >{}; Future ensureFontsLoaded() async { await _loadFonts(); + if (fontProvider != null) { + fontProvider!.delete(); + fontProvider = null; + } fontProvider = canvasKit.TypefaceFontProvider.Make(); + familyToFontMap.clear(); + + for (final RegisteredFont font in _registeredFonts) { + fontProvider!.registerFont(font.bytes, font.family); + familyToFontMap + .putIfAbsent(font.family, () => []) + .add(SkFont(font.typeface)); + } - for (var font in _registeredFonts) { - fontProvider.registerFont(font.bytes, font.flutterFamily); + for (final RegisteredFont font + in FontFallbackData.instance.registeredFallbackFonts) { + fontProvider!.registerFont(font.bytes, font.family); + familyToFontMap + .putIfAbsent(font.family, () => []) + .add(SkFont(font.typeface)); } } @@ -40,9 +65,8 @@ class SkiaFontCollection { if (_unloadedFonts.isEmpty) { return; } - final List<_RegisteredFont?> loadedFonts = - await Future.wait(_unloadedFonts); - for (_RegisteredFont? font in loadedFonts) { + final List loadedFonts = await Future.wait(_unloadedFonts); + for (final RegisteredFont? font in loadedFonts) { if (font != null) { _registeredFonts.add(font); } @@ -51,25 +75,23 @@ class SkiaFontCollection { } Future loadFontFromList(Uint8List list, {String? fontFamily}) async { - String? actualFamily = _readActualFamilyName(list); - - if (actualFamily == null) { + if (fontFamily == null) { + fontFamily = _readActualFamilyName(list); if (fontFamily == null) { - html.window.console - .warn('Failed to read font family name. Aborting font load.'); + printWarning('Failed to read font family name. Aborting font load.'); return; } - actualFamily = fontFamily; } - if (fontFamily == null) { - fontFamily = actualFamily; + final SkTypeface? typeface = + canvasKit.FontMgr.RefDefault().MakeTypefaceFromData(list); + if (typeface != null) { + _registeredFonts.add(RegisteredFont(list, fontFamily, typeface)); + await ensureFontsLoaded(); + } else { + printWarning('Failed to parse font family "$fontFamily"'); + return; } - - registeredFamilies.add(fontFamily); - - _registeredFonts.add(_RegisteredFont(list, fontFamily, actualFamily)); - await ensureFontsLoaded(); } Future registerFonts(AssetManager assetManager) async { @@ -79,8 +101,7 @@ class SkiaFontCollection { byteData = await assetManager.load('FontManifest.json'); } on AssetManagerException catch (e) { if (e.httpStatus == 404) { - html.window.console - .warn('Font manifest does not exist at `${e.url}` – ignoring.'); + printWarning('Font manifest does not exist at `${e.url}` – ignoring.'); return; } else { rethrow; @@ -88,22 +109,26 @@ class SkiaFontCollection { } final List? fontManifest = - json.decode(utf8.decode(byteData.buffer.asUint8List())); + json.decode(utf8.decode(byteData.buffer.asUint8List())) as List?; if (fontManifest == null) { throw AssertionError( 'There was a problem trying to load FontManifest.json'); } - for (Map fontFamily + bool registeredRoboto = false; + + for (final Map fontFamily in fontManifest.cast>()) { - final String family = fontFamily['family']!; - final List fontAssets = fontFamily['fonts']; + final String family = fontFamily.readString('family'); + final List fontAssets = fontFamily.readList('fonts'); - registeredFamilies.add(family); + if (family == 'Roboto') { + registeredRoboto = true; + } - for (dynamic fontAssetItem in fontAssets) { - final Map fontAsset = fontAssetItem; - final String asset = fontAsset['asset']; + for (final dynamic fontAssetItem in fontAssets) { + final Map fontAsset = fontAssetItem as Map; + final String asset = fontAsset.readString('asset'); _unloadedFonts .add(_registerFont(assetManager.getAssetUrl(asset), family)); } @@ -112,67 +137,73 @@ class SkiaFontCollection { /// We need a default fallback font for CanvasKit, in order to /// avoid crashing while laying out text with an unregistered font. We chose /// Roboto to match Android. - if (!registeredFamilies.contains('Roboto')) { + if (!registeredRoboto) { // Download Roboto and add it to the font buffers. _unloadedFonts.add(_registerFont(_robotoUrl, 'Roboto')); } } - Future<_RegisteredFont?> _registerFont(String url, String family) async { + Future debugRegisterTestFonts() async { + _unloadedFonts.add(_registerFont(_ahemUrl, 'Ahem')); + FontFallbackData.instance.globalFontFallbacks.add('Ahem'); + } + + Future _registerFont(String url, String family) async { ByteBuffer buffer; try { - buffer = await html.window - .fetch(url) - .then(_getArrayBuffer as FutureOr Function(dynamic)); + buffer = await httpFetch(url).then(_getArrayBuffer); } catch (e) { - html.window.console.warn('Failed to load font $family at $url'); - html.window.console.warn(e); + printWarning('Failed to load font $family at $url'); + printWarning(e.toString()); return null; } final Uint8List bytes = buffer.asUint8List(); - String? actualFamily = _readActualFamilyName(bytes); - - if (actualFamily == null) { - html.window.console.warn('Failed to determine the actual name of the ' - 'font $family at $url. Defaulting to $family.'); - actualFamily = family; + final SkTypeface? typeface = + canvasKit.FontMgr.RefDefault().MakeTypefaceFromData(bytes); + if (typeface != null) { + return RegisteredFont(bytes, family, typeface); + } else { + printWarning('Failed to load font $family at $url'); + printWarning('Verify that $url contains a valid font.'); + return null; } - - return _RegisteredFont(bytes, family, actualFamily); } String? _readActualFamilyName(Uint8List bytes) { - final SkFontMgr tmpFontMgr = canvasKit.SkFontMgr.FromData([bytes])!; - String? actualFamily = tmpFontMgr.getFamilyName(0); + final SkFontMgr tmpFontMgr = + canvasKit.FontMgr.FromData([bytes])!; + final String? actualFamily = tmpFontMgr.getFamilyName(0); tmpFontMgr.delete(); return actualFamily; } - Future? _getArrayBuffer(dynamic fetchResult) { - // TODO(yjbanov): fetchResult.arrayBuffer is a dynamic invocation. Clean it up. + Future _getArrayBuffer(html.Body fetchResult) { return fetchResult .arrayBuffer() .then((dynamic x) => x as ByteBuffer); } SkFontMgr? skFontMgr; - late TypefaceFontProvider fontProvider; + TypefaceFontProvider? fontProvider; } /// Represents a font that has been registered. -class _RegisteredFont { - /// The font family that the font was declared to have by Flutter. - final String flutterFamily; +class RegisteredFont { + /// The font family name for this font. + final String family; /// The byte data for this font. final Uint8List bytes; - /// The font family that was parsed from the font's bytes. - final String actualFamily; + /// The [SkTypeface] created from this font's [bytes]. + /// + /// This is used to determine which code points are supported by this font. + final SkTypeface typeface; - _RegisteredFont(this.bytes, this.flutterFamily, this.actualFamily) - : assert(bytes != null), // ignore: unnecessary_null_comparison - assert(flutterFamily != null), // ignore: unnecessary_null_comparison - assert(actualFamily != null); // ignore: unnecessary_null_comparison + RegisteredFont(this.bytes, this.family, this.typeface) { + // This is a hack which causes Skia to cache the decoded font. + final SkFont skFont = SkFont(typeface); + skFont.getGlyphBounds([0], null, null); + } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/image.dart b/lib/web_ui/lib/src/engine/canvaskit/image.dart index badaa8e6042e4..f4731330f4740 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -2,194 +2,355 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../html_image_codec.dart'; +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'skia_object_cache.dart'; /// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia. -void skiaInstantiateImageCodec(Uint8List list, Callback callback, +ui.Codec skiaInstantiateImageCodec(Uint8List list, [int? width, int? height, int? format, int? rowBytes]) { - final SkAnimatedImage skAnimatedImage = - canvasKit.MakeAnimatedImageFromEncoded(list); - final CkAnimatedImage animatedImage = CkAnimatedImage(skAnimatedImage); - final CkAnimatedImageCodec codec = CkAnimatedImageCodec(animatedImage); - callback(codec); + return CkAnimatedImage.decodeFromBytes(list, 'encoded image bytes'); } -/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia after requesting from URI. -void skiaInstantiateWebImageCodec(String src, Callback callback, - WebOnlyImageCodecChunkCallback? chunkCallback) { - chunkCallback?.call(0, 100); - //TODO: Switch to using MakeImageFromCanvasImageSource when animated images are supported. - html.HttpRequest.request( - src, - responseType: "arraybuffer", - ).then((html.HttpRequest response) { - chunkCallback?.call(100, 100); - final Uint8List list = - new Uint8List.view((response.response as ByteBuffer)); - final SkAnimatedImage skAnimatedImage = - canvasKit.MakeAnimatedImageFromEncoded(list); - final CkAnimatedImage animatedImage = CkAnimatedImage(skAnimatedImage); - final CkAnimatedImageCodec codec = CkAnimatedImageCodec(animatedImage); - callback(codec); - }); +/// Thrown when the web engine fails to decode an image, either due to a +/// network issue, corrupted image contents, or missing codec. +class ImageCodecException implements Exception { + ImageCodecException(this._message); + + final String _message; + + @override + String toString() => 'ImageCodecException: $_message'; } -/// A wrapper for `SkAnimatedImage`. -class CkAnimatedImage implements ui.Image { - final SkAnimatedImage _skAnimatedImage; +const String _kNetworkImageMessage = 'Failed to load network image.'; - // Use a box because `SkImage` may be deleted either due to this object - // being garbage-collected, or by an explicit call to [delete]. - late final SkiaObjectBox box; +typedef HttpRequestFactory = html.HttpRequest Function(); +// ignore: prefer_function_declarations_over_variables +HttpRequestFactory httpRequestFactory = () => html.HttpRequest(); +void debugRestoreHttpRequestFactory() { + httpRequestFactory = () => html.HttpRequest(); +} - CkAnimatedImage(this._skAnimatedImage) { - box = SkiaObjectBox(this, _skAnimatedImage as SkDeletable); +/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia after +/// requesting from URI. +Future skiaInstantiateWebImageCodec( + String url, WebOnlyImageCodecChunkCallback? chunkCallback) { + final Completer completer = Completer(); + + final html.HttpRequest request = httpRequestFactory(); + request.open('GET', url, async: true); + request.responseType = 'arraybuffer'; + if (chunkCallback != null) { + request.onProgress.listen((html.ProgressEvent event) { + chunkCallback.call(event.loaded!, event.total!); + }); } + request.onError.listen((html.ProgressEvent event) { + completer.completeError(ImageCodecException('$_kNetworkImageMessage\n' + 'Image URL: $url\n' + 'Trying to load an image from another domain? Find answers at:\n' + 'https://flutter.dev/docs/development/platform-integration/web-images')); + }); + + request.onLoad.listen((html.ProgressEvent event) { + final int status = request.status!; + final bool accepted = status >= 200 && status < 300; + final bool fileUri = status == 0; // file:// URIs have status of 0. + final bool notModified = status == 304; + final bool unknownRedirect = status > 307 && status < 400; + final bool success = accepted || fileUri || notModified || unknownRedirect; + + if (!success) { + completer.completeError( + ImageCodecException('$_kNetworkImageMessage\n' + 'Image URL: $url\n' + 'Server response code: $status'), + ); + return; + } + + try { + final Uint8List list = + Uint8List.view(request.response as ByteBuffer); + final CkAnimatedImage codec = CkAnimatedImage.decodeFromBytes(list, url); + completer.complete(codec); + } catch (error, stackTrace) { + completer.completeError(error, stackTrace); + } + }); + + request.send(); + return completer.future; +} + +/// The CanvasKit implementation of [ui.Codec]. +/// +/// Wraps `SkAnimatedImage`. +class CkAnimatedImage extends ManagedSkiaObject + implements ui.Codec { + /// Decodes an image from a list of encoded bytes. + CkAnimatedImage.decodeFromBytes(this._bytes, this.src); + + final String src; + final Uint8List _bytes; + int _frameCount = 0; + int _repetitionCount = -1; + + /// The index to the next frame to be decoded. + int _nextFrameIndex = 0; + @override - void dispose() { - box.delete(); + SkAnimatedImage createDefault() { + final SkAnimatedImage? animatedImage = + canvasKit.MakeAnimatedImageFromEncoded(_bytes); + if (animatedImage == null) { + throw ImageCodecException( + 'Failed to decode image data.\n' + 'Image source: $src', + ); + } + + _frameCount = animatedImage.getFrameCount(); + _repetitionCount = animatedImage.getRepetitionCount(); + + // If the object has been deleted then resurrected, it may already have + // iterated over some frames. We need to skip over them. + for (int i = 0; i < _nextFrameIndex; i++) { + animatedImage.decodeNextFrame(); + } + return animatedImage; } @override - ui.Image clone() => this; + SkAnimatedImage resurrect() => createDefault(); @override - bool isCloneOf(ui.Image other) => other == this; + bool get isResurrectionExpensive => true; @override - List? debugGetOpenHandleStackTraces() => null; - int get frameCount => _skAnimatedImage.getFrameCount(); - - /// Decodes the next frame and returns the frame duration. - Duration decodeNextFrame() { - final int durationMillis = _skAnimatedImage.decodeNextFrame(); - return Duration(milliseconds: durationMillis); + void delete() { + rawSkiaObject?.delete(); } - int get repetitionCount => _skAnimatedImage.getRepetitionCount(); + bool _disposed = false; + bool get debugDisposed => _disposed; + + bool _debugCheckIsNotDisposed() { + assert(!_disposed, 'This image has been disposed.'); + return true; + } - CkImage get currentFrameAsImage { - return CkImage(_skAnimatedImage.getCurrentFrame()); + @override + void dispose() { + assert( + !_disposed, + 'Cannot dispose a codec that has already been disposed.', + ); + _disposed = true; + delete(); } @override - int get width => _skAnimatedImage.width(); + int get frameCount { + assert(_debugCheckIsNotDisposed()); + return _frameCount; + } @override - int get height => _skAnimatedImage.height(); + int get repetitionCount { + assert(_debugCheckIsNotDisposed()); + return _repetitionCount; + } @override - Future toByteData( - {ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { - Uint8List bytes; + Future getNextFrame() { + assert(_debugCheckIsNotDisposed()); + final int durationMillis = skiaObject.decodeNextFrame(); + final Duration duration = Duration(milliseconds: durationMillis); + final CkImage image = CkImage(skiaObject.makeImageAtCurrentFrame()); + _nextFrameIndex = (_nextFrameIndex + 1) % _frameCount; + return Future.value(AnimatedImageFrameInfo(duration, image)); + } +} - if (format == ui.ImageByteFormat.rawRgba) { - final SkImageInfo imageInfo = SkImageInfo( +/// A [ui.Image] backed by an `SkImage` from Skia. +class CkImage implements ui.Image, StackTraceDebugger { + CkImage(SkImage skImage) { + if (assertionsEnabled) { + _debugStackTrace = StackTrace.current; + } + if (browserSupportsFinalizationRegistry) { + box = SkiaObjectBox(this, skImage); + } else { + // If finalizers are not supported we need to be able to resurrect the + // image if it was temporarily deleted. To do that, we keep the original + // pixels and ask the SkiaObjectBox to make an image from them when + // resurrecting. + // + // IMPORTANT: the alphaType, colorType, and colorSpace passed to + // _encodeImage and to canvasKit.MakeImage must be the same. Otherwise + // Skia will misinterpret the pixels and corrupt the image. + final ByteData? originalBytes = _encodeImage( + skImage: skImage, + format: ui.ImageByteFormat.rawRgba, alphaType: canvasKit.AlphaType.Premul, colorType: canvasKit.ColorType.RGBA_8888, colorSpace: SkColorSpaceSRGB, - width: width, - height: height, ); - bytes = _skAnimatedImage.readPixels(imageInfo, 0, 0); - } else { - final SkData skData = _skAnimatedImage.encodeToData(); //defaults to PNG 100% - // make a copy that we can return - bytes = Uint8List.fromList(canvasKit.getSkDataBytes(skData)); + if (originalBytes == null) { + printWarning('Unable to encode image to bytes. We will not ' + 'be able to resurrect it once it has been garbage collected.'); + return; + } + final int originalWidth = skImage.width(); + final int originalHeight = skImage.height(); + box = SkiaObjectBox.resurrectable(this, skImage, () { + return canvasKit.MakeImage( + SkImageInfo( + alphaType: canvasKit.AlphaType.Premul, + colorType: canvasKit.ColorType.RGBA_8888, + colorSpace: SkColorSpaceSRGB, + width: originalWidth, + height: originalHeight, + ), + originalBytes.buffer.asUint8List(), + 4 * originalWidth); + }); } + } - final ByteData data = bytes.buffer.asByteData(0, bytes.length); - return Future.value(data); + CkImage.cloneOf(this.box) { + if (assertionsEnabled) { + _debugStackTrace = StackTrace.current; + } + box.ref(this); } @override - String toString() => '[$width\u00D7$height]'; -} - -/// A [ui.Image] backed by an `SkImage` from Skia. -class CkImage implements ui.Image { - final SkImage skImage; + StackTrace get debugStackTrace => _debugStackTrace!; + StackTrace? _debugStackTrace; // Use a box because `SkImage` may be deleted either due to this object // being garbage-collected, or by an explicit call to [delete]. - late final SkiaObjectBox box; + late final SkiaObjectBox box; + + /// The underlying Skia image object. + /// + /// Do not store the returned value. It is memory-managed by [SkiaObjectBox]. + /// Storing it may result in use-after-free bugs. + SkImage get skImage => box.skiaObject; + + bool _disposed = false; - CkImage(this.skImage) { - box = SkiaObjectBox(this, skImage as SkDeletable); + bool _debugCheckIsNotDisposed() { + assert(!_disposed, 'This image has been disposed.'); + return true; } @override void dispose() { - box.delete(); + assert( + !_disposed, + 'Cannot dispose an image that has already been disposed.', + ); + _disposed = true; + box.unref(this); } @override - ui.Image clone() => this; + bool get debugDisposed { + if (assertionsEnabled) { + return _disposed; + } + throw StateError( + 'Image.debugDisposed is only available when asserts are enabled.'); + } @override - bool isCloneOf(ui.Image other) => other == this; + CkImage clone() { + assert(_debugCheckIsNotDisposed()); + return CkImage.cloneOf(box); + } @override - List? debugGetOpenHandleStackTraces() => null; + bool isCloneOf(ui.Image other) { + assert(_debugCheckIsNotDisposed()); + return other is CkImage && other.skImage.isAliasOf(skImage); + } @override - int get width => skImage.width(); + List? debugGetOpenHandleStackTraces() => + box.debugGetStackTraces(); @override - int get height => skImage.height(); + int get width { + assert(_debugCheckIsNotDisposed()); + return skImage.width(); + } @override - Future toByteData( - {ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { - Uint8List bytes; + int get height { + assert(_debugCheckIsNotDisposed()); + return skImage.height(); + } - if (format == ui.ImageByteFormat.rawRgba) { - final SkImageInfo imageInfo = SkImageInfo( - alphaType: canvasKit.AlphaType.Premul, - colorType: canvasKit.ColorType.RGBA_8888, - colorSpace: SkColorSpaceSRGB, - width: width, - height: height, - ); - bytes = skImage.readPixels(imageInfo, 0, 0); + @override + Future toByteData({ + ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba, + }) { + assert(_debugCheckIsNotDisposed()); + final ByteData? data = _encodeImage( + skImage: skImage, + format: format, + alphaType: canvasKit.AlphaType.Premul, + colorType: canvasKit.ColorType.RGBA_8888, + colorSpace: SkColorSpaceSRGB, + ); + if (data == null) { + return Future.error('Failed to encode the image into bytes.'); } else { - final SkData skData = skImage.encodeToData(); //defaults to PNG 100% - // make a copy that we can return - bytes = Uint8List.fromList(canvasKit.getSkDataBytes(skData)); + return Future.value(data); } - - final ByteData data = bytes.buffer.asByteData(0, bytes.length); - return Future.value(data); } - @override - String toString() => '[$width\u00D7$height]'; -} - -/// A [Codec] that wraps an `SkAnimatedImage`. -class CkAnimatedImageCodec implements ui.Codec { - CkAnimatedImage animatedImage; + static ByteData? _encodeImage({ + required SkImage skImage, + required ui.ImageByteFormat format, + required SkAlphaType alphaType, + required SkColorType colorType, + required ColorSpace colorSpace, + }) { + Uint8List? bytes; - CkAnimatedImageCodec(this.animatedImage); + if (format == ui.ImageByteFormat.rawRgba) { + final SkImageInfo imageInfo = SkImageInfo( + alphaType: alphaType, + colorType: colorType, + colorSpace: colorSpace, + width: skImage.width(), + height: skImage.height(), + ); + bytes = skImage.readPixels(0, 0, imageInfo); + } else { + bytes = skImage.encodeToBytes(); //defaults to PNG 100% + } - @override - void dispose() { - animatedImage.dispose(); + return bytes?.buffer.asByteData(0, bytes.length); } @override - int get frameCount => animatedImage.frameCount; - - @override - int get repetitionCount => animatedImage.repetitionCount; - - @override - Future getNextFrame() { - final Duration duration = animatedImage.decodeNextFrame(); - final CkImage image = animatedImage.currentFrameAsImage; - return Future.value(AnimatedImageFrameInfo(duration, image)); + String toString() { + assert(_debugCheckIsNotDisposed()); + return '[$width\u00D7$height]'; } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart index 02ebb4bccad4e..4b48c28f43668 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -2,19 +2,48 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'color_filter.dart'; +import 'skia_object_cache.dart'; + +/// An [ImageFilter] that can create a managed skia [SkImageFilter] object. +/// +/// Concrete subclasses of this interface must provide efficient implementation +/// of [operator==], to avoid re-creating the underlying skia filters +/// whenever possible. +/// +/// Currently implemented by [CkImageFilter] and [CkColorFilter]. +abstract class CkManagedSkImageFilterConvertible + implements ui.ImageFilter { + ManagedSkiaObject get imageFilter; +} /// The CanvasKit implementation of [ui.ImageFilter]. /// -/// Currently only supports `blur`. -class CkImageFilter extends ManagedSkiaObject implements ui.ImageFilter { - CkImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0}) - : _sigmaX = sigmaX, - _sigmaY = sigmaY; +/// Currently only supports `blur`, `matrix`, and ColorFilters. +abstract class CkImageFilter extends ManagedSkiaObject + implements CkManagedSkImageFilterConvertible { + factory CkImageFilter.blur( + {required double sigmaX, + required double sigmaY, + required ui.TileMode tileMode}) = _CkBlurImageFilter; + factory CkImageFilter.color({required CkColorFilter colorFilter}) = + CkColorFilterImageFilter; + factory CkImageFilter.matrix( + {required Float64List matrix, + required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter; - final double _sigmaX; - final double _sigmaY; + CkImageFilter._(); + + @override + ManagedSkiaObject get imageFilter => this; + + SkImageFilter _initSkiaObject(); @override SkImageFilter createDefault() => _initSkiaObject(); @@ -26,28 +55,113 @@ class CkImageFilter extends ManagedSkiaObject implements ui.Image void delete() { rawSkiaObject?.delete(); } +} + +class CkColorFilterImageFilter extends CkImageFilter { + CkColorFilterImageFilter({required this.colorFilter}) : super._(); + + final CkColorFilter colorFilter; + + @override + SkImageFilter _initSkiaObject() => colorFilter.initRawImageFilter(); + + @override + int get hashCode => colorFilter.hashCode; + + @override + bool operator ==(Object other) { + if (runtimeType != other.runtimeType) { + return false; + } + return other is CkColorFilterImageFilter && + other.colorFilter == colorFilter; + } + + @override + String toString() => colorFilter.toString(); +} +class _CkBlurImageFilter extends CkImageFilter { + _CkBlurImageFilter( + {required this.sigmaX, required this.sigmaY, required this.tileMode}) + : super._(); + + final double sigmaX; + final double sigmaY; + final ui.TileMode tileMode; + + String get _modeString { + switch (tileMode) { + case ui.TileMode.clamp: + return 'clamp'; + case ui.TileMode.mirror: + return 'mirror'; + case ui.TileMode.repeated: + return 'repeated'; + case ui.TileMode.decal: + return 'decal'; + } + } + + @override SkImageFilter _initSkiaObject() { - return canvasKit.SkImageFilter.MakeBlur( - _sigmaX, - _sigmaY, - canvasKit.TileMode.Clamp, + return canvasKit.ImageFilter.MakeBlur( + sigmaX, + sigmaY, + toSkTileMode(tileMode), null, ); } @override bool operator ==(Object other) { - return other is CkImageFilter - && other._sigmaX == _sigmaX - && other._sigmaY == _sigmaY; + if (runtimeType != other.runtimeType) { + return false; + } + return other is _CkBlurImageFilter && + other.sigmaX == sigmaX && + other.sigmaY == sigmaY && + other.tileMode == tileMode; } @override - int get hashCode => ui.hashValues(_sigmaX, _sigmaY); + int get hashCode => ui.hashValues(sigmaX, sigmaY, tileMode); @override String toString() { - return 'ImageFilter.blur($_sigmaX, $_sigmaY)'; + return 'ImageFilter.blur($sigmaX, $sigmaY, $_modeString)'; + } +} + +class _CkMatrixImageFilter extends CkImageFilter { + _CkMatrixImageFilter({ required Float64List matrix, required this.filterQuality }) + : this.matrix = Float64List.fromList(matrix), // ignore: unnecessary_this + super._(); + + final Float64List matrix; + final ui.FilterQuality filterQuality; + + @override + SkImageFilter _initSkiaObject() { + return canvasKit.ImageFilter.MakeMatrixTransform( + toSkMatrixFromFloat64(matrix), + toSkFilterQuality(filterQuality), + null, + ); } + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is _CkMatrixImageFilter + && other.filterQuality == filterQuality + && listEquals(other.matrix, matrix); + } + + @override + int get hashCode => ui.hashValues(filterQuality, ui.hashList(matrix)); + + @override + String toString() => 'ImageFilter.matrix($matrix, $filterQuality)'; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart index c1d91ad525925..351c9ea0c0c05 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart @@ -1,45 +1,136 @@ // 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. +@JS() +library canvaskit_initialization; -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js' as js; -/// EXPERIMENTAL: Enable the Skia-based rendering backend. -const bool experimentalUseSkia = +import 'package:js/js.dart'; + +import '../../engine.dart' show kProfileMode; +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import 'canvaskit_api.dart'; +import 'fonts.dart'; + +/// A JavaScript entrypoint that allows developer to set rendering backend +/// at runtime before launching the application. +@JS('window.flutterWebRenderer') +external String? get requestedRendererType; + +/// Whether to use CanvasKit as the rendering backend. +bool get useCanvasKit => flutterWebAutoDetect ? _detectRenderer() : _useSkia; + +/// Returns true if CanvasKit is used. +/// +/// Otherwise, returns false. +bool _detectRenderer() { + if (requestedRendererType != null) { + return requestedRendererType! == 'canvaskit'; + } + // If requestedRendererType is not specified, use CanvasKit for desktop and + // html for mobile. + return isDesktop; +} + +/// Auto detect which rendering backend to use. +/// +/// Using flutter tools option "--web-render=auto" or not specifying one +/// would set the value to true. Otherwise, it would be false. +const bool flutterWebAutoDetect = + bool.fromEnvironment('FLUTTER_WEB_AUTO_DETECT', defaultValue: true); + +/// Enable the Skia-based rendering backend. +/// +/// Using flutter tools option "--web-render=canvaskit" would set the value to +/// true. +/// Using flutter tools option "--web-render=html" would set the value to false. +const bool _useSkia = bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false); -// If set to true, forces CPU-only rendering (i.e. no WebGL). -const bool canvasKitForceCpuOnly = - bool.fromEnvironment('FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', defaultValue: false); +/// If set to true, forces CPU-only rendering (i.e. no WebGL). +/// +/// This is mainly used for testing or for apps that want to ensure they +/// run on devices which don't support WebGL. +const bool canvasKitForceCpuOnly = bool.fromEnvironment( + 'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', + defaultValue: false); + +/// The version of CanvasKit used by the web engine by default. +// DO NOT EDIT THE NEXT LINE OF CODE MANUALLY +// See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. +const String canvaskitVersion = '0.28.1'; /// The URL to use when downloading the CanvasKit script and associated wasm. /// +/// The expected directory structure nested under this URL is as follows: +/// +/// /canvaskit.js - the release build of CanvasKit JS API bindings +/// /canvaskit.wasm - the release build of CanvasKit WASM module +/// /profiling/canvaskit.js - the profile build of CanvasKit JS API bindings +/// /profiling/canvaskit.wasm - the profile build of CanvasKit WASM module +/// +/// The base URL can be overridden using the `FLUTTER_WEB_CANVASKIT_URL` +/// environment variable, which can be set in the Flutter tool using the +/// `--dart-define` option. The value must end with a `/`. +/// +/// Example: +/// +/// ``` +/// flutter run \ +/// -d chrome \ +/// --web-renderer=canvaskit \ +/// --dart-define=FLUTTER_WEB_CANVASKIT_URL=https://example.com/custom-canvaskit-build/ +/// ``` +/// /// When CanvasKit pushes a new release to NPM, update this URL to reflect the /// most recent version. For example, if CanvasKit releases version 0.34.0 to /// NPM, update this URL to `https://unpkg.com/canvaskit-wasm@0.34.0/bin/`. const String canvasKitBaseUrl = String.fromEnvironment( 'FLUTTER_WEB_CANVASKIT_URL', - defaultValue: 'https://unpkg.com/canvaskit-wasm@0.17.3/bin/', + defaultValue: 'https://unpkg.com/canvaskit-wasm@$canvaskitVersion/bin/', ); +const String canvasKitBuildUrl = + canvasKitBaseUrl + (kProfileMode ? 'profiling/' : ''); +const String canvasKitJavaScriptBindingsUrl = + canvasKitBuildUrl + 'canvaskit.js'; +String canvasKitWasmModuleUrl(String file) => _currentCanvasKitBase! + file; + +/// The script element which CanvasKit is loaded from. +html.ScriptElement? _canvasKitScript; + +/// A [Future] which completes when the CanvasKit script has been loaded. +Future? _canvasKitLoaded; + +/// The currently used base URL for loading CanvasKit. +String? _currentCanvasKitBase; /// Initialize CanvasKit. /// /// This calls `CanvasKitInit` and assigns the global [canvasKit] object. -Future initializeCanvasKit() { +Future initializeCanvasKit({String? canvasKitBase}) { final Completer canvasKitCompleter = Completer(); - late StreamSubscription loadSubscription; - loadSubscription = domRenderer.canvasKitScript!.onLoad.listen((_) { - loadSubscription.cancel(); - final CanvasKitInitPromise canvasKitInitPromise = CanvasKitInit(CanvasKitInitOptions( - locateFile: js.allowInterop((String file, String unusedBase) => canvasKitBaseUrl + file), - )); - canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) { - canvasKit = ck; - windowFlutterCanvasKit = canvasKit; - canvasKitCompleter.complete(); - })); - }); + if (windowFlutterCanvasKit != null) { + canvasKit = windowFlutterCanvasKit!; + canvasKitCompleter.complete(); + } else { + _startDownloadingCanvasKit(canvasKitBase); + _canvasKitLoaded!.then((_) { + final CanvasKitInitPromise canvasKitInitPromise = + CanvasKitInit(CanvasKitInitOptions( + locateFile: js.allowInterop( + (String file, String unusedBase) => canvasKitWasmModuleUrl(file)), + )); + canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) { + canvasKit = ck; + windowFlutterCanvasKit = canvasKit; + canvasKitCompleter.complete(); + })); + }); + } /// Add a Skia scene host. skiaSceneHost = html.Element.tag('flt-scene'); @@ -47,6 +138,91 @@ Future initializeCanvasKit() { return canvasKitCompleter.future; } +/// Starts downloading the CanvasKit JavaScript file at [canvasKitBase] and sets +/// [_canvasKitLoaded]. +void _startDownloadingCanvasKit(String? canvasKitBase) { + final String canvasKitJavaScriptUrl = canvasKitBase != null + ? canvasKitBase + 'canvaskit.js' + : canvasKitJavaScriptBindingsUrl; + _currentCanvasKitBase = canvasKitBase ?? canvasKitBuildUrl; + // Only reset CanvasKit if it's not already available. + if (windowFlutterCanvasKit == null) { + _canvasKitScript?.remove(); + _canvasKitScript = html.ScriptElement(); + _canvasKitScript!.src = canvasKitJavaScriptUrl; + + final Completer canvasKitLoadCompleter = Completer(); + _canvasKitLoaded = canvasKitLoadCompleter.future; + + late StreamSubscription loadSubscription; + loadSubscription = _canvasKitScript!.onLoad.listen((_) { + loadSubscription.cancel(); + canvasKitLoadCompleter.complete(); + }); + + // TODO(hterkelsen): Rather than this monkey-patch hack, we should + // build CanvasKit ourselves. See: + // https://github.com/flutter/flutter/issues/52588 + + // Monkey-patch the top-level `module` and `exports` objects so that + // CanvasKit doesn't attempt to register itself as an anonymous module. + // + // The idea behind making these fake `exports` and `module` objects is + // that `canvaskit.js` contains the following lines of code: + // + // if (typeof exports === 'object' && typeof module === 'object') + // module.exports = CanvasKitInit; + // else if (typeof define === 'function' && define['amd']) + // define([], function() { return CanvasKitInit; }); + // + // We need to avoid hitting the case where CanvasKit defines an anonymous + // module, since this breaks RequireJS, which DDC and some plugins use. + // Temporarily removing the `define` function won't work because RequireJS + // could load in between this code running and the CanvasKit code running. + // Also, we cannot monkey-patch the `define` function because it is + // non-configurable (it is a top-level 'var'). + + // First check if `exports` and `module` are already defined. If so, then + // CommonJS is being used, and we shouldn't have any problems. + final js.JsFunction objectConstructor = js.context['Object'] as js.JsFunction; + if (js.context['exports'] == null) { + final js.JsObject exportsAccessor = js.JsObject.jsify({ + 'get': js.allowInterop(() { + if (html.document.currentScript == _canvasKitScript) { + return js.JsObject(objectConstructor); + } else { + return js.context['_flutterWebCachedExports']; + } + }), + 'set': js.allowInterop((dynamic value) { + js.context['_flutterWebCachedExports'] = value; + }), + 'configurable': true, + }); + objectConstructor.callMethod( + 'defineProperty', [js.context, 'exports', exportsAccessor]); + } + if (js.context['module'] == null) { + final js.JsObject moduleAccessor = js.JsObject.jsify({ + 'get': js.allowInterop(() { + if (html.document.currentScript == _canvasKitScript) { + return js.JsObject(objectConstructor); + } else { + return js.context['_flutterWebCachedModule']; + } + }), + 'set': js.allowInterop((dynamic value) { + js.context['_flutterWebCachedModule'] = value; + }), + 'configurable': true, + }); + objectConstructor.callMethod( + 'defineProperty', [js.context, 'module', moduleAccessor]); + } + html.document.head!.append(_canvasKitScript!); + } +} + /// The Skia font collection. SkiaFontCollection get skiaFontCollection => _skiaFontCollection!; SkiaFontCollection? _skiaFontCollection; diff --git a/lib/web_ui/lib/src/engine/canvaskit/interval_tree.dart b/lib/web_ui/lib/src/engine/canvaskit/interval_tree.dart new file mode 100644 index 0000000000000..f3fb5f7dd87ce --- /dev/null +++ b/lib/web_ui/lib/src/engine/canvaskit/interval_tree.dart @@ -0,0 +1,157 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'font_fallbacks.dart' show CodeunitRange; + +/// A tree which stores a set of intervals that can be queried for intersection. +class IntervalTree { + /// The root node of the interval tree. + final IntervalTreeNode root; + + IntervalTree._(this.root); + + /// Creates an interval tree from a mapping of [T] values to a list of ranges. + /// + /// When the interval tree is queried, it will return a list of [T]s which + /// have a range which contains the point. + factory IntervalTree.createFromRanges(Map> rangesMap) { + assert(rangesMap.isNotEmpty); + // Get a list of all the ranges ordered by start index. + final List> intervals = >[]; + rangesMap.forEach((T key, List rangeList) { + for (final CodeunitRange range in rangeList) { + intervals.add(IntervalTreeNode(key, range.start, range.end)); + } + }); + assert(intervals.isNotEmpty); + + intervals + .sort((IntervalTreeNode a, IntervalTreeNode b) => a.low - b.low); + + // Make a balanced binary search tree from the nodes sorted by low value. + IntervalTreeNode? _makeBalancedTree(List> nodes) { + if (nodes.isEmpty) { + return null; + } + if (nodes.length == 1) { + return nodes.single; + } + final int mid = nodes.length ~/ 2; + final IntervalTreeNode root = nodes[mid]; + root.left = _makeBalancedTree(nodes.sublist(0, mid)); + root.right = _makeBalancedTree(nodes.sublist(mid + 1)); + return root; + } + + // Given a node, computes the highest `high` point of all of the subnodes. + // + // As a side effect, this also computes the high point of all subnodes. + void _computeHigh(IntervalTreeNode root) { + if (root.left == null && root.right == null) { + root.computedHigh = root.high; + } else if (root.left == null) { + _computeHigh(root.right!); + root.computedHigh = math.max(root.high, root.right!.computedHigh); + } else if (root.right == null) { + _computeHigh(root.left!); + root.computedHigh = math.max(root.high, root.left!.computedHigh); + } else { + _computeHigh(root.right!); + _computeHigh(root.left!); + root.computedHigh = math.max( + root.high, + math.max( + root.left!.computedHigh, + root.right!.computedHigh, + )); + } + } + + final IntervalTreeNode root = _makeBalancedTree(intervals)!; + _computeHigh(root); + + return IntervalTree._(root); + } + + /// Returns the list of objects which have been associated with intervals that + /// intersect with [x]. + List intersections(int x) { + final List results = []; + root.searchForPoint(x, results); + return results; + } + + /// Whether this tree contains at least one interval that includes [x]. + bool containsDeep(int x) { + return root.containsDeep(x); + } +} + +class IntervalTreeNode { + final T value; + final int low; + final int high; + int computedHigh; + + IntervalTreeNode? left; + IntervalTreeNode? right; + + IntervalTreeNode(this.value, this.low, this.high) : computedHigh = high; + + Iterable enumerateAllElements() sync* { + if (left != null) { + yield* left!.enumerateAllElements(); + } + yield value; + if (right != null) { + yield* right!.enumerateAllElements(); + } + } + + /// Whether this node contains [x]. + /// + /// Does not recursively check whether child nodes contain [x]. + bool containsShallow(int x) { + return low <= x && x <= high; + } + + /// Whether this sub-tree contains [x]. + /// + /// Recursively checks whether child nodes contain [x]. + bool containsDeep(int x) { + if (x > computedHigh) { + // x is above the highest possible value stored in this subtree. + // Don't bother checking intervals. + return false; + } + if (containsShallow(x)) { + return true; + } + if (left?.containsDeep(x) == true) { + return true; + } + if (x < low) { + // The right tree can't possible contain x. Don't bother checking. + return false; + } + return right?.containsDeep(x) == true; + } + + // Searches the tree rooted at this node for all T containing [x]. + void searchForPoint(int x, List result) { + if (x > computedHigh) { + return; + } + left?.searchForPoint(x, result); + if (containsShallow(x)) { + result.add(value); + } + if (x < low) { + return; + } + right?.searchForPoint(x, result); + } +} diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index eb885ef9b5f3e..d061e4ab8897b 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -2,8 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; +import '../vector_math.dart'; +import 'canvas.dart'; +import 'embedded_views.dart'; +import 'n_way_canvas.dart'; +import 'painting.dart'; +import 'path.dart'; +import 'picture.dart'; +import 'raster_cache.dart'; +import 'util.dart'; /// A layer to be composed into a scene. /// @@ -28,6 +38,11 @@ abstract class Layer implements ui.EngineLayer { /// Paint this layer into the scene. void paint(PaintContext paintContext); + + // TODO(dnfield): Implement ui.EngineLayer.dispose for CanvasKit. + // https://github.com/flutter/flutter/issues/82878 + @override + void dispose() {} } /// A context shared by all layers during the preroll pass. @@ -41,6 +56,28 @@ class PrerollContext { final MutatorsStack mutatorsStack = MutatorsStack(); PrerollContext(this.rasterCache, this.viewEmbedder); + + ui.Rect get cullRect { + ui.Rect cullRect = ui.Rect.largest; + for (final Mutator m in mutatorsStack) { + ui.Rect clipRect; + switch (m.type) { + case MutatorType.clipRect: + clipRect = m.rect!; + break; + case MutatorType.clipRRect: + clipRect = m.rrect!.outerRect; + break; + case MutatorType.clipPath: + clipRect = m.path!.getBounds(); + break; + default: + continue; + } + cullRect = cullRect.intersect(clipRect); + } + return cullRect; + } } /// A context shared by all layers during the paint pass. @@ -71,6 +108,11 @@ class PaintContext { abstract class ContainerLayer extends Layer { final List _layers = []; + /// The list of child layers. + /// + /// Useful in tests. + List get debugLayers => _layers; + /// Register [child] as a child of this layer. void add(Layer child) { child.parent = this; @@ -89,7 +131,7 @@ abstract class ContainerLayer extends Layer { /// [Rect] is empty. ui.Rect prerollChildren(PrerollContext context, Matrix4 childMatrix) { ui.Rect childPaintBounds = ui.Rect.zero; - for (Layer layer in _layers) { + for (final Layer layer in _layers) { layer.preroll(context, childMatrix); if (childPaintBounds.isEmpty) { childPaintBounds = layer.paintBounds; @@ -104,7 +146,7 @@ abstract class ContainerLayer extends Layer { void paintChildren(PaintContext context) { assert(needsPainting); - for (Layer layer in _layers) { + for (final Layer layer in _layers) { if (layer.needsPainting) { layer.paint(context); } @@ -112,37 +154,62 @@ abstract class ContainerLayer extends Layer { } } -class BackdropFilterLayer extends ContainerLayer { +/// The top-most layer in the layer tree. +/// +/// This layer does not draw anything. It's only used so we can add leaf layers +/// to [LayerSceneBuilder] without requiring a [ContainerLayer]. +class RootLayer extends ContainerLayer { + @override + void paint(PaintContext paintContext) { + paintChildren(paintContext); + } +} + +class BackdropFilterEngineLayer extends ContainerLayer + implements ui.BackdropFilterEngineLayer { final ui.ImageFilter _filter; + final ui.BlendMode _blendMode; - BackdropFilterLayer(this._filter); + BackdropFilterEngineLayer(this._filter, this._blendMode); @override - void paint(PaintContext context) { - context.internalNodesCanvas.saveLayerWithFilter(paintBounds, _filter); - paintChildren(context); - context.internalNodesCanvas.restore(); + void preroll(PrerollContext prerollContext, Matrix4 matrix) { + final ui.Rect childBounds = prerollChildren(prerollContext, matrix); + paintBounds = childBounds.expandToInclude(prerollContext.cullRect); } + + @override + void paint(PaintContext paintContext) { + final CkPaint paint = CkPaint()..blendMode = _blendMode; + paintContext.internalNodesCanvas + .saveLayerWithFilter(paintBounds, _filter, paint); + paintChildren(paintContext); + paintContext.internalNodesCanvas.restore(); + } + + // TODO(dnfield): dispose of the _filter + // https://github.com/flutter/flutter/issues/82832 } /// A layer that clips its child layers by a given [Path]. -class ClipPathLayer extends ContainerLayer { +class ClipPathEngineLayer extends ContainerLayer + implements ui.ClipPathEngineLayer { /// The path used to clip child layers. - final ui.Path _clipPath; + final CkPath _clipPath; final ui.Clip _clipBehavior; - ClipPathLayer(this._clipPath, this._clipBehavior) + ClipPathEngineLayer(this._clipPath, this._clipBehavior) : assert(_clipBehavior != ui.Clip.none); @override - void preroll(PrerollContext context, Matrix4 matrix) { - context.mutatorsStack.pushClipPath(_clipPath); - final ui.Rect childPaintBounds = prerollChildren(context, matrix); + void preroll(PrerollContext prerollContext, Matrix4 matrix) { + prerollContext.mutatorsStack.pushClipPath(_clipPath); + final ui.Rect childPaintBounds = prerollChildren(prerollContext, matrix); final ui.Rect clipBounds = _clipPath.getBounds(); if (childPaintBounds.overlaps(clipBounds)) { paintBounds = childPaintBounds.intersect(clipBounds); } - context.mutatorsStack.pop(); + prerollContext.mutatorsStack.pop(); } @override @@ -165,22 +232,23 @@ class ClipPathLayer extends ContainerLayer { } /// A layer that clips its child layers by a given [Rect]. -class ClipRectLayer extends ContainerLayer { +class ClipRectEngineLayer extends ContainerLayer + implements ui.ClipRectEngineLayer { /// The rectangle used to clip child layers. final ui.Rect _clipRect; final ui.Clip _clipBehavior; - ClipRectLayer(this._clipRect, this._clipBehavior) + ClipRectEngineLayer(this._clipRect, this._clipBehavior) : assert(_clipBehavior != ui.Clip.none); @override - void preroll(PrerollContext context, Matrix4 matrix) { - context.mutatorsStack.pushClipRect(_clipRect); - final ui.Rect childPaintBounds = prerollChildren(context, matrix); + void preroll(PrerollContext prerollContext, Matrix4 matrix) { + prerollContext.mutatorsStack.pushClipRect(_clipRect); + final ui.Rect childPaintBounds = prerollChildren(prerollContext, matrix); if (childPaintBounds.overlaps(_clipRect)) { paintBounds = childPaintBounds.intersect(_clipRect); } - context.mutatorsStack.pop(); + prerollContext.mutatorsStack.pop(); } @override @@ -205,22 +273,23 @@ class ClipRectLayer extends ContainerLayer { } /// A layer that clips its child layers by a given [RRect]. -class ClipRRectLayer extends ContainerLayer { +class ClipRRectEngineLayer extends ContainerLayer + implements ui.ClipRRectEngineLayer { /// The rounded rectangle used to clip child layers. final ui.RRect _clipRRect; final ui.Clip? _clipBehavior; - ClipRRectLayer(this._clipRRect, this._clipBehavior) + ClipRRectEngineLayer(this._clipRRect, this._clipBehavior) : assert(_clipBehavior != ui.Clip.none); @override - void preroll(PrerollContext context, Matrix4 matrix) { - context.mutatorsStack.pushClipRRect(_clipRRect); - final ui.Rect childPaintBounds = prerollChildren(context, matrix); + void preroll(PrerollContext prerollContext, Matrix4 matrix) { + prerollContext.mutatorsStack.pushClipRRect(_clipRRect); + final ui.Rect childPaintBounds = prerollChildren(prerollContext, matrix); if (childPaintBounds.overlaps(_clipRRect.outerRect)) { paintBounds = childPaintBounds.intersect(_clipRRect.outerRect); } - context.mutatorsStack.pop(); + prerollContext.mutatorsStack.pop(); } @override @@ -242,22 +311,23 @@ class ClipRRectLayer extends ContainerLayer { } /// A layer that paints its children with the given opacity. -class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer { +class OpacityEngineLayer extends ContainerLayer + implements ui.OpacityEngineLayer { final int _alpha; final ui.Offset _offset; - OpacityLayer(this._alpha, this._offset); + OpacityEngineLayer(this._alpha, this._offset); @override - void preroll(PrerollContext context, Matrix4 matrix) { + void preroll(PrerollContext prerollContext, Matrix4 matrix) { final Matrix4 childMatrix = Matrix4.copy(matrix); childMatrix.translate(_offset.dx, _offset.dy); - context.mutatorsStack + prerollContext.mutatorsStack .pushTransform(Matrix4.translationValues(_offset.dx, _offset.dy, 0.0)); - context.mutatorsStack.pushOpacity(_alpha); - super.preroll(context, childMatrix); - context.mutatorsStack.pop(); - context.mutatorsStack.pop(); + prerollContext.mutatorsStack.pushOpacity(_alpha); + super.preroll(prerollContext, childMatrix); + prerollContext.mutatorsStack.pop(); + prerollContext.mutatorsStack.pop(); paintBounds = paintBounds.translate(_offset.dx, _offset.dy); } @@ -265,7 +335,7 @@ class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer { void paint(PaintContext paintContext) { assert(needsPainting); - final ui.Paint paint = ui.Paint(); + final CkPaint paint = CkPaint(); paint.color = ui.Color.fromARGB(_alpha, 0, 0, 0); paintContext.internalNodesCanvas.save(); @@ -282,56 +352,20 @@ class OpacityLayer extends ContainerLayer implements ui.OpacityEngineLayer { } /// A layer that transforms its child layers by the given transform matrix. -class TransformLayer extends ContainerLayer - implements ui.OffsetEngineLayer, ui.TransformEngineLayer { +class TransformEngineLayer extends ContainerLayer + implements ui.TransformEngineLayer { /// The matrix with which to transform the child layers. final Matrix4 _transform; - TransformLayer(this._transform); + TransformEngineLayer(this._transform); @override - void preroll(PrerollContext context, Matrix4 matrix) { - final Matrix4 childMatrix = matrix * _transform; - context.mutatorsStack.pushTransform(_transform); - final ui.Rect childPaintBounds = prerollChildren(context, childMatrix); - paintBounds = _transformRect(_transform, childPaintBounds); - context.mutatorsStack.pop(); - } - - /// Applies the given matrix as a perspective transform to the given point. - /// - /// This function assumes the given point has a z-coordinate of 0.0. The - /// z-coordinate of the result is ignored. - static ui.Offset _transformPoint(Matrix4 transform, ui.Offset point) { - final Vector3 position3 = Vector3(point.dx, point.dy, 0.0); - final Vector3 transformed3 = transform.perspectiveTransform(position3); - return ui.Offset(transformed3.x, transformed3.y); - } - - /// Returns a rect that bounds the result of applying the given matrix as a - /// perspective transform to the given rect. - /// - /// This function assumes the given rect is in the plane with z equals 0.0. - /// The transformed rect is then projected back into the plane with z equals - /// 0.0 before computing its bounding rect. - static ui.Rect _transformRect(Matrix4 transform, ui.Rect rect) { - final ui.Offset point1 = _transformPoint(transform, rect.topLeft); - final ui.Offset point2 = _transformPoint(transform, rect.topRight); - final ui.Offset point3 = _transformPoint(transform, rect.bottomLeft); - final ui.Offset point4 = _transformPoint(transform, rect.bottomRight); - return ui.Rect.fromLTRB( - _min4(point1.dx, point2.dx, point3.dx, point4.dx), - _min4(point1.dy, point2.dy, point3.dy, point4.dy), - _max4(point1.dx, point2.dx, point3.dx, point4.dx), - _max4(point1.dy, point2.dy, point3.dy, point4.dy)); - } - - static double _min4(double a, double b, double c, double d) { - return math.min(a, math.min(b, math.min(c, d))); - } - - static double _max4(double a, double b, double c, double d) { - return math.max(a, math.max(b, math.max(c, d))); + void preroll(PrerollContext prerollContext, Matrix4 matrix) { + final Matrix4 childMatrix = matrix.multiplied(_transform); + prerollContext.mutatorsStack.pushTransform(_transform); + final ui.Rect childPaintBounds = prerollChildren(prerollContext, childMatrix); + paintBounds = transformRect(_transform, childPaintBounds); + prerollContext.mutatorsStack.pop(); } @override @@ -345,21 +379,69 @@ class TransformLayer extends ContainerLayer } } +/// Translates its children along x and y coordinates. +/// +/// This is a thin wrapper over [TransformEngineLayer] just so the framework +/// gets the "OffsetEngineLayer" when calling `runtimeType.toString()`. This is +/// better for debugging. +class OffsetEngineLayer extends TransformEngineLayer + implements ui.OffsetEngineLayer { + OffsetEngineLayer(double dx, double dy) + : super(Matrix4.translationValues(dx, dy, 0.0)); +} + /// A layer that applies an [ui.ImageFilter] to its children. -class ImageFilterLayer extends ContainerLayer implements ui.OpacityEngineLayer { - ImageFilterLayer(this._filter); +class ImageFilterEngineLayer extends ContainerLayer + implements ui.ImageFilterEngineLayer { + ImageFilterEngineLayer(this._filter); final ui.ImageFilter _filter; @override void paint(PaintContext paintContext) { assert(needsPainting); - final ui.Paint paint = ui.Paint(); + final CkPaint paint = CkPaint(); paint.imageFilter = _filter; paintContext.internalNodesCanvas.saveLayer(paintBounds, paint); paintChildren(paintContext); paintContext.internalNodesCanvas.restore(); } + + // TODO(dnfield): dispose of the _filter + // https://github.com/flutter/flutter/issues/82832 +} + +class ShaderMaskEngineLayer extends ContainerLayer + implements ui.ShaderMaskEngineLayer { + ShaderMaskEngineLayer( + this.shader, this.maskRect, this.blendMode, this.filterQuality); + + final ui.Shader shader; + final ui.Rect maskRect; + final ui.BlendMode blendMode; + final ui.FilterQuality filterQuality; + + @override + void paint(PaintContext paintContext) { + assert(needsPainting); + + paintContext.internalNodesCanvas.saveLayer(paintBounds, null); + paintChildren(paintContext); + + final CkPaint paint = CkPaint(); + paint.shader = shader; + paint.blendMode = blendMode; + paint.filterQuality = filterQuality; + + paintContext.leafNodesCanvas!.save(); + paintContext.leafNodesCanvas!.translate(maskRect.left, maskRect.top); + + paintContext.leafNodesCanvas!.drawRect( + ui.Rect.fromLTWH(0, 0, maskRect.width, maskRect.height), paint); + paintContext.leafNodesCanvas!.restore(); + + paintContext.internalNodesCanvas.restore(); + } } /// A layer containing a [Picture]. @@ -400,7 +482,7 @@ class PictureLayer extends Layer { /// /// The shape clips its children to a given [Path], and casts a shadow based /// on the given elevation. -class PhysicalShapeLayer extends ContainerLayer +class PhysicalShapeEngineLayer extends ContainerLayer implements ui.PhysicalShapeEngineLayer { final double _elevation; final ui.Color _color; @@ -408,7 +490,7 @@ class PhysicalShapeLayer extends ContainerLayer final CkPath _path; final ui.Clip _clipBehavior; - PhysicalShapeLayer( + PhysicalShapeEngineLayer( this._elevation, this._color, this._shadowColor, @@ -419,60 +501,8 @@ class PhysicalShapeLayer extends ContainerLayer @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { prerollChildren(prerollContext, matrix); - - paintBounds = _path.getBounds(); - if (_elevation == 0.0) { - // No need to extend the paint bounds if there is no shadow. - return; - } else { - // Add some margin to the paint bounds to leave space for the shadow. - // We fill this whole region and clip children to it so we don't need to - // join the child paint bounds. - // The offset is calculated as follows: - - // .--- (kLightRadius) - // -------/ (light) - // | / - // | / - // |/ - // |O - // /| (kLightHeight) - // / | - // / | - // / | - // / | - // ------------- (layer) - // /| | - // / | | (elevation) - // A / | |B - // ------------------------------------------------ (canvas) - // --- (extent of shadow) - // - // E = lt } t = (r + w/2)/h - // } => - // r + w/2 = ht } E = (l/h)(r + w/2) - // - // Where: E = extent of shadow - // l = elevation of layer - // r = radius of the light source - // w = width of the layer - // h = light height - // t = tangent of AOB, i.e., multiplier for elevation to extent - final double devicePixelRatio = ui.window.devicePixelRatio; - - final double radius = kLightRadius * devicePixelRatio; - // tangent for x - double tx = (radius + paintBounds.width * 0.5) / kLightHeight; - // tangent for y - double ty = (radius + paintBounds.height * 0.5) / kLightHeight; - - paintBounds = ui.Rect.fromLTRB( - paintBounds.left - tx, - paintBounds.top - ty, - paintBounds.right + tx, - paintBounds.bottom + ty, - ); - } + paintBounds = computeSkShadowBounds( + _path, _elevation, ui.window.devicePixelRatio, matrix); } @override @@ -480,13 +510,13 @@ class PhysicalShapeLayer extends ContainerLayer assert(needsPainting); if (_elevation != 0) { - drawShadow(paintContext.leafNodesCanvas!, _path, _shadowColor!, _elevation, - _color.alpha != 0xff); + drawShadow(paintContext.leafNodesCanvas!, _path, _shadowColor!, + _elevation, _color.alpha != 0xff); } - final ui.Paint paint = ui.Paint()..color = _color; + final CkPaint paint = CkPaint()..color = _color; if (_clipBehavior != ui.Clip.antiAliasWithSaveLayer) { - paintContext.leafNodesCanvas!.drawPath(_path, paint as CkPaint); + paintContext.leafNodesCanvas!.drawPath(_path, paint); } final int saveCount = paintContext.internalNodesCanvas.save(); @@ -510,7 +540,7 @@ class PhysicalShapeLayer extends ContainerLayer // (https://github.com/flutter/flutter/issues/18057#issue-328003931) // using saveLayer, we have to call drawPaint instead of drawPath as // anti-aliased drawPath will always have such artifacts. - paintContext.leafNodesCanvas!.drawPaint(paint as CkPaint); + paintContext.leafNodesCanvas!.drawPaint(paint); } paintChildren(paintContext); @@ -522,12 +552,32 @@ class PhysicalShapeLayer extends ContainerLayer /// /// The blur of the shadow is decided by the [elevation], and the /// shadow is painted with the given [color]. - static void drawShadow(CkCanvas canvas, ui.Path path, ui.Color color, + static void drawShadow(CkCanvas canvas, CkPath path, ui.Color color, double elevation, bool transparentOccluder) { canvas.drawShadow(path, color, elevation, transparentOccluder); } } +/// A layer which contains a [ui.ColorFilter]. +class ColorFilterEngineLayer extends ContainerLayer + implements ui.ColorFilterEngineLayer { + ColorFilterEngineLayer(this.filter); + + final ui.ColorFilter filter; + + @override + void paint(PaintContext paintContext) { + assert(needsPainting); + + final CkPaint paint = CkPaint(); + paint.colorFilter = filter; + + paintContext.internalNodesCanvas.saveLayer(paintBounds, paint); + paintChildren(paintContext); + paintContext.internalNodesCanvas.restore(); + } +} + /// A layer which renders a platform view (an HTML element in this case). class PlatformViewLayer extends Layer { PlatformViewLayer(this.viewId, this.offset, this.width, this.height); @@ -538,21 +588,21 @@ class PlatformViewLayer extends Layer { final double height; @override - void preroll(PrerollContext context, Matrix4 matrix) { + void preroll(PrerollContext prerollContext, Matrix4 matrix) { paintBounds = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height); - context.viewEmbedder!.prerollCompositeEmbeddedView( + prerollContext.viewEmbedder!.prerollCompositeEmbeddedView( viewId, EmbeddedViewParams( offset, ui.Size(width, height), - context.mutatorsStack, + prerollContext.mutatorsStack, ), ); } @override - void paint(PaintContext context) { - CkCanvas? canvas = context.viewEmbedder!.compositeEmbeddedView(viewId); - context.leafNodesCanvas = canvas; + void paint(PaintContext paintContext) { + final CkCanvas? canvas = paintContext.viewEmbedder!.compositeEmbeddedView(viewId); + paintContext.leafNodesCanvas = canvas; } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart index 71e634ed41b94..0835c8bf91431 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart @@ -2,40 +2,40 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show toMatrix32; +import '../vector_math.dart'; +import 'layer.dart'; +import 'layer_tree.dart'; +import 'path.dart'; +import 'picture.dart'; class LayerScene implements ui.Scene { final LayerTree layerTree; - LayerScene(Layer? rootLayer) : layerTree = LayerTree() { - layerTree.rootLayer = rootLayer; - } + LayerScene(RootLayer rootLayer) : layerTree = LayerTree(rootLayer); @override void dispose() {} @override Future toImage(int width, int height) { - throw UnsupportedError('LayerScene.toImage not implemented.'); + final ui.Picture picture = layerTree.flatten(); + return picture.toImage(width, height); } } class LayerSceneBuilder implements ui.SceneBuilder { - Layer? rootLayer; - ContainerLayer? currentLayer; - - @override - void addChildScene({ - ui.Offset offset = ui.Offset.zero, - double width = 0.0, - double height = 0.0, - ui.SceneHost? sceneHost, - bool hitTestable = true, - }) { - throw UnimplementedError(); + LayerSceneBuilder() : rootLayer = RootLayer() { + currentLayer = rootLayer; } + final RootLayer rootLayer; + late ContainerLayer currentLayer; + @override void addPerformanceOverlay(int enabledOptions, ui.Rect bounds) { // We don't plan to implement this on the web. @@ -49,16 +49,13 @@ class LayerSceneBuilder implements ui.SceneBuilder { bool isComplexHint = false, bool willChangeHint = false, }) { - currentLayer!.add(PictureLayer( + currentLayer.add(PictureLayer( picture as CkPicture, offset, isComplexHint, willChangeHint)); } @override void addRetained(ui.EngineLayer retainedLayer) { - if (currentLayer == null) { - return; - } - currentLayer!.add(retainedLayer as Layer); + currentLayer.add(retainedLayer as Layer); } @override @@ -70,7 +67,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { bool freeze = false, ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { - // TODO(b/128315641): implement addTexture. + // TODO(hterkelsen): implement addTexture, b/128315641 } @override @@ -81,104 +78,103 @@ class LayerSceneBuilder implements ui.SceneBuilder { double height = 0.0, Object? webOnlyPaintedBy, }) { - currentLayer!.add(PlatformViewLayer(viewId, offset, width, height)); + currentLayer.add(PlatformViewLayer(viewId, offset, width, height)); } @override - ui.Scene build() { + LayerScene build() { return LayerScene(rootLayer); } @override void pop() { - if (currentLayer == null) { + if (currentLayer == rootLayer) { + // Don't pop the root layer. It must always be there. return; } - currentLayer = currentLayer!.parent; + currentLayer = currentLayer.parent!; } @override - ui.BackdropFilterEngineLayer? pushBackdropFilter( + BackdropFilterEngineLayer pushBackdropFilter( ui.ImageFilter filter, { + ui.BlendMode blendMode = ui.BlendMode.srcOver, ui.EngineLayer? oldLayer, }) { - pushLayer(BackdropFilterLayer(filter)); - return null; + return pushLayer(BackdropFilterEngineLayer( + filter, + blendMode, + )); } @override - ui.ClipPathEngineLayer? pushClipPath( + ClipPathEngineLayer pushClipPath( ui.Path path, { ui.Clip clipBehavior = ui.Clip.antiAlias, ui.EngineLayer? oldLayer, }) { - pushLayer(ClipPathLayer(path, clipBehavior)); - return null; + return pushLayer( + ClipPathEngineLayer(path as CkPath, clipBehavior)); } @override - ui.ClipRRectEngineLayer? pushClipRRect( + ClipRRectEngineLayer pushClipRRect( ui.RRect rrect, { ui.Clip? clipBehavior, ui.EngineLayer? oldLayer, }) { - pushLayer(ClipRRectLayer(rrect, clipBehavior)); - return null; + return pushLayer( + ClipRRectEngineLayer(rrect, clipBehavior)); } @override - ui.ClipRectEngineLayer? pushClipRect( + ClipRectEngineLayer pushClipRect( ui.Rect rect, { ui.Clip clipBehavior = ui.Clip.antiAlias, ui.EngineLayer? oldLayer, }) { - pushLayer(ClipRectLayer(rect, clipBehavior)); - return null; + return pushLayer( + ClipRectEngineLayer(rect, clipBehavior)); } @override - ui.ColorFilterEngineLayer pushColorFilter( + ColorFilterEngineLayer pushColorFilter( ui.ColorFilter filter, { ui.ColorFilterEngineLayer? oldLayer, }) { assert(filter != null); // ignore: unnecessary_null_comparison - throw UnimplementedError(); + return pushLayer(ColorFilterEngineLayer(filter)); } - ui.ImageFilterEngineLayer? pushImageFilter( + @override + ImageFilterEngineLayer pushImageFilter( ui.ImageFilter filter, { ui.ImageFilterEngineLayer? oldLayer, }) { assert(filter != null); // ignore: unnecessary_null_comparison - pushLayer(ImageFilterLayer(filter)); - return null; + return pushLayer(ImageFilterEngineLayer(filter)); } @override - ui.OffsetEngineLayer pushOffset( + OffsetEngineLayer pushOffset( double dx, double dy, { ui.EngineLayer? oldLayer, }) { - final Matrix4 matrix = Matrix4.translationValues(dx, dy, 0.0); - final TransformLayer layer = TransformLayer(matrix); - pushLayer(layer); - return layer; + return pushLayer(OffsetEngineLayer(dx, dy)); } @override - ui.OpacityEngineLayer pushOpacity( + OpacityEngineLayer pushOpacity( int alpha, { ui.EngineLayer? oldLayer, ui.Offset offset = ui.Offset.zero, }) { - final OpacityLayer layer = OpacityLayer(alpha, offset); - pushLayer(layer); - return layer; + return pushLayer(OpacityEngineLayer(alpha, offset)); } @override - ui.PhysicalShapeEngineLayer pushPhysicalShape({ + PhysicalShapeEngineLayer pushPhysicalShape({ required ui.Path path, required double elevation, required ui.Color color, @@ -186,64 +182,55 @@ class LayerSceneBuilder implements ui.SceneBuilder { ui.Clip clipBehavior = ui.Clip.none, ui.EngineLayer? oldLayer, }) { - final PhysicalShapeLayer layer = PhysicalShapeLayer( + return pushLayer(PhysicalShapeEngineLayer( elevation, color, shadowColor, path as CkPath, clipBehavior, - ); - pushLayer(layer); - return layer; + )); } @override - ui.ShaderMaskEngineLayer pushShaderMask( + ShaderMaskEngineLayer pushShaderMask( ui.Shader shader, ui.Rect maskRect, ui.BlendMode blendMode, { ui.EngineLayer? oldLayer, + ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { - throw UnimplementedError(); + return pushLayer( + ShaderMaskEngineLayer(shader, maskRect, blendMode, filterQuality)); } @override - ui.TransformEngineLayer? pushTransform( + TransformEngineLayer pushTransform( Float64List matrix4, { ui.EngineLayer? oldLayer, }) { final Matrix4 matrix = Matrix4.fromFloat32List(toMatrix32(matrix4)); - pushLayer(TransformLayer(matrix)); - return null; + return pushLayer(TransformEngineLayer(matrix)); } @override void setCheckerboardOffscreenLayers(bool checkerboard) { - // TODO: implement setCheckerboardOffscreenLayers + // TODO(hterkelsen): implement setCheckerboardOffscreenLayers } @override void setCheckerboardRasterCacheImages(bool checkerboard) { - // TODO: implement setCheckerboardRasterCacheImages + // TODO(hterkelsen): implement setCheckerboardRasterCacheImages } @override void setRasterizerTracingThreshold(int frameInterval) { - // TODO: implement setRasterizerTracingThreshold + // TODO(hterkelsen): implement setRasterizerTracingThreshold } - void pushLayer(ContainerLayer layer) { - if (rootLayer == null) { - rootLayer = currentLayer = layer; - return; - } - - if (currentLayer == null) { - return; - } - - currentLayer!.add(layer); + T pushLayer(T layer) { + currentLayer.add(layer); currentLayer = layer; + return layer; } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart index 3b40be4728b39..ef677320de8df 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart @@ -2,13 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show kProfilePrerollFrame, kProfileApplyFrame; +import '../profiler.dart'; +import '../vector_math.dart'; +import 'canvas.dart'; +import 'embedded_views.dart'; +import 'layer.dart'; +import 'n_way_canvas.dart'; +import 'picture_recorder.dart'; +import 'raster_cache.dart'; /// A tree of [Layer]s that, together with a [Size] compose a frame. class LayerTree { + LayerTree(this.rootLayer); + /// The root of the layer tree. - Layer? rootLayer; + final RootLayer rootLayer; /// The size (in physical pixels) of the frame to paint this layer tree into. final ui.Size frameSize = ui.window.physicalSize; @@ -27,7 +38,7 @@ class LayerTree { ignoreRasterCache ? null : frame.rasterCache, frame.viewEmbedder, ); - rootLayer!.preroll(context, Matrix4.identity()); + rootLayer.preroll(context, Matrix4.identity()); } /// Paints the layer tree into the given [frame]. @@ -37,20 +48,41 @@ class LayerTree { void paint(Frame frame, {bool ignoreRasterCache = false}) { final CkNWayCanvas internalNodesCanvas = CkNWayCanvas(); internalNodesCanvas.addCanvas(frame.canvas); - final List overlayCanvases = + final List overlayCanvases = frame.viewEmbedder!.getCurrentCanvases(); for (int i = 0; i < overlayCanvases.length; i++) { internalNodesCanvas.addCanvas(overlayCanvases[i]); } + // Clear the canvases before painting + internalNodesCanvas.clear(const ui.Color(0x00000000)); final PaintContext context = PaintContext( internalNodesCanvas, frame.canvas, ignoreRasterCache ? null : frame.rasterCache, frame.viewEmbedder, ); - if (rootLayer!.needsPainting) { - rootLayer!.paint(context); + if (rootLayer.needsPainting) { + rootLayer.paint(context); + } + } + + /// Flattens the tree into a single [ui.Picture]. + /// + /// This picture does not contain any platform views. + ui.Picture flatten() { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + final PrerollContext prerollContext = PrerollContext(null, null); + rootLayer.preroll(prerollContext, Matrix4.identity()); + + final CkNWayCanvas internalNodesCanvas = CkNWayCanvas(); + internalNodesCanvas.addCanvas(canvas); + final PaintContext paintContext = + PaintContext(internalNodesCanvas, canvas, null, null); + if (rootLayer.needsPainting) { + rootLayer.paint(paintContext); } + return recorder.endRecording(); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart index 8c120d1520035..be67b48b35ccb 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import 'canvaskit_api.dart'; +import 'skia_object_cache.dart'; /// The CanvasKit implementation of [ui.MaskFilter]. class CkMaskFilter extends ManagedSkiaObject { @@ -21,11 +23,11 @@ class CkMaskFilter extends ManagedSkiaObject { SkMaskFilter resurrect() => _initSkiaObject(); SkMaskFilter _initSkiaObject() { - return canvasKit.MakeBlurMaskFilter( + return canvasKit.MaskFilter.MakeBlur( toSkBlurStyle(_blurStyle), _sigma, true, - ); + )!; } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart index bc736dd8a6f6c..529d74908e271 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart @@ -2,14 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'canvas.dart'; +import 'painting.dart'; +import 'path.dart'; /// A virtual canvas that applies operations to multiple canvases at once. class CkNWayCanvas { - final List _canvases = []; + final List _canvases = []; - void addCanvas(CkCanvas? canvas) { + void addCanvas(CkCanvas canvas) { _canvases.add(canvas); } @@ -17,71 +22,79 @@ class CkNWayCanvas { int save() { int saveCount = 0; for (int i = 0; i < _canvases.length; i++) { - saveCount = _canvases[i]!.save(); + saveCount = _canvases[i].save(); } return saveCount; } /// Calls [saveLayer] on all canvases. - void saveLayer(ui.Rect bounds, ui.Paint? paint) { + void saveLayer(ui.Rect bounds, CkPaint? paint) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.saveLayer(bounds, paint as CkPaint); + _canvases[i].saveLayer(bounds, paint); } } /// Calls [saveLayerWithFilter] on all canvases. - void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, + [CkPaint? paint]) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.saveLayerWithFilter(bounds, filter); + _canvases[i].saveLayerWithFilter(bounds, filter, paint); } } /// Calls [restore] on all canvases. void restore() { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.restore(); + _canvases[i].restore(); } } /// Calls [restoreToCount] on all canvases. void restoreToCount(int count) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.restoreToCount(count); + _canvases[i].restoreToCount(count); } } /// Calls [translate] on all canvases. void translate(double dx, double dy) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.translate(dx, dy); + _canvases[i].translate(dx, dy); } } /// Calls [transform] on all canvases. void transform(Float32List matrix) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.transform(matrix); + _canvases[i].transform(matrix); + } + } + + /// Calls [clear] on all canvases. + void clear(ui.Color color) { + for (int i = 0; i < _canvases.length; i++) { + _canvases[i].clear(color); } } /// Calls [clipPath] on all canvases. - void clipPath(ui.Path? path, bool doAntiAlias) { + void clipPath(CkPath path, bool doAntiAlias) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.clipPath(path!, doAntiAlias); + _canvases[i].clipPath(path, doAntiAlias); } } /// Calls [clipRect] on all canvases. void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.clipRect(rect, clipOp, doAntiAlias); + _canvases[i].clipRect(rect, clipOp, doAntiAlias); } } /// Calls [clipRRect] on all canvases. void clipRRect(ui.RRect rrect, bool doAntiAlias) { for (int i = 0; i < _canvases.length; i++) { - _canvases[i]!.clipRRect(rrect, doAntiAlias); + _canvases[i].clipRRect(rrect, doAntiAlias); } } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/painting.dart b/lib/web_ui/lib/src/engine/canvaskit/painting.dart index 550ff12351e40..4e7d8e203fc4d 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/painting.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/painting.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import 'canvaskit_api.dart'; +import 'color_filter.dart'; +import 'image_filter.dart'; +import 'mask_filter.dart'; +import 'shader.dart'; +import 'skia_object_cache.dart'; /// The implementation of [ui.Paint] used by the CanvasKit backend. /// @@ -124,7 +130,7 @@ class CkPaint extends ManagedSkiaObject implements ui.Paint { return; } _shader = value as CkShader?; - skiaObject.setShader(_shader?.skiaObject); + skiaObject.setShader(_shader?.withQuality(_filterQuality)); } CkShader? _shader; @@ -138,10 +144,16 @@ class CkPaint extends ManagedSkiaObject implements ui.Paint { } _maskFilter = value; if (value != null) { - _ckMaskFilter = CkMaskFilter.blur( - value.webOnlyBlurStyle, - value.webOnlySigma, - ); + // CanvasKit returns `null` if the sigma is `0` or infinite. + if (!(value.webOnlySigma.isFinite && value.webOnlySigma > 0)) { + // Don't create a [CkMaskFilter]. + _ckMaskFilter = null; + } else { + _ckMaskFilter = CkMaskFilter.blur( + value.webOnlyBlurStyle, + value.webOnlySigma, + ); + } } else { _ckMaskFilter = null; } @@ -159,26 +171,28 @@ class CkPaint extends ManagedSkiaObject implements ui.Paint { return; } _filterQuality = value; - skiaObject.setFilterQuality(toSkFilterQuality(value)); + skiaObject.setShader(_shader?.withQuality(value)); } ui.FilterQuality _filterQuality = ui.FilterQuality.none; @override - ui.ColorFilter? get colorFilter => _colorFilter; + ui.ColorFilter? get colorFilter => _managedColorFilter?.colorFilter; @override set colorFilter(ui.ColorFilter? value) { - if (_colorFilter == value) { + if (colorFilter == value) { return; } - final EngineColorFilter? engineValue = value as EngineColorFilter?; - _colorFilter = engineValue; - _ckColorFilter = engineValue?._toCkColorFilter(); - skiaObject.setColorFilter(_ckColorFilter?.skiaObject); + + if (value == null) { + _managedColorFilter = null; + } else { + _managedColorFilter = ManagedSkColorFilter(value as CkColorFilter); + } + skiaObject.setColorFilter(_managedColorFilter?.skiaObject); } - EngineColorFilter? _colorFilter; - CkColorFilter? _ckColorFilter; + ManagedSkColorFilter? _managedColorFilter; @override double get strokeMiterLimit => _strokeMiterLimit; @@ -200,11 +214,14 @@ class CkPaint extends ManagedSkiaObject implements ui.Paint { if (_imageFilter == value) { return; } - _imageFilter = value as CkImageFilter?; - skiaObject.setImageFilter(_imageFilter?.skiaObject); + + _imageFilter = value as CkManagedSkImageFilterConvertible?; + _managedImageFilter = _imageFilter?.imageFilter; + skiaObject.setImageFilter(_managedImageFilter?.skiaObject); } - CkImageFilter? _imageFilter; + CkManagedSkImageFilterConvertible? _imageFilter; + ManagedSkiaObject? _managedImageFilter; @override SkPaint createDefault() { @@ -222,11 +239,13 @@ class CkPaint extends ManagedSkiaObject implements ui.Paint { paint.setStrokeWidth(_strokeWidth); paint.setAntiAlias(_isAntiAlias); paint.setColorInt(_color.value); - paint.setShader(_shader?.skiaObject); + paint.setShader(_shader?.withQuality(_filterQuality)); paint.setMaskFilter(_ckMaskFilter?.skiaObject); - paint.setColorFilter(_ckColorFilter?.skiaObject); - paint.setImageFilter(_imageFilter?.skiaObject); - paint.setFilterQuality(toSkFilterQuality(_filterQuality)); + paint.setColorFilter(_managedColorFilter?.skiaObject); + paint.setImageFilter(_managedImageFilter?.skiaObject); + paint.setStrokeCap(toSkStrokeCap(_strokeCap)); + paint.setStrokeJoin(toSkStrokeJoin(_strokeJoin)); + paint.setStrokeMiter(_strokeMiterLimit); return paint; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/path.dart b/lib/web_ui/lib/src/engine/canvaskit/path.dart index 1302736587424..a9ed3864198af 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path.dart @@ -2,25 +2,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../vector_math.dart'; +import 'canvaskit_api.dart'; +import 'path_metrics.dart'; +import 'skia_object_cache.dart'; /// An implementation of [ui.Path] which is backed by an `SkPath`. /// /// The `SkPath` is required for `CkCanvas` methods which take a path. -class CkPath implements ui.Path { - final SkPath _skPath; - - CkPath() : _skPath = SkPath(), _fillType = ui.PathFillType.nonZero { - _skPath.setFillType(toSkFillType(_fillType)); - } +class CkPath extends ManagedSkiaObject implements ui.Path { + CkPath() : _fillType = ui.PathFillType.nonZero; - CkPath.from(CkPath other) : _skPath = SkPath(other._skPath), _fillType = other.fillType { - _skPath.setFillType(toSkFillType(_fillType)); + CkPath.from(CkPath other) + : _fillType = other.fillType, + super(other.skiaObject.copy()) { + skiaObject.setFillType(toSkFillType(_fillType)); } - CkPath._fromSkPath(SkPath skPath, this._fillType) : _skPath = skPath { - _skPath.setFillType(toSkFillType(_fillType)); + CkPath.fromSkPath(SkPath skPath, this._fillType) : super(skPath) { + skiaObject.setFillType(toSkFillType(_fillType)); } ui.PathFillType _fillType; @@ -34,13 +39,13 @@ class CkPath implements ui.Path { return; } _fillType = newFillType; - _skPath.setFillType(toSkFillType(newFillType)); + skiaObject.setFillType(toSkFillType(newFillType)); } @override void addArc(ui.Rect oval, double startAngle, double sweepAngle) { const double toDegrees = 180.0 / math.pi; - _skPath.addArc( + skiaObject.addArc( toSkRect(oval), startAngle * toDegrees, sweepAngle * toDegrees, @@ -49,7 +54,7 @@ class CkPath implements ui.Path { @override void addOval(ui.Rect oval) { - _skPath.addOval(toSkRect(oval), false, 1); + skiaObject.addOval(toSkRect(oval), false, 1); } @override @@ -64,8 +69,8 @@ class CkPath implements ui.Path { skMatrix[5] += offset.dy; } final CkPath otherPath = path as CkPath; - _skPath.addPath( - otherPath._skPath, + skiaObject.addPath( + otherPath.skiaObject, skMatrix[0], skMatrix[1], skMatrix[2], @@ -83,40 +88,28 @@ class CkPath implements ui.Path { void addPolygon(List points, bool close) { assert(points != null); // ignore: unnecessary_null_comparison final SkFloat32List encodedPoints = toMallocedSkPoints(points); - _skPath.addPoly(encodedPoints.toTypedArray(), close); + skiaObject.addPoly(encodedPoints.toTypedArray(), close); freeFloat32List(encodedPoints); } @override void addRRect(ui.RRect rrect) { - final SkFloat32List skRadii = mallocFloat32List(8); - final Float32List radii = skRadii.toTypedArray(); - radii[0] = rrect.tlRadiusX; - radii[1] = rrect.tlRadiusY; - radii[2] = rrect.trRadiusX; - radii[3] = rrect.trRadiusY; - radii[4] = rrect.brRadiusX; - radii[5] = rrect.brRadiusY; - radii[6] = rrect.blRadiusX; - radii[7] = rrect.blRadiusY; - _skPath.addRoundRect( - toOuterSkRect(rrect), - radii, + skiaObject.addRRect( + toSkRRect(rrect), false, ); - freeFloat32List(skRadii); } @override void addRect(ui.Rect rect) { - _skPath.addRect(toSkRect(rect)); + skiaObject.addRect(toSkRect(rect)); } @override void arcTo( ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { const double toDegrees = 180.0 / math.pi; - _skPath.arcToOval( + skiaObject.arcToOval( toSkRect(rect), startAngle * toDegrees, sweepAngle * toDegrees, @@ -130,7 +123,7 @@ class CkPath implements ui.Path { double rotation = 0.0, bool largeArc = false, bool clockwise = true}) { - _skPath.arcToRotated( + skiaObject.arcToRotated( radius.x, radius.y, rotation, @@ -143,7 +136,7 @@ class CkPath implements ui.Path { @override void close() { - _skPath.close(); + skiaObject.close(); } @override @@ -153,18 +146,18 @@ class CkPath implements ui.Path { @override void conicTo(double x1, double y1, double x2, double y2, double w) { - _skPath.conicTo(x1, y1, x2, y2, w); + skiaObject.conicTo(x1, y1, x2, y2, w); } @override bool contains(ui.Offset point) { - return _skPath.contains(point.dx, point.dy); + return skiaObject.contains(point.dx, point.dy); } @override void cubicTo( double x1, double y1, double x2, double y2, double x3, double y3) { - _skPath.cubicTo(x1, y1, x2, y2, x3, y3); + skiaObject.cubicTo(x1, y1, x2, y2, x3, y3); } @override @@ -179,8 +172,8 @@ class CkPath implements ui.Path { skMatrix[5] += offset.dy; } final CkPath otherPath = path as CkPath; - _skPath.addPath( - otherPath._skPath, + skiaObject.addPath( + otherPath.skiaObject, skMatrix[0], skMatrix[1], skMatrix[2], @@ -195,21 +188,21 @@ class CkPath implements ui.Path { } @override - ui.Rect getBounds() => _skPath.getBounds().toRect(); + ui.Rect getBounds() => fromSkRect(skiaObject.getBounds()); @override void lineTo(double x, double y) { - _skPath.lineTo(x, y); + skiaObject.lineTo(x, y); } @override void moveTo(double x, double y) { - _skPath.moveTo(x, y); + skiaObject.moveTo(x, y); } @override void quadraticBezierTo(double x1, double y1, double x2, double y2) { - _skPath.quadTo(x1, y1, x2, y2); + skiaObject.quadTo(x1, y1, x2, y2); } @override @@ -218,7 +211,7 @@ class CkPath implements ui.Path { double rotation = 0.0, bool largeArc = false, bool clockwise = true}) { - _skPath.rArcTo( + skiaObject.rArcTo( radius.x, radius.y, rotation, @@ -231,43 +224,45 @@ class CkPath implements ui.Path { @override void relativeConicTo(double x1, double y1, double x2, double y2, double w) { - _skPath.rConicTo(x1, y1, x2, y2, w); + skiaObject.rConicTo(x1, y1, x2, y2, w); } @override void relativeCubicTo( double x1, double y1, double x2, double y2, double x3, double y3) { - _skPath.rCubicTo(x1, y1, x2, y2, x3, y3); + skiaObject.rCubicTo(x1, y1, x2, y2, x3, y3); } @override void relativeLineTo(double dx, double dy) { - _skPath.rLineTo(dx, dy); + skiaObject.rLineTo(dx, dy); } @override void relativeMoveTo(double dx, double dy) { - _skPath.rMoveTo(dx, dy); + skiaObject.rMoveTo(dx, dy); } @override void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { - _skPath.rQuadTo(x1, y1, x2, y2); + skiaObject.rQuadTo(x1, y1, x2, y2); } @override void reset() { + // Only reset the local field. Skia will reset its internal state via + // SkPath.reset() below. _fillType = ui.PathFillType.nonZero; - _skPath.reset(); + skiaObject.reset(); } @override ui.Path shift(ui.Offset offset) { // Since CanvasKit does not expose `SkPath.offset`, create a copy of this // path and call `transform` on it. - final SkPath newPath = _skPath.copy(); + final SkPath newPath = skiaObject.copy(); newPath.transform(1.0, 0.0, offset.dx, 0.0, 1.0, offset.dy, 0.0, 0.0, 0.0); - return CkPath._fromSkPath(newPath, _fillType); + return CkPath.fromSkPath(newPath, _fillType); } static CkPath combine( @@ -277,17 +272,17 @@ class CkPath implements ui.Path { ) { final CkPath path1 = uiPath1 as CkPath; final CkPath path2 = uiPath2 as CkPath; - final SkPath newPath = canvasKit.MakePathFromOp( - path1._skPath, - path2._skPath, + final SkPath newPath = canvasKit.Path.MakeFromOp( + path1.skiaObject, + path2.skiaObject, toSkPathOp(operation), ); - return CkPath._fromSkPath(newPath, path1._fillType); + return CkPath.fromSkPath(newPath, path1._fillType); } @override ui.Path transform(Float64List matrix4) { - final SkPath newPath = _skPath.copy(); + final SkPath newPath = skiaObject.copy(); final Float32List m = toSkMatrixFromFloat64(matrix4); newPath.transform( m[0], @@ -300,15 +295,40 @@ class CkPath implements ui.Path { m[7], m[8], ); - return CkPath._fromSkPath(newPath, _fillType); + return CkPath.fromSkPath(newPath, _fillType); } String? toSvgString() { - return _skPath.toSVGString(); + return skiaObject.toSVGString(); } /// Return `true` if this path contains no segments. bool get isEmpty { - return _skPath.isEmpty(); + return skiaObject.isEmpty(); + } + + @override + bool get isResurrectionExpensive => true; + + @override + SkPath createDefault() { + final SkPath path = SkPath(); + path.setFillType(toSkFillType(_fillType)); + return path; + } + + List? _cachedCommands; + + @override + void delete() { + _cachedCommands = skiaObject.toCmds(); + rawSkiaObject?.delete(); + } + + @override + SkPath resurrect() { + final SkPath path = canvasKit.Path.MakeFromCmds(_cachedCommands!); + path.setFillType(toSkFillType(_fillType)); + return path; } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart b/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart index 77ea992396c23..c1d4288ed0ef0 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:collection'; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'canvaskit_api.dart'; +import 'path.dart'; +import 'skia_object_cache.dart'; class CkPathMetrics extends IterableBase implements ui.PathMetrics { @@ -14,20 +20,16 @@ class CkPathMetrics extends IterableBase /// The [CkPath.isEmpty] case is special-cased to avoid booting the WASM machinery just to find out there are no contours. @override - Iterator get iterator => _path.isEmpty ? const CkPathMetricIteratorEmpty._() : CkContourMeasureIter(_path, _forceClosed); + Iterator get iterator => _path.isEmpty + ? const CkPathMetricIteratorEmpty._() + : CkContourMeasureIter(this); } -class CkContourMeasureIter implements Iterator { - CkContourMeasureIter(CkPath path, bool forceClosed) - : _skObject = SkContourMeasureIter( - path._skPath, - forceClosed, - 1, - ), - _fillType = path._fillType; +class CkContourMeasureIter extends ManagedSkiaObject + implements Iterator { + CkContourMeasureIter(this._metrics); - final SkContourMeasureIter _skObject; - final ui.PathFillType _fillType; + final CkPathMetrics _metrics; /// A monotonically increasing counter used to generate [ui.PathMetric.contourIndex]. /// @@ -39,47 +41,79 @@ class CkContourMeasureIter implements Iterator { final ui.PathMetric? currentMetric = _current; if (currentMetric == null) { throw RangeError( - 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' - '- The iteration has not started yet. If so, call "moveNext" to start iteration.' - '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".' - ); + 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' + '- The iteration has not started yet. If so, call "moveNext" to start iteration.' + '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".'); } return currentMetric; } + CkContourMeasure? _current; @override bool moveNext() { - final SkContourMeasure? skContourMeasure = _skObject.next(); + final SkContourMeasure? skContourMeasure = skiaObject.next(); if (skContourMeasure == null) { _current = null; return false; } - _current = CkContourMeasure(_contourIndexCounter, skContourMeasure, _fillType); + _current = + CkContourMeasure(_metrics, skContourMeasure, _contourIndexCounter); _contourIndexCounter += 1; return true; } + + @override + SkContourMeasureIter createDefault() { + return SkContourMeasureIter( + _metrics._path.skiaObject, + _metrics._forceClosed, + 1.0, + ); + } + + @override + SkContourMeasureIter resurrect() { + final SkContourMeasureIter iterator = createDefault(); + + // When resurrecting we must advance the iterator to the last known + // position. + for (int i = 0; i < _contourIndexCounter; i++) { + iterator.next(); + } + + return iterator; + } + + @override + void delete() { + rawSkiaObject?.delete(); + } } -class CkContourMeasure implements ui.PathMetric { - CkContourMeasure(this.contourIndex, this._skObject, this._fillType); +class CkContourMeasure extends ManagedSkiaObject + implements ui.PathMetric { + CkContourMeasure(this._metrics, SkContourMeasure jsObject, this.contourIndex) + : super(jsObject); - final SkContourMeasure _skObject; - final ui.PathFillType _fillType; + /// The path metrics used to create this measure. + /// + /// This is used to resurrect the object if it is deleted prematurely. + final CkPathMetrics _metrics; @override final int contourIndex; @override ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { - final SkPath skPath = _skObject.getSegment(start, end, startWithMoveTo); - return CkPath._fromSkPath(skPath, _fillType); + final SkPath skPath = skiaObject.getSegment(start, end, startWithMoveTo); + return CkPath.fromSkPath(skPath, _metrics._path.fillType); } @override ui.Tangent getTangentForOffset(double distance) { - final Float32List posTan = _skObject.getPosTan(distance); + final Float32List posTan = skiaObject.getPosTan(distance); return ui.Tangent( ui.Offset(posTan[0], posTan[1]), ui.Offset(posTan[2], posTan[3]), @@ -88,12 +122,46 @@ class CkContourMeasure implements ui.PathMetric { @override bool get isClosed { - return _skObject.isClosed(); + return skiaObject.isClosed(); } @override double get length { - return _skObject.length(); + return skiaObject.length(); + } + + @override + SkContourMeasure createDefault() { + // This method must never be called. The default instance comes from the + // iterator's [SkContourMeasureIter.next] method initialized by the + // constructor. + throw StateError('Unreachable code'); + } + + @override + SkContourMeasure resurrect() { + final CkContourMeasureIter iterator = + _metrics.iterator as CkContourMeasureIter; + final SkContourMeasureIter skIterator = iterator.skiaObject; + + // When resurrecting we must advance the iterator to the last known + // position. + for (int i = 0; i < contourIndex; i++) { + skIterator.next(); + } + + final SkContourMeasure? result = skIterator.next(); + + if (result == null) { + throw StateError('Failed to resurrect SkContourMeasure'); + } + + return result; + } + + @override + void delete() { + rawSkiaObject?.delete(); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/picture.dart b/lib/web_ui/lib/src/engine/canvaskit/picture.dart index 2a0d292ebd45b..2591f7f303dfa 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/picture.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/picture.dart @@ -2,40 +2,96 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; -class CkPicture implements ui.Picture { - final SkiaObject skiaObject; +import '../profiler.dart'; +import 'canvas.dart'; +import 'canvaskit_api.dart'; +import 'image.dart'; +import 'skia_object_cache.dart'; + +/// Implements [ui.Picture] on top of [SkPicture]. +/// +/// Unlike most other [ManagedSkiaObject] implementations, instances of this +/// class may have their Skia counterparts deleted before finalization registry +/// or [SkiaObjectCache] decide to delete it. +class CkPicture extends ManagedSkiaObject implements ui.Picture { final ui.Rect? cullRect; + final CkPictureSnapshot? _snapshot; + + CkPicture(SkPicture picture, this.cullRect, this._snapshot) : + assert( + browserSupportsFinalizationRegistry && _snapshot == null || + _snapshot != null, + 'If the browser does not support FinalizationRegistry (WeakRef), then we must have a picture snapshot to be able to resurrect it.', + ), super(picture); - CkPicture(SkPicture picture, this.cullRect) - : skiaObject = SkPictureSkiaObject(picture); @override int get approximateBytesUsed => 0; + /// Whether the picture has been disposed of. + /// + /// This is indended to be used in tests and assertions only. + bool get debugIsDisposed => _isDisposed; + + /// This is set to true when [dispose] is called and is never reset back to + /// false. + /// + /// This extra flag is necessary on top of [rawSkiaObject] because + /// [rawSkiaObject] being null does not indicate permanent deletion. See + /// similar flag [SkiaObjectBox.isDeletedPermanently]. + bool _isDisposed = false; + @override void dispose() { - skiaObject.delete(); + assert(!_isDisposed, 'Object has been disposed.'); + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter('Picture disposed'); + } + _isDisposed = true; + _snapshot?.dispose(); + + // Emulate what SkiaObjectCache does. + rawSkiaObject?.delete(); + rawSkiaObject = null; } @override Future toImage(int width, int height) async { + assert(!_isDisposed); final SkSurface skSurface = canvasKit.MakeSurface(width, height); final SkCanvas skCanvas = skSurface.getCanvas(); - skCanvas.drawPicture(skiaObject.skiaObject); + skCanvas.drawPicture(skiaObject); final SkImage skImage = skSurface.makeImageSnapshot(); skSurface.dispose(); return CkImage(skImage); } -} -class SkPictureSkiaObject extends OneShotSkiaObject { - SkPictureSkiaObject(SkPicture picture) : super(picture); + @override + bool get isResurrectionExpensive => true; + + @override + SkPicture createDefault() { + // The default object is supplied in the constructor. + throw StateError('Unreachable code'); + } + + @override + SkPicture resurrect() { + // If a picture has been explicitly disposed of, it can no longer be + // resurrected. An attempt to resurrect after the framework told the + // engine to dispose of the picture likely indicates a bug in the engine. + assert(!_isDisposed); + return _snapshot!.toPicture(); + } @override void delete() { - rawSkiaObject.delete(); + // This method may be called after [dispose], in which case there's nothing + // left to do. The Skia object is deleted permanently. + if (!_isDisposed) { + rawSkiaObject?.delete(); + } } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart b/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart index fa6793d0f2297..1b63de0693384 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'canvas.dart'; +import 'canvaskit_api.dart'; +import 'picture.dart'; class CkPictureRecorder implements ui.PictureRecorder { ui.Rect? _cullRect; @@ -13,15 +18,17 @@ class CkPictureRecorder implements ui.PictureRecorder { CkCanvas beginRecording(ui.Rect bounds) { _cullRect = bounds; final SkPictureRecorder recorder = _skRecorder = SkPictureRecorder(); - final SkRect skRect = toSkRect(bounds); + final Float32List skRect = toSkRect(bounds); final SkCanvas skCanvas = recorder.beginRecording(skRect); - return _recordingCanvas = CkCanvas(skCanvas); + return _recordingCanvas = browserSupportsFinalizationRegistry + ? CkCanvas(skCanvas) + : RecordingCkCanvas(skCanvas, bounds); } CkCanvas? get recordingCanvas => _recordingCanvas; @override - ui.Picture endRecording() { + CkPicture endRecording() { final SkPictureRecorder? recorder = _skRecorder; if (recorder == null) { @@ -31,7 +38,7 @@ class CkPictureRecorder implements ui.PictureRecorder { final SkPicture skPicture = recorder.finishRecordingAsPicture(); recorder.delete(); _skRecorder = null; - return CkPicture(skPicture, _cullRect); + return CkPicture(skPicture, _cullRect, _recordingCanvas!.pictureSnapshot); } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart b/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart index 543b2d56bcc28..dea8d0ba14fab 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; +// TODO(hterkelsen): Delete this once the slots change lands? class PlatformMessage { final String channel; final ByteData data; diff --git a/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart b/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart index 85ebf7c49ec7d..bfb96257e8e72 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../vector_math.dart'; +import 'canvas.dart'; /// A cache of [Picture]s that have already been rasterized. /// diff --git a/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart b/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart index 76b9e69e53afe..b8b420bb829a6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart @@ -2,19 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import '../frame_reference.dart'; +import 'canvas.dart'; +import 'embedded_views.dart'; +import 'layer_tree.dart'; +import 'surface.dart'; +import 'surface_factory.dart'; /// A class that can rasterize [LayerTree]s into a given [Surface]. class Rasterizer { - final Surface surface; final CompositorContext context = CompositorContext(); final List _postFrameCallbacks = []; - Rasterizer(this.surface); - void setSkiaResourceCacheMaxBytes(int bytes) => - surface.setSkiaResourceCacheMaxBytes(bytes); + SurfaceFactory.instance.baseSurface.setSkiaResourceCacheMaxBytes(bytes); /// Creates a new frame from this rasterizer's surface, draws the given /// [LayerTree] into it, and then submits the frame. @@ -25,16 +29,17 @@ class Rasterizer { return; } - final SurfaceFrame frame = surface.acquireFrame(layerTree.frameSize); - surface.viewEmbedder.frameSize = layerTree.frameSize; + final SurfaceFrame frame = + SurfaceFactory.instance.baseSurface.acquireFrame(layerTree.frameSize); + HtmlViewEmbedder.instance.frameSize = layerTree.frameSize; final CkCanvas canvas = frame.skiaCanvas; final Frame compositorFrame = - context.acquireFrame(canvas, surface.viewEmbedder); + context.acquireFrame(canvas, HtmlViewEmbedder.instance); compositorFrame.raster(layerTree, ignoreRasterCache: true); - surface.addToScene(); + SurfaceFactory.instance.baseSurface.addToScene(); frame.submit(); - surface.viewEmbedder.submitFrame(); + HtmlViewEmbedder.instance.submitFrame(); } finally { _runPostFrameCallbacks(); } @@ -49,9 +54,15 @@ class Rasterizer { final ui.VoidCallback callback = _postFrameCallbacks[i]; callback(); } - for (int i = 0; i < _frameReferences.length; i++) { - _frameReferences[i].value = null; + for (int i = 0; i < frameReferences.length; i++) { + frameReferences[i].value = null; } - _frameReferences.clear(); + frameReferences.clear(); + } + + /// Forces the post-frame callbacks to run. Useful in tests. + @visibleForTesting + void debugRunPostFrameCallbacks() { + _runPostFrameCallbacks(); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/shader.dart b/lib/web_ui/lib/src/engine/canvaskit/shader.dart index 6b0074b609d4c..f8c25d4827cca 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/shader.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/shader.dart @@ -2,10 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; +import '../validators.dart'; +import 'canvaskit_api.dart'; +import 'image.dart'; +import 'initialization.dart'; +import 'skia_object_cache.dart'; + +abstract class CkShader extends ManagedSkiaObject + implements ui.Shader { + SkShader withQuality(ui.FilterQuality contextualQuality) => skiaObject; -abstract class CkShader extends ManagedSkiaObject implements ui.Shader { @override void delete() { rawSkiaObject?.delete(); @@ -15,14 +27,14 @@ abstract class CkShader extends ManagedSkiaObject implements ui.Shader class CkGradientSweep extends CkShader implements ui.Gradient { CkGradientSweep(this.center, this.colors, this.colorStops, this.tileMode, this.startAngle, this.endAngle, this.matrix4) - : assert(_offsetIsValid(center)), + : assert(offsetIsValid(center)), assert(colors != null), // ignore: unnecessary_null_comparison assert(tileMode != null), // ignore: unnecessary_null_comparison assert(startAngle != null), // ignore: unnecessary_null_comparison assert(endAngle != null), // ignore: unnecessary_null_comparison assert(startAngle < endAngle), - assert(matrix4 == null || _matrix4IsValid(matrix4)) { - _validateColorStops(colors, colorStops); + assert(matrix4 == null || matrix4IsValid(matrix4)) { + validateColorStops(colors, colorStops); } final ui.Offset center; @@ -35,16 +47,17 @@ class CkGradientSweep extends CkShader implements ui.Gradient { @override SkShader createDefault() { - return canvasKit.SkShader.MakeSweepGradient( + const double toDegrees = 180.0 / math.pi; + return canvasKit.Shader.MakeSweepGradient( center.dx, center.dy, - toSkFloatColorList(colors), + toFlatColors(colors), toSkColorStops(colorStops), toSkTileMode(tileMode), matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null, 0, - startAngle, - endAngle, + toDegrees * startAngle, + toDegrees * endAngle, ); } @@ -61,14 +74,15 @@ class CkGradientLinear extends CkShader implements ui.Gradient { this.colors, this.colorStops, this.tileMode, - Float64List? matrix, - ) : assert(_offsetIsValid(from)), - assert(_offsetIsValid(to)), + Float32List? matrix, + ) : assert(offsetIsValid(from)), + assert(offsetIsValid(to)), assert(colors != null), // ignore: unnecessary_null_comparison assert(tileMode != null), // ignore: unnecessary_null_comparison - this.matrix4 = matrix == null ? null : _FastMatrix64(matrix) { + matrix4 = matrix { if (assertionsEnabled) { - _validateColorStops(colors, colorStops); + assert(matrix4 == null || matrix4IsValid(matrix4!)); + validateColorStops(colors, colorStops); } } @@ -77,18 +91,19 @@ class CkGradientLinear extends CkShader implements ui.Gradient { final List colors; final List? colorStops; final ui.TileMode tileMode; - final _FastMatrix64? matrix4; + final Float32List? matrix4; @override SkShader createDefault() { - assert(experimentalUseSkia); + assert(useCanvasKit); - return canvasKit.SkShader.MakeLinearGradient( + return canvasKit.Shader.MakeLinearGradient( toSkPoint(from), toSkPoint(to), - toSkFloatColorList(colors), + toFlatColors(colors), toSkColorStops(colorStops), toSkTileMode(tileMode), + matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null, ); } @@ -109,12 +124,12 @@ class CkGradientRadial extends CkShader implements ui.Gradient { @override SkShader createDefault() { - assert(experimentalUseSkia); + assert(useCanvasKit); - return canvasKit.SkShader.MakeRadialGradient( + return canvasKit.Shader.MakeRadialGradient( toSkPoint(center), radius, - toSkFloatColorList(colors), + toFlatColors(colors), toSkColorStops(colorStops), toSkTileMode(tileMode), matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null, @@ -141,13 +156,13 @@ class CkGradientConical extends CkShader implements ui.Gradient { @override SkShader createDefault() { - assert(experimentalUseSkia); - return canvasKit.SkShader.MakeTwoPointConicalGradient( + assert(useCanvasKit); + return canvasKit.Shader.MakeTwoPointConicalGradient( toSkPoint(focal), focalRadius, toSkPoint(center), radius, - toSkFloatColorList(colors), + toFlatColors(colors), toSkColorStops(colorStops), toSkTileMode(tileMode), matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null, @@ -160,24 +175,50 @@ class CkGradientConical extends CkShader implements ui.Gradient { } class CkImageShader extends CkShader implements ui.ImageShader { - CkImageShader( - ui.Image image, this.tileModeX, this.tileModeY, this.matrix4) - : _skImage = image as CkImage; + CkImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4, + this.filterQuality) + : _image = image as CkImage; final ui.TileMode tileModeX; final ui.TileMode tileModeY; final Float64List matrix4; - final CkImage _skImage; + final ui.FilterQuality? filterQuality; + final CkImage _image; + ui.FilterQuality? _cachedQuality; @override - SkShader createDefault() => _skImage.skImage.makeShader( - toSkTileMode(tileModeX), - toSkTileMode(tileModeY), - toSkMatrixFromFloat64(matrix4), - ); + SkShader withQuality(ui.FilterQuality contextualQuality) { + final ui.FilterQuality quality = filterQuality ?? contextualQuality; + SkShader? shader = rawSkiaObject; + if (_cachedQuality != quality || shader == null) { + if (quality == ui.FilterQuality.high) { + shader = _image.skImage.makeShaderCubic( + toSkTileMode(tileModeX), + toSkTileMode(tileModeY), + 1.0 / 3.0, + 1.0 / 3.0, + toSkMatrixFromFloat64(matrix4), + ); + } else { + shader = _image.skImage.makeShaderOptions( + toSkTileMode(tileModeX), + toSkTileMode(tileModeY), + toSkFilterMode(quality), + toSkMipmapMode(quality), + toSkMatrixFromFloat64(matrix4), + ); + } + _cachedQuality = quality; + rawSkiaObject = shader; + } + return shader; + } @override - SkShader resurrect() => createDefault(); + SkShader createDefault() => withQuality(ui.FilterQuality.none); + + @override + SkShader resurrect() => withQuality(_cachedQuality ?? ui.FilterQuality.none); @override void delete() { diff --git a/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart b/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart index 36cdf7e7c5176..d05a262c6df57 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:collection'; + +import 'package:meta/meta.dart'; + +import '../../engine.dart' show EnginePlatformDispatcher, Instrumentation; +import '../util.dart'; +import 'canvaskit_api.dart'; /// A cache of Skia objects whose memory Flutter manages. /// @@ -25,17 +30,17 @@ class SkiaObjectCache { /// A doubly linked list of the objects in the cache. /// /// This makes it fast to move a recently used object to the front. - final DoubleLinkedQueue _itemQueue; + final DoubleLinkedQueue> _itemQueue; /// A map of objects to their associated node in the [_itemQueue]. /// /// This makes it fast to find the node in the queue when we need to /// move the object to the front of the queue. - final Map> _itemMap; + final Map, DoubleLinkedQueueEntry>> _itemMap; SkiaObjectCache(this.maximumSize) - : _itemQueue = DoubleLinkedQueue(), - _itemMap = >{}; + : _itemQueue = DoubleLinkedQueue>(), + _itemMap = , DoubleLinkedQueueEntry>>{}; /// The number of objects in the cache. int get length => _itemQueue.length; @@ -44,7 +49,7 @@ class SkiaObjectCache { /// /// This is only for testing. @visibleForTesting - bool debugContains(SkiaObject object) { + bool debugContains(SkiaObject object) { return _itemMap.containsKey(object); } @@ -53,7 +58,7 @@ class SkiaObjectCache { /// If adding [object] causes the total size of the cache to exceed /// [maximumSize], then the least recently used half of the cache /// will be deleted. - void add(SkiaObject object) { + void add(SkiaObject object) { _itemQueue.addFirst(object); _itemMap[object] = _itemQueue.firstEntry()!; @@ -63,8 +68,8 @@ class SkiaObjectCache { } /// Records that [object] was used in the most recent frame. - void markUsed(SkiaObject object) { - DoubleLinkedQueueEntry item = _itemMap[object]!; + void markUsed(SkiaObject object) { + final DoubleLinkedQueueEntry> item = _itemMap[object]!; item.remove(); _itemQueue.addFirst(object); _itemMap[object] = _itemQueue.firstEntry()!; @@ -74,7 +79,86 @@ class SkiaObjectCache { void resize() { final int itemsToDelete = maximumSize ~/ 2; for (int i = 0; i < itemsToDelete; i++) { - final SkiaObject oldObject = _itemQueue.removeLast(); + final SkiaObject oldObject = _itemQueue.removeLast(); + _itemMap.remove(oldObject); + oldObject.delete(); + oldObject.didDelete(); + } + } +} + +/// Like [SkiaObjectCache] but enforces the [maximumSize] of the cache +/// synchronously instead of waiting until a post-frame callback. +class SynchronousSkiaObjectCache { + /// This cache will never exceed this limit, even temporarily. + final int maximumSize; + + /// A doubly linked list of the objects in the cache. + /// + /// This makes it fast to move a recently used object to the front. + final DoubleLinkedQueue> _itemQueue; + + /// A map of objects to their associated node in the [_itemQueue]. + /// + /// This makes it fast to find the node in the queue when we need to + /// move the object to the front of the queue. + final Map, DoubleLinkedQueueEntry>> _itemMap; + + SynchronousSkiaObjectCache(this.maximumSize) + : _itemQueue = DoubleLinkedQueue>(), + _itemMap = , DoubleLinkedQueueEntry>>{}; + + /// The number of objects in the cache. + int get length => _itemQueue.length; + + /// Whether or not [object] is in the cache. + /// + /// This is only for testing. + @visibleForTesting + bool debugContains(SkiaObject object) { + return _itemMap.containsKey(object); + } + + /// Adds [object] to the cache. + /// + /// If adding [object] causes the total size of the cache to exceed + /// [maximumSize], then the least recently used objects are evicted and + /// deleted. + void add(SkiaObject object) { + assert( + !_itemMap.containsKey(object), + 'Cannot add object. Object is already in the cache: $object', + ); + _itemQueue.addFirst(object); + _itemMap[object] = _itemQueue.firstEntry()!; + _enforceCacheLimit(); + } + + /// Marks the [object] as most recently used. + /// + /// If [object] is in the cache returns true. If the object is not in + /// the cache, for example, because it was never added or because it was + /// evicted as a result of the app reaching the cache limit, returns false. + bool markUsed(SkiaObject object) { + final DoubleLinkedQueueEntry>? item = _itemMap[object]; + + if (item == null) { + return false; + } + + item.remove(); + _itemQueue.addFirst(object); + _itemMap[object] = _itemQueue.firstEntry()!; + return true; + } + + /// Ensures the cache does not exceed [maximumSize], evicting objects if + /// necessary. + /// + /// Calls `delete` and `didDelete` on objects evicted from the cache. + void _enforceCacheLimit() { + while (_itemQueue.length > maximumSize) { + final SkiaObject oldObject = _itemQueue.removeLast(); _itemMap.remove(oldObject); oldObject.delete(); oldObject.didDelete(); @@ -105,18 +189,24 @@ abstract class SkiaObject { /// collector to determine when it is safe to release the C++ object. /// /// In browsers that do not support weak references we pessimistically delete -/// the underlying C++ object before the Dart object is garbage-collected. The -/// current algorithm deletes objects at the end of every frame. This allows -/// reusing the C++ objects within the frame. If the object is used again after -/// is was deleted, we [resurrect] it based on the data available on the -/// JavaScript side. +/// the underlying C++ object before the Dart object is garbage-collected. +/// +/// If [isResurrectionExpensive] is false the object is deleted at the end of +/// the frame. If a deleted object is reused in a subsequent frame it is +/// resurrected by calling [resurrect]. This allows reusing the C++ objects +/// within the frame. +/// +/// If [isResurrectionExpensive] is true the object is put in a LRU cache. +/// Objects that are used least frequently are deleted from the cache when +/// the cache limit is reached. /// /// The lifecycle of a resurrectable C++ object is as follows: /// -/// - Create default: when instantiating a C++ object for a Dart object for the -/// first time, the C++ object is populated with default data (the defaults are -/// defined by Flutter; Skia defaults are corrected if necessary). The -/// default object is created by [createDefault]. +/// - Create: a managed object is created using a default instance that's +/// either supplied as a constructor argument, or obtained by calling +/// [createDefault]. The data in the new object is expected to contain +/// data matching Flutter's defaults (sometimes Skia defaults need to be +/// adjusted). /// - Zero or more cycles of delete + resurrect: when a Dart object is reused /// after its C++ object is deleted we create a new C++ object populated with /// data from the current state of the Dart object. This is done using the @@ -124,16 +214,28 @@ abstract class SkiaObject { /// - Final delete: if a Dart object is never reused, it is GC'd after its /// underlying C++ object is deleted. This is implemented by [SkiaObjects]. abstract class ManagedSkiaObject extends SkiaObject { - ManagedSkiaObject() { - final T defaultObject = createDefault(); + /// Creates a managed Skia object. + /// + /// If `instance` is null calls [createDefault] to create a Skia object to + /// manage. Otherwise, uses the provided instance. + /// + /// The provided instance must not be managed by another [ManagedSkiaObject], + /// as it may lead to undefined behavior. + ManagedSkiaObject([T? instance]) { + final T defaultObject = instance ?? createDefault(); rawSkiaObject = defaultObject; if (browserSupportsFinalizationRegistry) { // If FinalizationRegistry is supported we will only ever need the // default object, as we know precisely when to delete it. - skObjectFinalizationRegistry.register(this, defaultObject); + Collector.instance.register(this, defaultObject as SkDeletable); } else { // If FinalizationRegistry is _not_ supported we may need to delete // and resurrect the object multiple times before deleting it forever. + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${(defaultObject as SkDeletable).constructor.name} created', + ); + } if (isResurrectionExpensive) { SkiaObjects.manageExpensive(this); } else { @@ -148,6 +250,11 @@ abstract class ManagedSkiaObject extends SkiaObject { T _doResurrect() { assert(!browserSupportsFinalizationRegistry); final T skiaObject = resurrect(); + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${(skiaObject as SkDeletable).constructor.name} resurrected', + ); + } rawSkiaObject = skiaObject; if (isResurrectionExpensive) { SkiaObjects.manageExpensive(this); @@ -160,6 +267,18 @@ abstract class ManagedSkiaObject extends SkiaObject { @override void didDelete() { assert(!browserSupportsFinalizationRegistry); + + // Null indicates that the object has been manually disposed of. This + // happens for objects with manual lifecycles, such as Picture. + if (rawSkiaObject == null) { + return; + } + + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${(rawSkiaObject! as SkDeletable).constructor.name} deleted', + ); + } rawSkiaObject = null; } @@ -191,108 +310,214 @@ abstract class ManagedSkiaObject extends SkiaObject { bool get isResurrectionExpensive => false; } -// TODO(hterkelsen): [OneShotSkiaObject] is dangerous because it might delete -// the underlying Skia object while the associated Dart object is still in -// use. This issue discusses ways to address this: -// https://github.com/flutter/flutter/issues/60401 -/// A [SkiaObject] which is deleted once and cannot be used again. +/// Interface that classes wrapping [SkiaObjectBox] must implement. /// -/// In browsers that support weak references we use feedback from the garbage -/// collector to determine when it is safe to release the C++ object. Otherwise, -/// we use an LRU cache (see [SkiaObjects.manageOneShot]). -abstract class OneShotSkiaObject extends SkiaObject { - /// Returns the current skia object as is without attempting to - /// resurrect it. +/// Used to collect stack traces in debug mode. +abstract class StackTraceDebugger { + /// The stack trace pointing to code location that created or upreffed a + /// [SkiaObjectBox]. + StackTrace get debugStackTrace; +} + +/// A function that restores a Skia object that was temporarily deleted. +typedef Resurrector = T Function(); + +/// Uses reference counting to manage the lifecycle of a Skia object owned by a +/// wrapper object. +/// +/// The [ref] method can be used to increment the refcount to tell this box to +/// keep the underlying Skia object alive. +/// +/// The [unref] method can be used to decrement the refcount to tell this box +/// that a wrapper object no longer needs it. When the refcount drops to zero +/// the underlying Skia object is deleted permanently (see [isDeletedPermanently]). +/// +/// In addition to ref counting, this object is also managed by GC. In browsers +/// that support [SkFinalizationRegistry] the underlying Skia object is deleted +/// permanently when no JavaScript objects have references to this box. In +/// browsers that do not support [SkFinalizationRegistry] the underlying Skia +/// object may undergo several cycles of temporary deletions and resurrections +/// prior to being deleted permanently. A temporary deletion may effectively +/// be permanent if this object is garbage collected. This is safe because a +/// temporarily deleted object has no C++ resources to collect. +class SkiaObjectBox + extends SkiaObject { + /// Creates an object box that's memory-managed using [SkFinalizationRegistry]. /// - /// If the returned value is `null`, the corresponding C++ object has - /// been deleted. + /// This constructor must only be used if [browserSupportsFinalizationRegistry] is true. + SkiaObjectBox(R debugReferrer, T initialValue) : + assert(browserSupportsFinalizationRegistry), _resurrector = null { + _initialize(debugReferrer, initialValue); + Collector.instance.register(this, _skDeletable!); + } + + /// Creates an object box that's memory-managed using a [Resurrector]. /// - /// Use this field instead of the [skiaObject] getter when implementing - /// the [delete] method. - T rawSkiaObject; + /// This constructor must only be used if [browserSupportsFinalizationRegistry] is false. + SkiaObjectBox.resurrectable( + R debugReferrer, T initialValue, this._resurrector) : + assert(!browserSupportsFinalizationRegistry) { + _initialize(debugReferrer, initialValue); + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${_skDeletable?.constructor.name} created', + ); + } + SkiaObjects.manageExpensive(this); + } + + void _initialize(R debugReferrer, T initialValue) { + _update(initialValue); + if (assertionsEnabled) { + debugReferrers.add(debugReferrer); + } + assert(refCount == debugReferrers.length); + } - bool _isDeleted = false; + /// The number of objects sharing references to this box. + /// + /// When this count reaches zero, the underlying [skiaObject] is scheduled + /// for deletion. + int get refCount => _refCount; + int _refCount = 1; - OneShotSkiaObject(T skObject) : this.rawSkiaObject = skObject { - if (browserSupportsFinalizationRegistry) { - skObjectFinalizationRegistry.register(this, skObject); - } else { - SkiaObjects.manageOneShot(this); + /// When assertions are enabled, stores all objects that share this box. + /// + /// The length of this list is always identical to [refCount]. + /// + /// This list can be used for debugging ref counting issues. + final Set debugReferrers = {}; + + /// If asserts are enabled, the [StackTrace]s representing when a reference + /// was created. + List debugGetStackTraces() { + if (assertionsEnabled) { + return debugReferrers + .map((R referrer) => referrer.debugStackTrace) + .toList(); } + throw UnsupportedError(''); + } + + /// The Skia object whose lifecycle is being managed. + /// + /// Do not store this value outside this box. It is memory-managed by + /// [SkiaObjectBox]. Storing it may result in use-after-free bugs. + T? rawSkiaObject; + SkDeletable? _skDeletable; + Resurrector? _resurrector; + + void _update(T? newSkiaObject) { + rawSkiaObject = newSkiaObject; + _skDeletable = newSkiaObject as SkDeletable?; } @override - T get skiaObject { - if (browserSupportsFinalizationRegistry) { - return rawSkiaObject; - } - if (_isDeleted) { - throw StateError('Attempting to use a Skia object that has been freed.'); + T get skiaObject => rawSkiaObject ?? _doResurrect(); + + T _doResurrect() { + assert(!browserSupportsFinalizationRegistry); + assert(_resurrector != null); + assert(!_isDeletedPermanently, 'Cannot use deleted object.'); + _update(_resurrector!()); + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${_skDeletable?.constructor.name} resurrected', + ); } - SkiaObjects.oneShotCache.markUsed(this); - return rawSkiaObject; + SkiaObjects.manageExpensive(this); + return skiaObject; } @override - void didDelete() { - _isDeleted = true; + void delete() { + _skDeletable?.delete(); } -} -/// Manages the lifecycle of a Skia object owned by a wrapper object. -/// -/// When the wrapper is garbage collected, deletes the corresponding -/// [skObject] (only in browsers that support weak references). -/// -/// The [delete] method can be used to eagerly delete the [skObject] -/// before the wrapper is garbage collected. -/// -/// The [delete] method may be called any number of times. The box -/// will only delete the object once. -class SkiaObjectBox { - SkiaObjectBox(Object wrapper, this.skObject) { - if (browserSupportsFinalizationRegistry) { - boxRegistry.register(wrapper, this); + @override + void didDelete() { + if (Instrumentation.enabled) { + Instrumentation.instance.incrementCounter( + '${_skDeletable?.constructor.name} deleted', + ); } + assert(!browserSupportsFinalizationRegistry); + _update(null); } - /// The Skia object whose lifecycle is being managed. - final SkDeletable skObject; + /// Whether this object has been deleted permanently. + /// + /// If this is true it will remain true forever, and the Skia object is no + /// longer resurrectable. + /// + /// See also [isDeletedTemporarily]. + bool get isDeletedPermanently => _isDeletedPermanently; + bool _isDeletedPermanently = false; - /// Whether this object has been deleted. - bool get isDeleted => _isDeleted; - bool _isDeleted = false; + /// Whether the underlying [rawSkiaObject] has been deleted, but it may still + /// be resurrected (see [SkiaObjectBox.resurrectable]). + bool get isDeletedTemporarily => + rawSkiaObject == null && !_isDeletedPermanently; - /// Deletes Skia objects when their wrappers are garbage collected. - static final SkObjectFinalizationRegistry boxRegistry = - SkObjectFinalizationRegistry(js.allowInterop((SkiaObjectBox box) { - box.delete(); - })); + /// Increases the reference count of this box because a new object began + /// sharing ownership of the underlying [skiaObject]. + /// + /// Clones must be [dispose]d when finished. + void ref(R debugReferrer) { + assert(!_isDeletedPermanently, + 'Cannot increment ref count on a deleted handle.'); + assert(_refCount > 0); + assert( + debugReferrers.add(debugReferrer), + 'Attempted to increment ref count by the same referrer more than once.', + ); + _refCount += 1; + assert(refCount == debugReferrers.length); + } - /// Deletes the [skObject]. + /// Decrements the reference count for the [skObject]. /// /// Does nothing if the object has already been deleted. - void delete() { - if (_isDeleted) { - return; + /// + /// If this causes the reference count to drop to zero, deletes the + /// [skObject]. + void unref(R debugReferrer) { + assert(!_isDeletedPermanently, + 'Attempted to unref an already deleted Skia object.'); + assert( + debugReferrers.remove(debugReferrer), + 'Attempted to decrement ref count by the same referrer more than once.', + ); + _refCount -= 1; + assert(refCount == debugReferrers.length); + if (_refCount == 0) { + // The object may be null because it was deleted temporarily, i.e. it was + // expecting the possibility of resurrection. + if (_skDeletable != null) { + if (browserSupportsFinalizationRegistry) { + Collector.instance.collect(_skDeletable!); + } else { + delete(); + didDelete(); + } + } + rawSkiaObject = null; + _skDeletable = null; + _resurrector = null; + _isDeletedPermanently = true; } - _isDeleted = true; - _skObjectDeleteQueue.add(skObject); - _skObjectCollector ??= _scheduleSkObjectCollection(); } } +// ignore: avoid_classes_with_only_static_members /// Singleton that manages the lifecycles of [SkiaObject] instances. class SkiaObjects { @visibleForTesting - static final List resurrectableObjects = - []; - - @visibleForTesting - static int maximumCacheSize = 8192; + static final List> resurrectableObjects = + >[]; @visibleForTesting - static final SkiaObjectCache oneShotCache = SkiaObjectCache(maximumCacheSize); + static int maximumCacheSize = 1024; @visibleForTesting static final SkiaObjectCache expensiveCache = @@ -308,32 +533,27 @@ class SkiaObjects { if (_addedCleanupCallback) { return; } - window.rasterizer!.addPostFrameCallback(postFrameCleanUp); + // This method is @visibleForTesting but we're getting a warning about + // using a @visibleForTesting member. + // ignore: invalid_use_of_visible_for_testing_member + EnginePlatformDispatcher.instance.rasterizer! + .addPostFrameCallback(postFrameCleanUp); _addedCleanupCallback = true; } /// Starts managing the lifecycle of resurrectable [object]. /// /// These can safely be deleted at any time. - static void manageResurrectable(ManagedSkiaObject object) { + static void manageResurrectable(ManagedSkiaObject object) { registerCleanupCallback(); resurrectableObjects.add(object); } - /// Starts managing the lifecycle of a one-shot [object]. - /// - /// We should avoid deleting these whenever we can, since we won't - /// be able to resurrect them. - static void manageOneShot(OneShotSkiaObject object) { - registerCleanupCallback(); - oneShotCache.add(object); - } - /// Starts managing the lifecycle of a resurrectable object that is expensive. /// /// Since it's expensive to resurrect, we shouldn't just delete it after every /// frame. Instead, add it to a cache and only delete it when the cache fills. - static void manageExpensive(ManagedSkiaObject object) { + static void manageExpensive(SkiaObject object) { registerCleanupCallback(); expensiveCache.add(object); } @@ -354,7 +574,7 @@ class SkiaObjects { } for (int i = 0; i < resurrectableObjects.length; i++) { - final SkiaObject object = resurrectableObjects[i]; + final SkiaObject object = resurrectableObjects[i]; object.delete(); object.didDelete(); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface.dart b/lib/web_ui/lib/src/engine/canvaskit/surface.dart index 17aac05770ecd..4713bdfaad7a5 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/surface.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/surface.dart @@ -2,8 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import '../platform_dispatcher.dart'; +import '../util.dart'; +import '../window.dart'; +import 'canvas.dart'; +import 'canvaskit_api.dart'; +import 'initialization.dart'; +import 'surface_factory.dart'; +import 'util.dart'; typedef SubmitCallback = bool Function(SurfaceFrame, CkCanvas); @@ -35,13 +46,54 @@ class SurfaceFrame { /// successive frames if they are the same size. Otherwise, a new [CkSurface] is /// created. class Surface { - Surface(this.viewEmbedder); + Surface(); CkSurface? _surface; - html.Element? htmlElement; + + /// If true, forces a new WebGL context to be created, even if the window + /// size is the same. This is used to restore the UI after the browser tab + /// goes dormant and loses the GL context. + bool _forceNewContext = true; + bool get debugForceNewContext => _forceNewContext; + + bool _contextLost = false; + bool get debugContextLost => _contextLost; + + /// A cached copy of the most recently created `webglcontextlost` listener. + /// + /// We must cache this function because each time we access the tear-off it + /// creates a new object, meaning we won't be able to remove this listener + /// later. + void Function(html.Event)? _cachedContextLostListener; + + /// A cached copy of the most recently created `webglcontextrestored` + /// listener. + /// + /// We must cache this function because each time we access the tear-off it + /// creates a new object, meaning we won't be able to remove this listener + /// later. + void Function(html.Event)? _cachedContextRestoredListener; + SkGrContext? _grContext; + int? _glContext; int? _skiaCacheBytes; + /// The root HTML element for this surface. + /// + /// This element contains the canvas used to draw the UI. Unlike the canvas, + /// this element is permanent. It is never replaced or deleted, until this + /// surface is disposed of via [dispose]. + /// + /// Conversely, the canvas that lives inside this element can be swapped, for + /// example, when the screen size changes, or when the WebGL context is lost + /// due to the browser tab becoming dormant. + final html.Element htmlElement = html.Element.tag('flt-canvas-container'); + + /// The underlying `` element used for this surface. + html.CanvasElement? htmlCanvas; + int _pixelWidth = -1; + int _pixelHeight = -1; + /// Specify the GPU resource cache limits. void setSkiaResourceCacheMaxBytes(int bytes) { _skiaCacheBytes = bytes; @@ -49,27 +101,24 @@ class Surface { } void _syncCacheBytes() { - if(_skiaCacheBytes != null) { + if (_skiaCacheBytes != null) { _grContext?.setResourceCacheLimitBytes(_skiaCacheBytes!); } } bool _addedToScene = false; - /// The default view embedder. Coordinates embedding platform views and - /// overlaying subsequent draw operations on top. - final HtmlViewEmbedder viewEmbedder; - /// Acquire a frame of the given [size] containing a drawable canvas. /// /// The given [size] is in physical pixels. SurfaceFrame acquireFrame(ui.Size size) { - final CkSurface surface = acquireRenderSurface(size); + final CkSurface surface = createOrUpdateSurfaces(size); if (surface.context != null) { canvasKit.setCurrentContext(surface.context!); } - SubmitCallback submitCallback = + // ignore: prefer_function_declarations_over_variables + final SubmitCallback submitCallback = (SurfaceFrame surfaceFrame, CkCanvas canvas) { return _presentSurface(); }; @@ -77,118 +126,250 @@ class Surface { return SurfaceFrame(surface, submitCallback); } - CkSurface acquireRenderSurface(ui.Size size) { - _createOrUpdateSurfaces(size); - return _surface!; - } - void addToScene() { if (!_addedToScene) { - skiaSceneHost!.children.insert(0, htmlElement!); + skiaSceneHost!.children.insert(0, htmlElement); } _addedToScene = true; } - ui.Size? _currentSize; + ui.Size? _currentCanvasPhysicalSize; + ui.Size? _currentSurfaceSize; + double _currentDevicePixelRatio = -1; - void _createOrUpdateSurfaces(ui.Size size) { + /// Creates a and SkSurface for the given [size]. + CkSurface createOrUpdateSurfaces(ui.Size size) { if (size.isEmpty) { throw CanvasKitError('Cannot create surfaces of empty size.'); } - if (size == _currentSize) { + // Check if the window is the same size as before, and if so, don't allocate + // a new canvas as the previous canvas is big enough to fit everything. + final ui.Size? previousSurfaceSize = _currentSurfaceSize; + if (!_forceNewContext && + previousSurfaceSize != null && + size.width == previousSurfaceSize.width && + size.height == previousSurfaceSize.height) { // The existing surface is still reusable. - return; + if (window.devicePixelRatio != _currentDevicePixelRatio) { + _updateLogicalHtmlCanvasSize(); + } + return _surface!; } - _currentSize = size; - _surface?.dispose(); - _surface = null; - htmlElement?.remove(); - htmlElement = null; - _addedToScene = false; + _currentDevicePixelRatio = window.devicePixelRatio; + + // If the current canvas size is smaller than the requested size then create + // a new, larger, canvas. Then update the GR context so we can create a new + // SkSurface. + final ui.Size? previousCanvasSize = _currentCanvasPhysicalSize; + if (_forceNewContext || + previousCanvasSize == null || + size.width > previousCanvasSize.width || + size.height > previousCanvasSize.height) { + // Initialize a new, larger, canvas. If the size is growing, then make the + // new canvas larger than required to avoid many canvas creations. + final ui.Size newSize = previousCanvasSize == null ? size : size * 1.4; + + // Only resources from the current context can be disposed. + if (_glContext != null && _glContext != 0) { + canvasKit.setCurrentContext(_glContext!); + } + _surface?.dispose(); + _surface = null; + _addedToScene = false; + _grContext?.releaseResourcesAndAbandonContext(); + _grContext?.delete(); + _grContext = null; + + _createNewCanvas(newSize); + _currentCanvasPhysicalSize = newSize; + } + + _currentSurfaceSize = size; + _translateCanvas(); + return _surface = _createNewSurface(size); + } + + /// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device + /// pixels. + /// + /// The logical size of the canvas is not based on the size of the window + /// but on the size of the canvas, which, due to `ceil()` above, may not be + /// the same as the window. We do not round/floor/ceil the logical size as + /// CSS pixels can contain more than one physical pixel and therefore to + /// match the size of the window precisely we use the most precise floating + /// point value we can get. + void _updateLogicalHtmlCanvasSize() { + final double logicalWidth = _pixelWidth / window.devicePixelRatio; + final double logicalHeight = _pixelHeight / window.devicePixelRatio; + htmlCanvas!.style + ..width = '${logicalWidth}px' + ..height = '${logicalHeight}px'; + } + + /// Translate the canvas so the surface covers the visible portion of the + /// screen. + /// + /// The may be larger than the visible screen, but the SkSurface is + /// exactly the size of the visible screen. Unfortunately, the SkSurface is + /// drawn in the lower left corner of the , and without translation, + /// only the top left of the is visible. So we shift the canvas up so + /// the bottom left corner is visible. + void _translateCanvas() { + final int surfaceHeight = _currentSurfaceSize!.height.ceil(); + final double offset = + (_pixelHeight - surfaceHeight) / window.devicePixelRatio; + htmlCanvas!.style.transform = 'translate(0, -${offset}px)'; + } + + void _contextRestoredListener(html.Event event) { + assert( + _contextLost, + 'Received "webglcontextrestored" event but never received ' + 'a "webglcontextlost" event.'); + _contextLost = false; + // Force the framework to rerender the frame. + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); + event.stopPropagation(); + event.preventDefault(); + } - _surface = _wrapHtmlCanvas(size); + void _contextLostListener(html.Event event) { + assert(event.target == htmlCanvas, + 'Received a context lost event for a disposed canvas'); + final SurfaceFactory factory = SurfaceFactory.instance; + _contextLost = true; + if (factory.isLive(this)) { + _forceNewContext = true; + event.preventDefault(); + } else { + dispose(); + } } - CkSurface _wrapHtmlCanvas(ui.Size physicalSize) { + /// This function is expensive. + /// + /// It's better to reuse canvas if possible. + void _createNewCanvas(ui.Size physicalSize) { + // Clear the container, if it's not empty. We're going to create a new . + if (this.htmlCanvas != null) { + this.htmlCanvas!.removeEventListener( + 'webglcontextrestored', + _cachedContextRestoredListener, + false, + ); + this.htmlCanvas!.removeEventListener( + 'webglcontextlost', + _cachedContextLostListener, + false, + ); + this.htmlCanvas!.remove(); + _cachedContextRestoredListener = null; + _cachedContextLostListener = null; + } + // If `physicalSize` is not precise, use a slightly bigger canvas. This way // we ensure that the rendred picture covers the entire browser window. - final int pixelWidth = physicalSize.width.ceil(); - final int pixelHeight = physicalSize.height.ceil(); + _pixelWidth = physicalSize.width.ceil(); + _pixelHeight = physicalSize.height.ceil(); final html.CanvasElement htmlCanvas = html.CanvasElement( - width: pixelWidth, - height: pixelHeight, + width: _pixelWidth, + height: _pixelHeight, ); + this.htmlCanvas = htmlCanvas; + htmlCanvas.style.position = 'absolute'; + _updateLogicalHtmlCanvasSize(); + + // When the browser tab using WebGL goes dormant the browser and/or OS may + // decide to clear GPU resources to let other tabs/programs use the GPU. + // When this happens, the browser sends the "webglcontextlost" event as a + // notification. When we receive this notification we force a new context. + // + // See also: https://www.khronos.org/webgl/wiki/HandlingContextLost + _cachedContextRestoredListener = _contextRestoredListener; + _cachedContextLostListener = _contextLostListener; + htmlCanvas.addEventListener( + 'webglcontextlost', + _cachedContextLostListener, + false, + ); + htmlCanvas.addEventListener( + 'webglcontextrestored', + _cachedContextRestoredListener, + false, + ); + _forceNewContext = false; + _contextLost = false; - // The logical size of the canvas is not based on the size of the window - // but on the size of the canvas, which, due to `ceil()` above, may not be - // the same as the window. We do not round/floor/ceil the logical size as - // CSS pixels can contain more than one physical pixel and therefore to - // match the size of the window precisely we use the most precise floating - // point value we can get. - final double logicalWidth = pixelWidth / ui.window.devicePixelRatio; - final double logicalHeight = pixelHeight / ui.window.devicePixelRatio; - htmlCanvas.style - ..position = 'absolute' - ..width = '${logicalWidth}px' - ..height = '${logicalHeight}px'; - - htmlElement = htmlCanvas; - if (webGLVersion == -1 || canvasKitForceCpuOnly) { - return _makeSoftwareCanvasSurface(htmlCanvas); - } else { - // Try WebGL first. + if (webGLVersion != -1 && !canvasKitForceCpuOnly) { final int glContext = canvasKit.GetWebGLContext( htmlCanvas, SkWebGLContextOptions( // Default to no anti-aliasing. Paint commands can be explicitly // anti-aliased by setting their `Paint` object's `antialias` property. - anitalias: 0, + antialias: 0, majorVersion: webGLVersion, ), ); - if (glContext == 0) { - return _makeSoftwareCanvasSurface(htmlCanvas); - } - - _grContext = canvasKit.MakeGrContext(glContext); - - if (_grContext == null) { - throw CanvasKitError('Failed to initialize CanvasKit. CanvasKit.MakeGrContext returned null.'); + _glContext = glContext; + + if (_glContext != 0) { + _grContext = canvasKit.MakeGrContext(glContext); + if (_grContext == null) { + throw CanvasKitError('Failed to initialize CanvasKit. ' + 'CanvasKit.MakeGrContext returned null.'); + } + // Set the cache byte limit for this grContext, if not specified it will + // use CanvasKit's default. + _syncCacheBytes(); } + } - // Set the cache byte limit for this grContext, if not specified it will use - // CanvasKit's default. - _syncCacheBytes(); + htmlElement.append(htmlCanvas); + } - SkSurface? skSurface = canvasKit.MakeOnScreenGLSurface( + CkSurface _createNewSurface(ui.Size size) { + assert(htmlCanvas != null); + if (webGLVersion == -1) { + return _makeSoftwareCanvasSurface( + htmlCanvas!, 'WebGL support not detected'); + } else if (canvasKitForceCpuOnly) { + return _makeSoftwareCanvasSurface( + htmlCanvas!, 'CPU rendering forced by application'); + } else if (_glContext == 0) { + return _makeSoftwareCanvasSurface( + htmlCanvas!, 'Failed to initialize WebGL context'); + } else { + canvasKit.setCurrentContext(_glContext!); + final SkSurface? skSurface = canvasKit.MakeOnScreenGLSurface( _grContext!, - pixelWidth, - pixelHeight, + size.width.ceil(), + size.height.ceil(), SkColorSpaceSRGB, ); if (skSurface == null) { - return _makeSoftwareCanvasSurface(htmlCanvas); + return _makeSoftwareCanvasSurface( + htmlCanvas!, 'Failed to initialize WebGL surface'); } - return CkSurface(skSurface, _grContext, glContext); + return CkSurface(skSurface, _glContext); } } static bool _didWarnAboutWebGlInitializationFailure = false; - CkSurface _makeSoftwareCanvasSurface(html.CanvasElement htmlCanvas) { + CkSurface _makeSoftwareCanvasSurface( + html.CanvasElement htmlCanvas, String reason) { if (!_didWarnAboutWebGlInitializationFailure) { - html.window.console.warn('WARNING: failed to initialize WebGL. Falling back to CPU-only rendering.'); + printWarning('WARNING: Falling back to CPU-only rendering. $reason.'); _didWarnAboutWebGlInitializationFailure = true; } return CkSurface( canvasKit.MakeSWCanvasSurface(htmlCanvas), null, - null, ); } @@ -199,17 +380,28 @@ class Surface { _surface!.flush(); return true; } + + void dispose() { + htmlCanvas?.removeEventListener( + 'webglcontextlost', _cachedContextLostListener, false); + htmlCanvas?.removeEventListener( + 'webglcontextrestored', _cachedContextRestoredListener, false); + _cachedContextLostListener = null; + _cachedContextRestoredListener = null; + htmlElement.remove(); + _surface?.dispose(); + } } /// A Dart wrapper around Skia's CkSurface. class CkSurface { final SkSurface _surface; - final SkGrContext? _grContext; final int? _glContext; - CkSurface(this._surface, this._grContext, this._glContext); + CkSurface(this._surface, this._glContext); CkCanvas getCanvas() { + assert(!_isDisposed, 'Attempting to use the canvas of a disposed surface'); return CkCanvas(_surface.getCanvas()); } @@ -227,17 +419,7 @@ class CkSurface { if (_isDisposed) { return; } - // Only resources from the current context can be disposed. - if (_glContext != null) { - canvasKit.setCurrentContext(_glContext!); - } _surface.dispose(); - - // In CPU-only mode there's no graphics context. - if (_grContext != null) { - _grContext!.releaseResourcesAndAbandonContext(); - _grContext!.delete(); - } _isDisposed = true; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart b/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart new file mode 100644 index 0000000000000..70cf9bfc68c9d --- /dev/null +++ b/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart @@ -0,0 +1,153 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:meta/meta.dart'; + +import '../util.dart'; +import 'embedded_views.dart'; +import 'surface.dart'; + +/// Caches surfaces used to overlay platform views. +class SurfaceFactory { + /// The cache singleton. + static final SurfaceFactory instance = + SurfaceFactory(HtmlViewEmbedder.maximumOverlaySurfaces); + + SurfaceFactory(this.maximumSurfaces) + : assert(maximumSurfaces >= 2, + 'The maximum number of surfaces must be at least 2'); + + /// The base surface to paint on. This is the default surface which will be + /// painted to. If there are no platform views, then this surface will receive + /// all painting commands. + final Surface baseSurface = Surface(); + + /// The shared backup surface + final Surface backupSurface = Surface(); + + /// The maximum number of surfaces which can be live at once. + final int maximumSurfaces; + + /// Surfaces created by this factory which are currently in use. + final List _liveSurfaces = []; + + /// Surfaces created by this factory which are no longer in use. These can be + /// reused. + final List _cache = []; + + /// The number of surfaces which have been created by this factory. + int get _surfaceCount => _liveSurfaces.length + _cache.length + 2; + + /// The number of surfaces created by this factory. Used for testing. + @visibleForTesting + int get debugSurfaceCount => _surfaceCount; + + /// Returns the number of cached surfaces. + /// + /// Useful in tests. + int get debugCacheSize => _cache.length; + + /// Whether or not we have already emitted a warning about creating too many + /// surfaces. + bool _warnedAboutTooManySurfaces = false; + + /// Gets a [Surface] which is ready to paint to. + /// + /// If there are available surfaces in the cache, then this will return one of + /// them. If this factory hasn't yet created [maximumSurfaces] surfaces, then a + /// new one will be created. If this factory has already created [maximumSurfaces] + /// surfaces, then this will return a backup surface which will be returned by + /// all subsequent calls to [getSurface] until some surfaces have been + /// released with [releaseSurface]. + Surface getSurface() { + if (_cache.isNotEmpty) { + final Surface surface = _cache.removeLast(); + _liveSurfaces.add(surface); + return surface; + } else if (debugSurfaceCount < maximumSurfaces) { + final Surface surface = Surface(); + _liveSurfaces.add(surface); + return surface; + } else { + if (!_warnedAboutTooManySurfaces) { + _warnedAboutTooManySurfaces = true; + printWarning('Flutter was unable to create enough overlay surfaces. ' + 'This is usually caused by too many platform views being ' + 'displayed at once. ' + 'You may experience incorrect rendering.'); + } + return backupSurface; + } + } + + /// Releases all surfaces so they can be reused in the next frame. + /// + /// If a released surface is in the DOM, it is not removed. This allows the + /// engine to release the surfaces at the end of the frame so they are ready + /// to be used in the next frame, but still used for painting in the current + /// frame. + void releaseSurfaces() { + _cache.addAll(_liveSurfaces); + _liveSurfaces.clear(); + } + + /// Removes all surfaces except the base surface from the DOM. + /// + /// This is called at the beginning of the frame to prepare for painting into + /// the new surfaces. + void removeSurfacesFromDom() { + _cache.forEach(_removeFromDom); + _removeFromDom(backupSurface); + } + + // Removes [surface] from the DOM. + void _removeFromDom(Surface surface) { + surface.htmlElement.remove(); + } + + /// Signals that a surface is no longer being used. It can be reused. + void releaseSurface(Surface surface) { + assert(surface != baseSurface, 'Attempting to release the base surface'); + if (surface == backupSurface) { + // If it's the backup surface, just remove it from the DOM. + surface.htmlElement.remove(); + return; + } + assert( + _liveSurfaces.contains(surface), + 'Attempting to release a Surface which ' + 'was not created by this factory'); + surface.htmlElement.remove(); + _liveSurfaces.remove(surface); + _cache.add(surface); + } + + /// Returns [true] if [surface] is currently being used to paint content. + /// + /// The base surface and backup surface always count as live. + /// + /// If a surface is not live, then it must be in the cache and ready to be + /// reused. + bool isLive(Surface surface) { + if (surface == baseSurface || + surface == backupSurface || + _liveSurfaces.contains(surface)) { + return true; + } + assert(_cache.contains(surface)); + return false; + } + + /// Dispose all surfaces created by this factory. Used in tests. + void debugClear() { + for (final Surface surface in _cache) { + surface.dispose(); + } + for (final Surface surface in _liveSurfaces) { + surface.dispose(); + } + _liveSurfaces.clear(); + _cache.clear(); + } +} diff --git a/lib/web_ui/lib/src/engine/canvaskit/text.dart b/lib/web_ui/lib/src/engine/canvaskit/text.dart index 146a9f0baf4e4..afa865166de76 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -2,9 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; +import 'canvaskit_api.dart'; +import 'font_fallbacks.dart'; +import 'initialization.dart'; +import 'painting.dart'; +import 'skia_object_cache.dart'; +import 'util.dart'; + +@immutable class CkParagraphStyle implements ui.ParagraphStyle { CkParagraphStyle({ ui.TextAlign? textAlign, @@ -19,7 +28,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - }) : skParagraphStyle = toSkParagraphStyle( + }) : skParagraphStyle = toSkParagraphStyle( textAlign, textDirection, maxLines, @@ -29,19 +38,31 @@ class CkParagraphStyle implements ui.ParagraphStyle { textHeightBehavior, fontWeight, fontStyle, + strutStyle, ellipsis, - ) { - _textDirection = textDirection ?? ui.TextDirection.ltr; - _fontFamily = fontFamily; - } - - SkParagraphStyle skParagraphStyle; - ui.TextDirection? _textDirection; - String? _fontFamily; + locale, + ), + _textDirection = textDirection ?? ui.TextDirection.ltr, + _fontFamily = fontFamily, + _fontSize = fontSize, + _height = height, + _leadingDistribution = textHeightBehavior?.leadingDistribution, + _fontWeight = fontWeight, + _fontStyle = fontStyle; + + final SkParagraphStyle skParagraphStyle; + final ui.TextDirection? _textDirection; + final String? _fontFamily; + final double? _fontSize; + final double? _height; + final ui.FontWeight? _fontWeight; + final ui.FontStyle? _fontStyle; + final ui.TextLeadingDistribution? _leadingDistribution; static SkTextStyleProperties toSkTextStyleProperties( String? fontFamily, double? fontSize, + double? height, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ) { @@ -54,15 +75,62 @@ class CkParagraphStyle implements ui.ParagraphStyle { skTextStyle.fontSize = fontSize; } - if (fontFamily == null || - !skiaFontCollection.registeredFamilies.contains(fontFamily)) { - fontFamily = 'Roboto'; + if (height != null) { + skTextStyle.heightMultiplier = height; } - skTextStyle.fontFamilies = [fontFamily]; + + skTextStyle.fontFamilies = _getEffectiveFontFamilies(fontFamily); return skTextStyle; } + static SkStrutStyleProperties toSkStrutStyleProperties( + ui.StrutStyle value, ui.TextHeightBehavior? paragraphHeightBehavior) { + final CkStrutStyle style = value as CkStrutStyle; + final SkStrutStyleProperties skStrutStyle = SkStrutStyleProperties(); + skStrutStyle.fontFamilies = + _getEffectiveFontFamilies(style._fontFamily, style._fontFamilyFallback); + + if (style._fontSize != null) { + skStrutStyle.fontSize = style._fontSize; + } + + if (style._height != null) { + skStrutStyle.heightMultiplier = style._height; + } + + final ui.TextLeadingDistribution? effectiveLeadingDistribution = + style._leadingDistribution ?? + paragraphHeightBehavior?.leadingDistribution; + switch (effectiveLeadingDistribution) { + case null: + break; + case ui.TextLeadingDistribution.even: + skStrutStyle.halfLeading = true; + break; + case ui.TextLeadingDistribution.proportional: + skStrutStyle.halfLeading = false; + break; + } + + if (style._leading != null) { + skStrutStyle.leading = style._leading; + } + + if (style._fontWeight != null || style._fontStyle != null) { + skStrutStyle.fontStyle = + toSkFontStyle(style._fontWeight, style._fontStyle); + } + + if (style._forceStrutHeight != null) { + skStrutStyle.forceStrutHeight = style._forceStrutHeight; + } + + skStrutStyle.strutEnabled = true; + + return skStrutStyle; + } + static SkParagraphStyle toSkParagraphStyle( ui.TextAlign? textAlign, ui.TextDirection? textDirection, @@ -73,7 +141,9 @@ class CkParagraphStyle implements ui.ParagraphStyle { ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, String? ellipsis, + ui.Locale? locale, ) { final SkParagraphStyleProperties properties = SkParagraphStyleProperties(); @@ -85,34 +155,48 @@ class CkParagraphStyle implements ui.ParagraphStyle { properties.textDirection = toSkTextDirection(textDirection); } + if (maxLines != null) { + properties.maxLines = maxLines; + } + if (height != null) { properties.heightMultiplier = height; } if (textHeightBehavior != null) { - properties.textHeightBehavior = textHeightBehavior.encode(); - } - - if (maxLines != null) { - properties.maxLines = maxLines; + properties.textHeightBehavior = + toSkTextHeightBehavior(textHeightBehavior); } if (ellipsis != null) { properties.ellipsis = ellipsis; } - properties.textStyle = - toSkTextStyleProperties(fontFamily, fontSize, fontWeight, fontStyle); + if (strutStyle != null) { + properties.strutStyle = + toSkStrutStyleProperties(strutStyle, textHeightBehavior); + } + + properties.textStyle = toSkTextStyleProperties( + fontFamily, fontSize, height, fontWeight, fontStyle); return canvasKit.ParagraphStyle(properties); } + + CkTextStyle getTextStyle() { + return CkTextStyle( + fontFamily: _fontFamily, + fontSize: _fontSize, + height: _height, + leadingDistribution: _leadingDistribution, + fontWeight: _fontWeight, + fontStyle: _fontStyle, + ); + } } +@immutable class CkTextStyle implements ui.TextStyle { - SkTextStyle skTextStyle; - CkPaint? background; - CkPaint? foreground; - factory CkTextStyle({ ui.Color? color, ui.TextDecoration? decoration, @@ -128,12 +212,138 @@ class CkTextStyle implements ui.TextStyle { double? letterSpacing, double? wordSpacing, double? height, + ui.TextLeadingDistribution? leadingDistribution, ui.Locale? locale, CkPaint? background, CkPaint? foreground, List? shadows, List? fontFeatures, }) { + return CkTextStyle._( + color, + decoration, + decorationColor, + decorationStyle, + decorationThickness, + fontWeight, + fontStyle, + textBaseline, + fontFamily, + fontFamilyFallback, + fontSize, + letterSpacing, + wordSpacing, + height, + leadingDistribution, + locale, + background, + foreground, + shadows, + fontFeatures, + ); + } + + CkTextStyle._( + this.color, + this.decoration, + this.decorationColor, + this.decorationStyle, + this.decorationThickness, + this.fontWeight, + this.fontStyle, + this.textBaseline, + this.fontFamily, + this.fontFamilyFallback, + this.fontSize, + this.letterSpacing, + this.wordSpacing, + this.height, + this.leadingDistribution, + this.locale, + this.background, + this.foreground, + this.shadows, + this.fontFeatures, + ); + + final ui.Color? color; + final ui.TextDecoration? decoration; + final ui.Color? decorationColor; + final ui.TextDecorationStyle? decorationStyle; + final double? decorationThickness; + final ui.FontWeight? fontWeight; + final ui.FontStyle? fontStyle; + final ui.TextBaseline? textBaseline; + final String? fontFamily; + final List? fontFamilyFallback; + final double? fontSize; + final double? letterSpacing; + final double? wordSpacing; + final double? height; + final ui.TextLeadingDistribution? leadingDistribution; + final ui.Locale? locale; + final CkPaint? background; + final CkPaint? foreground; + final List? shadows; + final List? fontFeatures; + + /// Merges this text style with [other] and returns the new text style. + /// + /// The values in this text style are used unless [other] specifically + /// overrides it. + CkTextStyle mergeWith(CkTextStyle other) { + return CkTextStyle( + color: other.color ?? color, + decoration: other.decoration ?? decoration, + decorationColor: other.decorationColor ?? decorationColor, + decorationStyle: other.decorationStyle ?? decorationStyle, + decorationThickness: other.decorationThickness ?? decorationThickness, + fontWeight: other.fontWeight ?? fontWeight, + fontStyle: other.fontStyle ?? fontStyle, + textBaseline: other.textBaseline ?? textBaseline, + fontFamily: other.fontFamily ?? fontFamily, + fontFamilyFallback: other.fontFamilyFallback ?? fontFamilyFallback, + fontSize: other.fontSize ?? fontSize, + letterSpacing: other.letterSpacing ?? letterSpacing, + wordSpacing: other.wordSpacing ?? wordSpacing, + height: other.height ?? height, + leadingDistribution: other.leadingDistribution ?? leadingDistribution, + locale: other.locale ?? locale, + background: other.background ?? background, + foreground: other.foreground ?? foreground, + shadows: other.shadows ?? shadows, + fontFeatures: other.fontFeatures ?? fontFeatures, + ); + } + + /// Lazy-initialized list of font families sent to Skia. + late final List effectiveFontFamilies = + _getEffectiveFontFamilies(fontFamily, fontFamilyFallback); + + /// Lazy-initialized Skia style used to pass the style to Skia. + /// + /// This is lazy because not every style ends up being passed to Skia, so the + /// conversion would be wasteful. + late final SkTextStyle skTextStyle = () { + // Write field values to locals so null checks promote types to non-null. + final ui.Color? color = this.color; + final ui.TextDecoration? decoration = this.decoration; + final ui.Color? decorationColor = this.decorationColor; + final ui.TextDecorationStyle? decorationStyle = this.decorationStyle; + final double? decorationThickness = this.decorationThickness; + final ui.FontWeight? fontWeight = this.fontWeight; + final ui.FontStyle? fontStyle = this.fontStyle; + final ui.TextBaseline? textBaseline = this.textBaseline; + final double? fontSize = this.fontSize; + final double? letterSpacing = this.letterSpacing; + final double? wordSpacing = this.wordSpacing; + final double? height = this.height; + final ui.Locale? locale = this.locale; + final CkPaint? background = this.background; + final CkPaint? foreground = this.foreground; + final List? shadows = this.shadows; + final List? fontFeatures = this.fontFeatures; + final SkTextStyleProperties properties = SkTextStyleProperties(); if (background != null) { @@ -162,22 +372,50 @@ class CkTextStyle implements ui.TextStyle { properties.decorationThickness = decorationThickness; } + if (decorationColor != null) { + properties.decorationColor = makeFreshSkColor(decorationColor); + } + + if (decorationStyle != null) { + properties.decorationStyle = toSkTextDecorationStyle(decorationStyle); + } + + if (textBaseline != null) { + properties.textBaseline = toSkTextBaseline(textBaseline); + } + if (fontSize != null) { properties.fontSize = fontSize; } - if (fontFamily == null || - !skiaFontCollection.registeredFamilies.contains(fontFamily)) { - fontFamily = 'Roboto'; + if (letterSpacing != null) { + properties.letterSpacing = letterSpacing; + } + + if (wordSpacing != null) { + properties.wordSpacing = wordSpacing; } - List fontFamilies = [fontFamily]; - if (fontFamilyFallback != null && - !fontFamilyFallback.every((font) => fontFamily == font)) { - fontFamilies.addAll(fontFamilyFallback); + if (height != null) { + properties.heightMultiplier = height; + } + + switch (leadingDistribution) { + case null: + break; + case ui.TextLeadingDistribution.even: + properties.halfLeading = true; + break; + case ui.TextLeadingDistribution.proportional: + properties.halfLeading = false; + break; + } + + if (locale != null) { + properties.locale = locale.toLanguageTag(); } - properties.fontFamilies = fontFamilies; + properties.fontFamilies = effectiveFontFamilies; if (fontWeight != null || fontStyle != null) { properties.fontStyle = toSkFontStyle(fontWeight, fontStyle); @@ -187,25 +425,98 @@ class CkTextStyle implements ui.TextStyle { properties.foregroundColor = makeFreshSkColor(foreground.color); } - // TODO(hterkelsen): Add support for - // - decorationColor - // - decorationStyle - // - textBaseline - // - letterSpacing - // - wordSpacing - // - height - // - locale - // - shadows - // - fontFeatures - return CkTextStyle._( - canvasKit.TextStyle(properties), foreground, background); + if (shadows != null) { + final List ckShadows = []; + for (final ui.Shadow shadow in shadows) { + final SkTextShadow ckShadow = SkTextShadow(); + ckShadow.color = makeFreshSkColor(shadow.color); + ckShadow.offset = toSkPoint(shadow.offset); + ckShadow.blurRadius = shadow.blurRadius; + ckShadows.add(ckShadow); + } + properties.shadows = ckShadows; + } + + if (fontFeatures != null) { + final List skFontFeatures = []; + for (final ui.FontFeature fontFeature in fontFeatures) { + final SkFontFeature skFontFeature = SkFontFeature(); + skFontFeature.name = fontFeature.feature; + skFontFeature.value = fontFeature.value; + skFontFeatures.add(skFontFeature); + } + properties.fontFeatures = skFontFeatures; + } + + return canvasKit.TextStyle(properties); + }(); +} + +class CkStrutStyle implements ui.StrutStyle { + CkStrutStyle({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? height, + // TODO(mdebbar): implement leadingDistribution. + ui.TextLeadingDistribution? leadingDistribution, + double? leading, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + bool? forceStrutHeight, + }) : _fontFamily = fontFamily, + _fontFamilyFallback = fontFamilyFallback, + _fontSize = fontSize, + _height = height, + _leadingDistribution = leadingDistribution, + _leading = leading, + _fontWeight = fontWeight, + _fontStyle = fontStyle, + _forceStrutHeight = forceStrutHeight; + + final String? _fontFamily; + final List? _fontFamilyFallback; + final double? _fontSize; + final double? _height; + final double? _leading; + final ui.FontWeight? _fontWeight; + final ui.FontStyle? _fontStyle; + final bool? _forceStrutHeight; + final ui.TextLeadingDistribution? _leadingDistribution; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is CkStrutStyle && + other._fontFamily == _fontFamily && + other._fontSize == _fontSize && + other._height == _height && + other._leading == _leading && + other._leadingDistribution == _leadingDistribution && + other._fontWeight == _fontWeight && + other._fontStyle == _fontStyle && + other._forceStrutHeight == _forceStrutHeight && + listEquals(other._fontFamilyFallback, _fontFamilyFallback); } - CkTextStyle._(this.skTextStyle, this.foreground, this.background); + @override + int get hashCode => ui.hashValues( + _fontFamily, + _fontFamilyFallback, + _fontSize, + _height, + _leading, + _leadingDistribution, + _fontWeight, + _fontStyle, + _forceStrutHeight, + ); } SkFontStyle toSkFontStyle(ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) { - final style = SkFontStyle(); + final SkFontStyle style = SkFontStyle(); if (fontWeight != null) { style.weight = toSkFontWeight(fontWeight); } @@ -215,15 +526,24 @@ SkFontStyle toSkFontStyle(ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) { return style; } -class CkParagraph extends ManagedSkiaObject - implements ui.Paragraph { - CkParagraph( - this._initialParagraph, this._paragraphStyle, this._paragraphCommands); +/// The CanvasKit implementation of [ui.Paragraph]. +/// +/// This class does not use [ManagedSkiaObject] because it requires that its +/// memory is reclaimed synchronously. This protects our memory usage from +/// blowing up if within a single frame the framework needs to layout a lot of +/// paragraphs. One common use-case is `ListView.builder`, which needs to layout +/// more of its content than it actually renders to compute the scroll position. +/// More generally, this protects from the pattern of laying out a lot of text +/// while painting a small subset of it. To achieve this a +/// [SynchronousSkiaObjectCache] is used that limits the number of live laid out +/// paragraphs at any point in time within or outside the frame. +class CkParagraph extends SkiaObject implements ui.Paragraph { + CkParagraph(this._skParagraph, this._paragraphStyle, this._paragraphCommands); /// The result of calling `build()` on the JS CkParagraphBuilder. /// /// This may be invalidated later. - final SkParagraph _initialParagraph; + SkParagraph? _skParagraph; /// The paragraph style used to build this paragraph. /// @@ -237,106 +557,180 @@ class CkParagraph extends ManagedSkiaObject /// is deleted. final List<_ParagraphCommand> _paragraphCommands; - /// The constraints from the last time we layed the paragraph out. + /// The constraints from the last time we laid the paragraph out. /// /// This is used to resurrect the paragraph if the initial paragraph /// is deleted. ui.ParagraphConstraints? _lastLayoutConstraints; @override - SkParagraph createDefault() => _initialParagraph; + SkParagraph get skiaObject => _ensureInitialized(_lastLayoutConstraints!); + + SkParagraph _ensureInitialized(ui.ParagraphConstraints constraints) { + SkParagraph? paragraph = _skParagraph; + + // Paragraph objects are immutable. It's OK to skip initialization and reuse + // existing object. + bool didRebuildSkiaObject = false; + if (paragraph == null) { + final CkParagraphBuilder builder = CkParagraphBuilder(_paragraphStyle); + for (final _ParagraphCommand command in _paragraphCommands) { + switch (command.type) { + case _ParagraphCommandType.addText: + builder.addText(command.text!); + break; + case _ParagraphCommandType.pop: + builder.pop(); + break; + case _ParagraphCommandType.pushStyle: + builder.pushStyle(command.style!); + break; + case _ParagraphCommandType.addPlaceholder: + builder._addPlaceholder(command.placeholderStyle!); + break; + } + } + paragraph = builder._buildSkParagraph(); + _skParagraph = paragraph; + didRebuildSkiaObject = true; + } - @override - SkParagraph resurrect() { - final builder = CkParagraphBuilder(_paragraphStyle); - for (_ParagraphCommand command in _paragraphCommands) { - switch (command.type) { - case _ParagraphCommandType.addText: - builder.addText(command.text!); - break; - case _ParagraphCommandType.pop: - builder.pop(); - break; - case _ParagraphCommandType.pushStyle: - builder.pushStyle(command.style!); - break; + final bool constraintsChanged = _lastLayoutConstraints != constraints; + if (didRebuildSkiaObject || constraintsChanged) { + _lastLayoutConstraints = constraints; + // TODO(het): CanvasKit throws an exception when laid out with + // a font that wasn't registered. + try { + paragraph.layout(constraints.width); + _alphabeticBaseline = paragraph.getAlphabeticBaseline(); + _didExceedMaxLines = paragraph.didExceedMaxLines(); + _height = paragraph.getHeight(); + _ideographicBaseline = paragraph.getIdeographicBaseline(); + _longestLine = paragraph.getLongestLine(); + _maxIntrinsicWidth = paragraph.getMaxIntrinsicWidth(); + _minIntrinsicWidth = paragraph.getMinIntrinsicWidth(); + _width = paragraph.getMaxWidth(); + _boxesForPlaceholders = + skRectsToTextBoxes(paragraph.getRectsForPlaceholders()); + } catch (e) { + printWarning('CanvasKit threw an exception while laying ' + 'out the paragraph. The font was "${_paragraphStyle._fontFamily}". ' + 'Exception:\n$e'); + rethrow; } } - final SkParagraph result = builder._buildCkParagraph(); - if (_lastLayoutConstraints != null) { - // We need to set the Skia object early so layout works. - rawSkiaObject = result; - this.layout(_lastLayoutConstraints!); + return paragraph; + } + + // Caches laid out paragraphs and synchronously reclaims memory if there's + // memory pressure. + // + // On May 26, 2021, 500 seemed like a reasonable number to pick for the cache + // size. At the time a single laid out SkParagraph used 100KB of memory. So, + // 500 items in the cache is roughly 50MB of memory, which is not too high, + // while at the same time enough for most use-cases. + // + // TODO(yjbanov): this strategy is not sufficient for the use-case where a + // lot of paragraphs are laid out _and_ rendered. To support + // this use-case without blowing up memory usage we need this: + // https://github.com/flutter/flutter/issues/81224 + static SynchronousSkiaObjectCache _paragraphCache = + SynchronousSkiaObjectCache(500); + + /// Marks this paragraph as having been used this frame. + /// + /// Puts this paragraph in a [SynchronousSkiaObjectCache], which will delete it + /// if there's memory pressure to do so. This protects our memory usage from + /// blowing up if within a single frame the framework needs to layout a lot of + /// paragraphs. One common use-case is `ListView.builder`, which needs to layout + /// more of its content than it actually renders to compute the scroll position. + void markUsed() { + // If the paragraph is already in the cache, just mark it as most recently + // used. Otherwise, add to cache. + if (!_paragraphCache.markUsed(this)) { + _paragraphCache.add(this); } - return result; } @override void delete() { - rawSkiaObject?.delete(); + _skParagraph!.delete(); } @override - bool get isResurrectionExpensive => true; + void didDelete() { + _skParagraph = null; + } @override - double get alphabeticBaseline => skiaObject.getAlphabeticBaseline(); + double get alphabeticBaseline => _alphabeticBaseline; + double _alphabeticBaseline = 0; @override - bool get didExceedMaxLines => skiaObject.didExceedMaxLines(); + bool get didExceedMaxLines => _didExceedMaxLines; + bool _didExceedMaxLines = false; @override - double get height => skiaObject.getHeight(); + double get height => _height; + double _height = 0; @override - double get ideographicBaseline => skiaObject.getIdeographicBaseline(); + double get ideographicBaseline => _ideographicBaseline; + double _ideographicBaseline = 0; @override - double get longestLine => skiaObject.getLongestLine(); + double get longestLine => _longestLine; + double _longestLine = 0; @override - double get maxIntrinsicWidth => skiaObject.getMaxIntrinsicWidth(); + double get maxIntrinsicWidth => _maxIntrinsicWidth; + double _maxIntrinsicWidth = 0; @override - double get minIntrinsicWidth => skiaObject.getMinIntrinsicWidth(); + double get minIntrinsicWidth => _minIntrinsicWidth; + double _minIntrinsicWidth = 0; @override - double get width => skiaObject.getMaxWidth(); + double get width => _width; + double _width = 0; - // TODO(hterkelsen): Implement placeholders once it's in CanvasKit @override - List getBoxesForPlaceholders() { - return const []; - } + List getBoxesForPlaceholders() => _boxesForPlaceholders!; + List? _boxesForPlaceholders; @override List getBoxesForRange( int start, int end, { - ui.BoxHeightStyle boxHeightStyle: ui.BoxHeightStyle.tight, - ui.BoxWidthStyle boxWidthStyle: ui.BoxWidthStyle.tight, + ui.BoxHeightStyle boxHeightStyle = ui.BoxHeightStyle.tight, + ui.BoxWidthStyle boxWidthStyle = ui.BoxWidthStyle.tight, }) { if (start < 0 || end < 0) { return const []; } - List skRects = skiaObject.getRectsForRange( + final SkParagraph paragraph = _ensureInitialized(_lastLayoutConstraints!); + final List> skRects = paragraph.getRectsForRange( start, end, toSkRectHeightStyle(boxHeightStyle), toSkRectWidthStyle(boxWidthStyle), ); - List result = []; + return skRectsToTextBoxes(skRects); + } + + List skRectsToTextBoxes(List skRects) { + final List result = []; for (int i = 0; i < skRects.length; i++) { - final SkRect rect = skRects[i]; + final List rect = skRects[i] as List; result.add(ui.TextBox.fromLTRBD( - rect.fLeft, - rect.fTop, - rect.fRight, - rect.fBottom, + rect[0], + rect[1], + rect[2], + rect[3], _paragraphStyle._textDirection!, )); } @@ -346,8 +740,9 @@ class CkParagraph extends ManagedSkiaObject @override ui.TextPosition getPositionForOffset(ui.Offset offset) { + final SkParagraph paragraph = _ensureInitialized(_lastLayoutConstraints!); final SkTextPosition positionWithAffinity = - skiaObject.getGlyphPositionAtCoordinate( + paragraph.getGlyphPositionAtCoordinate( offset.dx, offset.dy, ); @@ -356,64 +751,104 @@ class CkParagraph extends ManagedSkiaObject @override ui.TextRange getWordBoundary(ui.TextPosition position) { - final SkTextRange skRange = skiaObject.getWordBoundary(position.offset); + final SkParagraph paragraph = _ensureInitialized(_lastLayoutConstraints!); + final SkTextRange skRange = paragraph.getWordBoundary(position.offset); return ui.TextRange(start: skRange.start, end: skRange.end); } @override void layout(ui.ParagraphConstraints constraints) { - assert(constraints.width != null); // ignore: unnecessary_null_comparison - _lastLayoutConstraints = constraints; - - // Infinite width breaks layout, just use a very large number instead. - // TODO(het): Remove this once https://bugs.chromium.org/p/skia/issues/detail?id=9874 - // is fixed. - double width; - const double largeFiniteWidth = 1000000; - if (constraints.width.isInfinite) { - width = largeFiniteWidth; - } else { - width = constraints.width; - } - // TODO(het): CanvasKit throws an exception when laid out with - // a font that wasn't registered. - try { - skiaObject.layout(width); - } catch (e) { - html.window.console.warn('CanvasKit threw an exception while laying ' - 'out the paragraph. The font was "${_paragraphStyle._fontFamily}". ' - 'Exception:\n$e'); - rethrow; + if (_lastLayoutConstraints == constraints) { + return; } + _ensureInitialized(constraints); + + // See class-level and _paragraphCache doc comments for why we're releasing + // the paragraph immediately after layout. + markUsed(); } @override ui.TextRange getLineBoundary(ui.TextPosition position) { - // TODO(hterkelsen): Implement this when it's added to CanvasKit - throw UnimplementedError('getLineBoundary'); + final SkParagraph paragraph = _ensureInitialized(_lastLayoutConstraints!); + final List metrics = paragraph.getLineMetrics(); + final int offset = position.offset; + for (final SkLineMetrics metric in metrics) { + if (offset >= metric.startIndex && offset <= metric.endIndex) { + return ui.TextRange(start: metric.startIndex, end: metric.endIndex); + } + } + return const ui.TextRange(start: -1, end: -1); } @override List computeLineMetrics() { - // TODO(hterkelsen): Implement this when it's added to CanvasKit - throw UnimplementedError('computeLineMetrics'); + final SkParagraph paragraph = _ensureInitialized(_lastLayoutConstraints!); + final List skLineMetrics = paragraph.getLineMetrics(); + final List result = []; + for (final SkLineMetrics metric in skLineMetrics) { + result.add(CkLineMetrics._(metric)); + } + return result; } } +class CkLineMetrics implements ui.LineMetrics { + CkLineMetrics._(this.skLineMetrics); + + final SkLineMetrics skLineMetrics; + + @override + double get ascent => skLineMetrics.ascent; + + @override + double get descent => skLineMetrics.descent; + + // TODO(hterkelsen): Implement this correctly once SkParagraph does. + @override + double get unscaledAscent => skLineMetrics.ascent; + + @override + bool get hardBreak => skLineMetrics.isHardBreak; + + @override + double get baseline => skLineMetrics.baseline; + + @override + double get height => + (skLineMetrics.ascent + skLineMetrics.descent).round().toDouble(); + + @override + double get left => skLineMetrics.left; + + @override + double get width => skLineMetrics.width; + + @override + int get lineNumber => skLineMetrics.lineNumber; +} + class CkParagraphBuilder implements ui.ParagraphBuilder { final SkParagraphBuilder _paragraphBuilder; final CkParagraphStyle _style; final List<_ParagraphCommand> _commands; + int _placeholderCount; + final List _placeholderScales; + final List _styleStack; CkParagraphBuilder(ui.ParagraphStyle style) : _commands = <_ParagraphCommand>[], _style = style as CkParagraphStyle, + _placeholderCount = 0, + _placeholderScales = [], + _styleStack = [], _paragraphBuilder = canvasKit.ParagraphBuilder.MakeFromFontProvider( style.skParagraphStyle, skiaFontCollection.fontProvider, - ); + ) { + _styleStack.add(_style.getTextStyle()); + } - // TODO(hterkelsen): Implement placeholders. @override void addPlaceholder( double width, @@ -423,48 +858,136 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { double? baselineOffset, ui.TextBaseline? baseline, }) { - throw UnimplementedError('addPlaceholder'); + // Require a baseline to be specified if using a baseline-based alignment. + assert(!(alignment == ui.PlaceholderAlignment.aboveBaseline || + alignment == ui.PlaceholderAlignment.belowBaseline || + alignment == ui.PlaceholderAlignment.baseline) || baseline != null); + + _placeholderCount++; + _placeholderScales.add(scale); + final _CkParagraphPlaceholder placeholderStyle = toSkPlaceholderStyle( + width * scale, + height * scale, + alignment, + (baselineOffset ?? height) * scale, + baseline ?? ui.TextBaseline.alphabetic, + ); + _addPlaceholder(placeholderStyle); + } + + void _addPlaceholder(_CkParagraphPlaceholder placeholderStyle) { + _commands.add(_ParagraphCommand.addPlaceholder(placeholderStyle)); + _paragraphBuilder.addPlaceholder( + placeholderStyle.width, + placeholderStyle.height, + placeholderStyle.alignment, + placeholderStyle.baseline, + placeholderStyle.offset, + ); + } + + static _CkParagraphPlaceholder toSkPlaceholderStyle( + double width, + double height, + ui.PlaceholderAlignment alignment, + double baselineOffset, + ui.TextBaseline baseline, + ) { + final _CkParagraphPlaceholder properties = _CkParagraphPlaceholder( + width: width, + height: height, + alignment: toSkPlaceholderAlignment(alignment), + offset: baselineOffset, + baseline: toSkTextBaseline(baseline), + ); + return properties; } @override void addText(String text) { + final List fontFamilies = []; + final CkTextStyle style = _peekStyle(); + if (style.fontFamily != null) { + fontFamilies.add(style.fontFamily!); + } + if (style.fontFamilyFallback != null) { + fontFamilies.addAll(style.fontFamilyFallback!); + } + FontFallbackData.instance.ensureFontsSupportText(text, fontFamilies); _commands.add(_ParagraphCommand.addText(text)); _paragraphBuilder.addText(text); } @override - ui.Paragraph build() { - final builtParagraph = _buildCkParagraph(); + CkParagraph build() { + final SkParagraph builtParagraph = _buildSkParagraph(); return CkParagraph(builtParagraph, _style, _commands); } /// Builds the CkParagraph with the builder and deletes the builder. - SkParagraph _buildCkParagraph() { + SkParagraph _buildSkParagraph() { final SkParagraph result = _paragraphBuilder.build(); _paragraphBuilder.delete(); return result; } @override - int get placeholderCount => throw UnimplementedError('placeholderCount'); + int get placeholderCount => _placeholderCount; - // TODO(hterkelsen): Implement this once CanvasKit exposes placeholders. @override - List get placeholderScales => const []; + List get placeholderScales => _placeholderScales; @override void pop() { + if (_styleStack.length <= 1) { + // The top-level text style is paragraph-level. We don't pop it off. + if (assertionsEnabled) { + printWarning( + 'Cannot pop text style in ParagraphBuilder. ' + 'Already popped all text styles from the style stack.', + ); + } + return; + } _commands.add(const _ParagraphCommand.pop()); + _styleStack.removeLast(); _paragraphBuilder.pop(); } + CkTextStyle _peekStyle() { + assert(_styleStack.isNotEmpty); + return _styleStack.last; + } + + // Used as the paint for background or foreground in the text style when + // the other one is not specified. CanvasKit either both background and + // foreground paints specified, or neither, but Flutter allows one of them + // to go unspecified. + // + // This object is never deleted. It is effectively a static global constant. + // Therefore it doesn't need to be wrapped in CkPaint. + static final SkPaint _defaultTextForeground = SkPaint(); + static final SkPaint _defaultTextBackground = SkPaint() + ..setColorInt(0x00000000); + @override void pushStyle(ui.TextStyle style) { - final CkTextStyle skStyle = style as CkTextStyle; - _commands.add(_ParagraphCommand.pushStyle(skStyle)); + final CkTextStyle baseStyle = _peekStyle(); + final CkTextStyle ckStyle = style as CkTextStyle; + final CkTextStyle skStyle = baseStyle.mergeWith(ckStyle); + _styleStack.add(skStyle); + _commands.add(_ParagraphCommand.pushStyle(ckStyle)); if (skStyle.foreground != null || skStyle.background != null) { - final SkPaint foreground = skStyle.foreground?.skiaObject ?? SkPaint(); - final SkPaint background = skStyle.background?.skiaObject ?? SkPaint(); + SkPaint? foreground = skStyle.foreground?.skiaObject; + if (foreground == null) { + _defaultTextForeground.setColorInt( + skStyle.color?.value ?? 0xFF000000, + ); + foreground = _defaultTextForeground; + } + + final SkPaint background = + skStyle.background?.skiaObject ?? _defaultTextBackground; _paragraphBuilder.pushPaintStyle( skStyle.skTextStyle, foreground, background); } else { @@ -473,24 +996,67 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { } } +class _CkParagraphPlaceholder { + _CkParagraphPlaceholder({ + required this.width, + required this.height, + required this.alignment, + required this.baseline, + required this.offset, + }); + + final double width; + final double height; + final SkPlaceholderAlignment alignment; + final SkTextBaseline baseline; + final double offset; +} + class _ParagraphCommand { final _ParagraphCommandType type; final String? text; final CkTextStyle? style; + final _CkParagraphPlaceholder? placeholderStyle; - const _ParagraphCommand._(this.type, this.text, this.style); + const _ParagraphCommand._( + this.type, + this.text, + this.style, + this.placeholderStyle, + ); const _ParagraphCommand.addText(String text) - : this._(_ParagraphCommandType.addText, text, null); + : this._(_ParagraphCommandType.addText, text, null, null); - const _ParagraphCommand.pop() : this._(_ParagraphCommandType.pop, null, null); + const _ParagraphCommand.pop() + : this._(_ParagraphCommandType.pop, null, null, null); const _ParagraphCommand.pushStyle(CkTextStyle style) - : this._(_ParagraphCommandType.pushStyle, null, style); + : this._(_ParagraphCommandType.pushStyle, null, style, null); + + const _ParagraphCommand.addPlaceholder( + _CkParagraphPlaceholder placeholderStyle) + : this._( + _ParagraphCommandType.addPlaceholder, null, null, placeholderStyle); } enum _ParagraphCommandType { addText, pop, pushStyle, + addPlaceholder, +} + +List _getEffectiveFontFamilies(String? fontFamily, + [List? fontFamilyFallback]) { + final List fontFamilies = []; + if (fontFamily != null) { + fontFamilies.add(fontFamily); + } + if (fontFamilyFallback != null && + !fontFamilyFallback.every((String font) => fontFamily == font)) { + fontFamilies.addAll(fontFamilyFallback); + } + fontFamilies.addAll(FontFallbackData.instance.globalFontFallbacks); + return fontFamilies; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/util.dart b/lib/web_ui/lib/src/engine/canvaskit/util.dart index 47464e9f76751..7700abaf28ce7 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/util.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/util.dart @@ -2,8 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; +import '../vector_math.dart'; +import 'canvaskit_api.dart'; +import 'path.dart'; /// An error related to the CanvasKit rendering backend. class CanvasKitError extends Error { @@ -27,13 +34,126 @@ Float32List makeFreshSkColor(ui.Color color) { } ui.TextPosition fromPositionWithAffinity(SkTextPosition positionWithAffinity) { - final ui.TextAffinity affinity = ui.TextAffinity.values[positionWithAffinity.affinity.value]; + final ui.TextAffinity affinity = + ui.TextAffinity.values[positionWithAffinity.affinity.value]; return ui.TextPosition( offset: positionWithAffinity.pos, affinity: affinity, ); } +/// Shadow flag constants derived from Skia's SkShadowFlags.h. +class SkiaShadowFlags { + /// The occluding object is opaque, making the part of the shadow under the + /// occluder invisible. This allows some optimizations because some parts of + /// the shadow do not need to be accurate. + static const int kNone_ShadowFlag = 0x00; + + /// The occluding object is not opaque, making the part of the shadow under the + /// occluder visible. This requires that the shadow is rendered more accurately + /// and therefore is slightly more expensive. + static const int kTransparentOccluder_ShadowFlag = 0x01; + + /// Light position represents a direction, light radius is blur radius at + /// elevation 1. + /// + /// This makes the shadow to have a fixed position relative to the shape that + /// casts it. + static const int kDirectionalLight_ShadowFlag = 0x04; + + /// Complete value for the `flags` argument for opaque occluder. + static const int kDefaultShadowFlags = + kDirectionalLight_ShadowFlag | kNone_ShadowFlag; + + /// Complete value for the `flags` argument for transparent occluder. + static const int kTransparentOccluderShadowFlags = + kDirectionalLight_ShadowFlag | kTransparentOccluder_ShadowFlag; +} + +// These numbers have been chosen empirically to give a result closest to the +// material spec. +const double ckShadowAmbientAlpha = 0.039; +const double ckShadowSpotAlpha = 0.25; +const double ckShadowLightRadius = 1.1; +const double ckShadowLightHeight = 600.0; +const double ckShadowLightXOffset = 0; +const double ckShadowLightYOffset = -450; +const double ckShadowLightXTangent = ckShadowLightXOffset / ckShadowLightHeight; +const double ckShadowLightYTangent = ckShadowLightYOffset / ckShadowLightHeight; + +/// Computes the smallest rectangle that contains the shadow. +// Most of this logic is borrowed from SkDrawShadowInfo.cpp in Skia. +// TODO(yjbanov): switch to SkDrawShadowMetrics::GetLocalBounds when available +// See: +// - https://bugs.chromium.org/p/skia/issues/detail?id=11146 +// - https://github.com/flutter/flutter/issues/73492 +ui.Rect computeSkShadowBounds( + CkPath path, + double elevation, + double devicePixelRatio, + Matrix4 matrix, +) { + ui.Rect pathBounds = path.getBounds(); + + if (elevation == 0) { + return pathBounds; + } + + // For visual correctness the shadow offset and blur does not change with + // parent transforms. Therefore, in general case we have to first transform + // the shape bounds to device coordinates, then compute the shadow bounds, + // then transform the bounds back to local coordinates. However, if the + // transform is an identity or translation (a common case), we can skip this + // step. With directional lighting translation does not affect the size or + // shape of the shadow. Skipping this step saves us two transformRects and + // one matrix inverse. + final bool isComplex = !matrix.isIdentityOrTranslation(); + if (isComplex) { + pathBounds = transformRect(matrix, pathBounds); + } + + double left = pathBounds.left; + double top = pathBounds.top; + double right = pathBounds.right; + double bottom = pathBounds.bottom; + + final double ambientBlur = ambientBlurRadius(elevation); + final double spotBlur = ckShadowLightRadius * elevation; + final double spotOffsetX = -elevation * ckShadowLightXTangent; + final double spotOffsetY = -elevation * ckShadowLightYTangent; + + // The extra +1/-1 are to cover possible floating point errors. + left = left - 1 + (spotOffsetX - ambientBlur - spotBlur) * devicePixelRatio; + top = top - 1 + (spotOffsetY - ambientBlur - spotBlur) * devicePixelRatio; + right = right + 1 + (spotOffsetX + ambientBlur + spotBlur) * devicePixelRatio; + bottom = + bottom + 1 + (spotOffsetY + ambientBlur + spotBlur) * devicePixelRatio; + + final ui.Rect shadowBounds = ui.Rect.fromLTRB(left, top, right, bottom); + + if (isComplex) { + final Matrix4 inverse = Matrix4.zero(); + // The inverse only makes sense if the determinat is non-zero. + if (inverse.copyInverse(matrix) != 0.0) { + return transformRect(inverse, shadowBounds); + } else { + return shadowBounds; + } + } else { + return shadowBounds; + } +} + +const double kAmbientHeightFactor = 1.0 / 128.0; +const double kAmbientGeomFactor = 64.0; +const double kMaxAmbientRadius = + 300 * kAmbientHeightFactor * kAmbientGeomFactor; + +double ambientBlurRadius(double height) { + return math.min( + height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius); +} + void drawSkShadow( SkCanvas skCanvas, CkPath path, @@ -42,35 +162,29 @@ void drawSkShadow( bool transparentOccluder, double devicePixelRatio, ) { - const double ambientAlpha = 0.039; - const double spotAlpha = 0.25; + final int flags = transparentOccluder + ? SkiaShadowFlags.kTransparentOccluderShadowFlags + : SkiaShadowFlags.kDefaultShadowFlags; - final int flags = transparentOccluder ? 0x01 : 0x00; - - final ui.Rect bounds = path.getBounds(); - final double shadowX = (bounds.left + bounds.right) / 2.0; - final double shadowY = bounds.top - 600.0; - - ui.Color inAmbient = color.withAlpha((color.alpha * ambientAlpha).round()); - ui.Color inSpot = color.withAlpha((color.alpha * spotAlpha).round()); + final ui.Color inAmbient = + color.withAlpha((color.alpha * ckShadowAmbientAlpha).round()); + final ui.Color inSpot = color.withAlpha((color.alpha * ckShadowSpotAlpha).round()); final SkTonalColors inTonalColors = SkTonalColors( ambient: makeFreshSkColor(inAmbient), spot: makeFreshSkColor(inSpot), ); - final SkTonalColors tonalColors = - canvasKit.computeTonalColors(inTonalColors); + final SkTonalColors tonalColors = canvasKit.computeTonalColors(inTonalColors); skCanvas.drawShadow( - path._skPath, - Float32List(3) - ..[2] = devicePixelRatio * elevation, + path.skiaObject, + Float32List(3)..[2] = devicePixelRatio * elevation, Float32List(3) - ..[0] = shadowX - ..[1] = shadowY - ..[2] = devicePixelRatio * kLightHeight, - devicePixelRatio * kLightRadius, + ..[0] = ckShadowLightXOffset + ..[1] = ckShadowLightYOffset + ..[2] = devicePixelRatio * ckShadowLightHeight, + devicePixelRatio * ckShadowLightRadius, tonalColors.ambient, tonalColors.spot, flags, diff --git a/lib/web_ui/lib/src/engine/canvaskit/vertices.dart b/lib/web_ui/lib/src/engine/canvaskit/vertices.dart index ec13910db9c39..c45c2bf592a85 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/vertices.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/vertices.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'canvaskit_api.dart'; +import 'skia_object_cache.dart'; class CkVertices extends ManagedSkiaObject implements ui.Vertices { factory CkVertices( @@ -28,9 +32,9 @@ class CkVertices extends ManagedSkiaObject implements ui.Vertices { return CkVertices._( toSkVertexMode(mode), - toSkPoints2d(positions), - textureCoordinates != null ? toSkPoints2d(textureCoordinates) : null, - colors != null ? toSkFloatColorList(colors) : null, + toFlatSkPoints(positions), + textureCoordinates != null ? toFlatSkPoints(textureCoordinates) : null, + colors != null ? toFlatColors(colors) : null, indices != null ? toUint16List(indices) : null, ); } @@ -57,9 +61,9 @@ class CkVertices extends ManagedSkiaObject implements ui.Vertices { return CkVertices._( toSkVertexMode(mode), - rawPointsToSkPoints2d(positions), - textureCoordinates != null ? rawPointsToSkPoints2d(textureCoordinates) : null, - colors != null ? encodeRawColorList(colors) : null, + positions, + textureCoordinates, + colors?.buffer.asUint32List(), indices, ); } @@ -73,14 +77,14 @@ class CkVertices extends ManagedSkiaObject implements ui.Vertices { ); final SkVertexMode _mode; - final List _positions; - final List? _textureCoordinates; - final List? _colors; + final Float32List _positions; + final Float32List? _textureCoordinates; + final Uint32List? _colors; final Uint16List? _indices; @override SkVertices createDefault() { - return canvasKit.MakeSkVertices( + return canvasKit.MakeVertices( _mode, _positions, _textureCoordinates, diff --git a/lib/web_ui/lib/src/engine/canvaskit/viewport_metrics.dart b/lib/web_ui/lib/src/engine/canvaskit/viewport_metrics.dart index 2e7572f224a88..99347d356dbf9 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/viewport_metrics.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/viewport_metrics.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - class ViewportMetrics { final double devicePixelRatio; final double physicalWidth; diff --git a/lib/web_ui/lib/src/engine/clipboard.dart b/lib/web_ui/lib/src/engine/clipboard.dart index f5b162ac7bfa3..4bc1e8da2405e 100644 --- a/lib/web_ui/lib/src/engine/clipboard.dart +++ b/lib/web_ui/lib/src/engine/clipboard.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'services.dart'; +import 'util.dart'; /// Handles clipboard related platform messages. class ClipboardMessageHandler { @@ -20,7 +25,7 @@ class ClipboardMessageHandler { const MethodCodec codec = JSONMethodCodec(); bool errorEnvelopeEncoded = false; _copyToClipboardStrategy - .setData(methodCall.arguments['text']) + .setData(methodCall.arguments['text'] as String?) .then((bool success) { if (success) { callback!(codec.encodeSuccessEnvelope(true)); @@ -46,12 +51,29 @@ class ClipboardMessageHandler { final Map map = {'text': data}; callback!(codec.encodeSuccessEnvelope(map)); }).catchError((dynamic error) { - print('Could not get text from clipboard: $error'); - callback!(codec.encodeErrorEnvelope( - code: 'paste_fail', message: 'Clipboard.getData failed')); + if (error is UnimplementedError) { + // Clipboard.getData not supported. + // Passing [null] to [callback] indicates that the platform message isn't + // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is + // handled. + Future.delayed(Duration.zero).then((_) { + if (callback != null) { + callback(null); + } + }); + return; + } + _reportGetDataFailure(callback, codec, error); }); } + void _reportGetDataFailure(ui.PlatformMessageResponseCallback? callback, + MethodCodec codec, dynamic error) { + print('Could not get text from clipboard: $error'); + callback!(codec.encodeErrorEnvelope( + code: 'paste_fail', message: 'Clipboard.getData failed')); + } + /// Methods used by tests. set pasteFromClipboardStrategy(PasteFromClipboardStrategy strategy) { _pasteFromClipboardStrategy = strategy; @@ -62,17 +84,13 @@ class ClipboardMessageHandler { } } -bool _unsafeIsNull(dynamic object) { - return object == null; -} - /// Provides functionality for writing text to clipboard. /// /// A concrete implementation is picked at runtime based on the available /// APIs and the browser. abstract class CopyToClipboardStrategy { factory CopyToClipboardStrategy() { - return !_unsafeIsNull(html.window.navigator.clipboard) + return !unsafeIsNull(html.window.navigator.clipboard) ? ClipboardAPICopyStrategy() : ExecCommandCopyStrategy(); } @@ -92,7 +110,7 @@ abstract class CopyToClipboardStrategy { abstract class PasteFromClipboardStrategy { factory PasteFromClipboardStrategy() { return (browserEngine == BrowserEngine.firefox || - _unsafeIsNull(html.window.navigator.clipboard)) + unsafeIsNull(html.window.navigator.clipboard)) ? ExecCommandPasteStrategy() : ClipboardAPIPasteStrategy(); } @@ -112,9 +130,9 @@ class ClipboardAPICopyStrategy implements CopyToClipboardStrategy { await html.window.navigator.clipboard!.writeText(text!); } catch (error) { print('copy is not successful $error'); - return Future.value(false); + return Future.value(false); } - return Future.value(true); + return Future.value(true); } } @@ -135,7 +153,7 @@ class ClipboardAPIPasteStrategy implements PasteFromClipboardStrategy { class ExecCommandCopyStrategy implements CopyToClipboardStrategy { @override Future setData(String? text) { - return Future.value(_setDataSync(text)); + return Future.value(_setDataSync(text)); } bool _setDataSync(String? text) { @@ -185,9 +203,8 @@ class ExecCommandCopyStrategy implements CopyToClipboardStrategy { class ExecCommandPasteStrategy implements PasteFromClipboardStrategy { @override Future getData() { - // TODO(nurhan): https://github.com/flutter/flutter/issues/48581 - // TODO(nurhan): https://github.com/flutter/flutter/issues/48580 - print('Paste is not implemented for this browser.'); - throw UnimplementedError(); + // TODO(mdebbar): https://github.com/flutter/flutter/issues/48581 + return Future.error( + UnimplementedError('Paste is not implemented for this browser.')); } } diff --git a/lib/web_ui/lib/src/engine/color_filter.dart b/lib/web_ui/lib/src/engine/color_filter.dart index 230cdf9a2ae25..10b9202b00a2b 100644 --- a/lib/web_ui/lib/src/engine/color_filter.dart +++ b/lib/web_ui/lib/src/engine/color_filter.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import 'canvaskit/color_filter.dart'; /// A description of a color filter to apply when drawing a shape or compositing /// a layer with a particular [Paint]. A color filter is a function that takes @@ -21,11 +22,8 @@ class EngineColorFilter implements ui.ColorFilter { /// The output of this filter is then composited into the background according /// to the [Paint.blendMode], using the output of this filter as the source /// and the background as the destination. - const EngineColorFilter.mode(ui.Color color, ui.BlendMode blendMode) - : _color = color, - _blendMode = blendMode, - _matrix = null, - _type = _TypeMode; + const factory EngineColorFilter.mode(ui.Color color, ui.BlendMode blendMode) = + CkBlendModeColorFilter; /// Construct a color filter that transforms a color by a 5x5 matrix, where /// the fifth row is implicitly added in an identity configuration. @@ -87,86 +85,16 @@ class EngineColorFilter implements ui.ColorFilter { /// 0, 0, 0, 1, 0, /// ]); /// ``` - const EngineColorFilter.matrix(List matrix) - : _color = null, - _blendMode = null, - _matrix = matrix, - _type = _TypeMatrix; + const factory EngineColorFilter.matrix(List matrix) = + CkMatrixColorFilter; /// Construct a color filter that applies the sRGB gamma curve to the RGB /// channels. - const EngineColorFilter.linearToSrgbGamma() - : _color = null, - _blendMode = null, - _matrix = null, - _type = _TypeLinearToSrgbGamma; + const factory EngineColorFilter.linearToSrgbGamma() = + CkLinearToSrgbGammaColorFilter; /// Creates a color filter that applies the inverse of the sRGB gamma curve /// to the RGB channels. - const EngineColorFilter.srgbToLinearGamma() - : _color = null, - _blendMode = null, - _matrix = null, - _type = _TypeSrgbToLinearGamma; - - final ui.Color? _color; - final ui.BlendMode? _blendMode; - final List? _matrix; - final int _type; - - // The type of CkColorFilter class to create for Skia. - static const int _TypeMode = 1; // MakeModeFilter - static const int _TypeMatrix = 2; // MakeMatrixFilterRowMajor255 - static const int _TypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma - static const int _TypeSrgbToLinearGamma = 4; // MakeSRGBToLinearGamma - - @override - bool operator ==(Object other) { - return other is EngineColorFilter - && other._type == _type - && _listEquals(other._matrix, _matrix) - && other._color == _color - && other._blendMode == _blendMode; - } - - CkColorFilter? _toCkColorFilter() { - switch (_type) { - case _TypeMode: - if (_color == null || _blendMode == null) { - return null; - } - return CkColorFilter.mode(this); - case _TypeMatrix: - if (_matrix == null) { - return null; - } - assert(_matrix!.length == 20, 'Color Matrix must have 20 entries.'); - return CkColorFilter.matrix(this); - case _TypeLinearToSrgbGamma: - return CkColorFilter.linearToSrgbGamma(this); - case _TypeSrgbToLinearGamma: - return CkColorFilter.srgbToLinearGamma(this); - default: - throw StateError('Unknown mode $_type for ColorFilter.'); - } - } - - @override - int get hashCode => ui.hashValues(_color, _blendMode, ui.hashList(_matrix), _type); - - @override - String toString() { - switch (_type) { - case _TypeMode: - return 'ColorFilter.mode($_color, $_blendMode)'; - case _TypeMatrix: - return 'ColorFilter.matrix($_matrix)'; - case _TypeLinearToSrgbGamma: - return 'ColorFilter.linearToSrgbGamma()'; - case _TypeSrgbToLinearGamma: - return 'ColorFilter.srgbToLinearGamma()'; - default: - return 'Unknown ColorFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.'; - } - } + const factory EngineColorFilter.srgbToLinearGamma() = + CkSrgbToLinearGammaColorFilter; } diff --git a/lib/web_ui/lib/src/engine/dom_canvas.dart b/lib/web_ui/lib/src/engine/dom_canvas.dart deleted file mode 100644 index 0eabf6d8a4aac..0000000000000 --- a/lib/web_ui/lib/src/engine/dom_canvas.dart +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.10 -part of engine; - -/// A canvas that renders to DOM elements and CSS properties. -class DomCanvas extends EngineCanvas with SaveElementStackTracking { - @override - final html.Element rootElement = html.Element.tag('flt-dom-canvas'); - - DomCanvas() { - rootElement.style - ..position = 'absolute' - ..top = '0' - ..right = '0' - ..bottom = '0' - ..left = '0'; - } - - /// Prepare to reuse this canvas by clearing it's current contents. - @override - void clear() { - super.clear(); - // TODO(yjbanov): we should measure if reusing old elements is beneficial. - domRenderer.clearDom(rootElement); - } - - @override - void clipRect(ui.Rect rect) { - throw UnimplementedError(); - } - - @override - void clipRRect(ui.RRect rrect) { - throw UnimplementedError(); - } - - @override - void clipPath(ui.Path path) { - throw UnimplementedError(); - } - - @override - void drawColor(ui.Color color, ui.BlendMode blendMode) { - // TODO(yjbanov): implement blendMode - final html.Element box = html.Element.tag('draw-color'); - box.style - ..position = 'absolute' - ..top = '0' - ..right = '0' - ..bottom = '0' - ..left = '0' - ..backgroundColor = colorToCssString(color); - currentElement.append(box); - } - - @override - void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawPaint(SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawRect(ui.Rect rect, SurfacePaintData paint) { - _drawRect(rect, paint, 'draw-rect'); - } - - html.Element _drawRect(ui.Rect rect, SurfacePaintData paint, String tagName) { - assert(paint.shader == null); - final html.Element rectangle = html.Element.tag(tagName); - assert(() { - rectangle.setAttribute('flt-rect', '$rect'); - rectangle.setAttribute('flt-paint', '$paint'); - return true; - }()); - String effectiveTransform; - final bool isStroke = paint.style == ui.PaintingStyle.stroke; - final double strokeWidth = paint.strokeWidth ?? 0.0; - final double left = math.min(rect.left, rect.right); - final double right = math.max(rect.left, rect.right); - final double top = math.min(rect.top, rect.bottom); - final double bottom = math.max(rect.top, rect.bottom); - if (currentTransform.isIdentity()) { - if (isStroke) { - effectiveTransform = - 'translate(${left - (strokeWidth / 2.0)}px, ${top - (strokeWidth / 2.0)}px)'; - } else { - effectiveTransform = 'translate(${left}px, ${top}px)'; - } - } else { - // Clone to avoid mutating _transform. - final Matrix4 translated = currentTransform.clone(); - if (isStroke) { - translated.translate( - left - (strokeWidth / 2.0), top - (strokeWidth / 2.0)); - } else { - translated.translate(left, top); - } - effectiveTransform = matrix4ToCssTransform(translated); - } - final html.CssStyleDeclaration style = rectangle.style; - style - ..position = 'absolute' - ..transformOrigin = '0 0 0' - ..transform = effectiveTransform; - - final String cssColor = - paint.color == null ? '#000000' : colorToCssString(paint.color)!; - - if (paint.maskFilter != null) { - style.filter = 'blur(${paint.maskFilter!.webOnlySigma}px)'; - } - - if (isStroke) { - style - ..width = '${right - left - strokeWidth}px' - ..height = '${bottom - top - strokeWidth}px' - ..border = '${strokeWidth}px solid $cssColor'; - } else { - style - ..width = '${right - left}px' - ..height = '${bottom - top}px' - ..backgroundColor = cssColor; - } - - currentElement.append(rectangle); - return rectangle; - } - - @override - void drawRRect(ui.RRect rrect, SurfacePaintData paint) { - html.Element element = _drawRect(rrect.outerRect, paint, 'draw-rrect'); - element.style.borderRadius = '${rrect.blRadiusX.toStringAsFixed(3)}px'; - } - - @override - void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawOval(ui.Rect rect, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawPath(ui.Path path, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { - throw UnimplementedError(); - } - - @override - void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { - final html.Element paragraphElement = - _drawParagraphElement(paragraph as EngineParagraph, offset, transform: currentTransform); - currentElement.append(paragraphElement); - } - - @override - void drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { - throw UnimplementedError(); - } - - @override - void endOfPaint() { - // No reuse of elements yet to handle here. Noop. - } -} diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 9e1a3b50ff375..cc83ed4920853 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -2,8 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:ui/ui.dart' as ui; + +import '../engine.dart' show buildMode, registerHotRestartListener; +import 'browser_detection.dart'; +import 'canvaskit/initialization.dart'; +import 'host_node.dart'; +import 'keyboard_binding.dart'; +import 'platform_dispatcher.dart'; +import 'pointer_binding.dart'; +import 'semantics.dart'; +import 'text_editing/text_editing.dart'; +import 'util.dart'; +import 'window.dart'; class DomRenderer { DomRenderer() { @@ -13,8 +28,6 @@ class DomRenderer { reset(); - TextMeasurementService.initialize(rulerCacheCapacity: 10); - assert(() { _setupHotRestart(); return true; @@ -27,9 +40,12 @@ class DomRenderer { static const int vibrateHeavyImpact = 30; static const int vibrateSelectionClick = 10; + // The tag name for the root view of the flutter app (glass-pane) + static const String _glassPaneTagName = 'flt-glass-pane'; + /// Fires when browser language preferences change. static const html.EventStreamProvider languageChangeEvent = - const html.EventStreamProvider('languagechange'); + html.EventStreamProvider('languagechange'); /// Listens to window resize events. StreamSubscription? _resizeSubscription; @@ -44,27 +60,41 @@ class DomRenderer { /// Configures the screen, such as scaling. html.MetaElement? _viewportMeta; - /// The canvaskit script, downloaded from a CDN. Only created if - /// [experimentalUseSkia] is set to true. - html.ScriptElement? get canvasKitScript => _canvasKitScript; - html.ScriptElement? _canvasKitScript; - /// The element that contains the [sceneElement]. /// /// This element is created and inserted in the HTML DOM once. It is never /// removed or moved. However the [sceneElement] may be replaced inside it. /// - /// This element precedes the [glassPaneElement] so that it never receives - /// input events. All input events are processed by [glassPaneElement] and the - /// semantics tree. + /// This element is inserted after the [semanticsHostElement] so that + /// platform views take precedence in DOM event handling. html.Element? get sceneHostElement => _sceneHostElement; html.Element? _sceneHostElement; + /// A child element of body outside the shadowroot that hosts + /// global resources such svg filters and clip paths when using webkit. + html.Element? _resourcesHost; + + /// The element that contains the semantics tree. + /// + /// This element is created and inserted in the HTML DOM once. It is never + /// removed or moved. + /// + /// We render semantics inside the glasspane for proper focus and event + /// handling. If semantics is behind the glasspane, the phone will disable + /// focusing by touch, only by tabbing around the UI. If semantics is in + /// front of glasspane, then DOM event won't bubble up to the glasspane so + /// it can forward events to the framework. + /// + /// This element is inserted before the [semanticsHostElement] so that + /// platform views take precedence in DOM event handling. + html.Element? get semanticsHostElement => _semanticsHostElement; + html.Element? _semanticsHostElement; + /// The last scene element rendered by the [render] method. html.Element? get sceneElement => _sceneElement; html.Element? _sceneElement; - /// This is state persistant across hot restarts that indicates what + /// This is state persistent across hot restarts that indicates what /// to clear. We delay removal of old visible state to make the /// transition appear smooth. static const String _staleHotRestartStore = '__flutter_state'; @@ -78,12 +108,13 @@ class DomRenderer { /// This getter calls the `hasFocus` method of the `Document` interface. /// See for more details: /// https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus - bool? get windowHasFocus => js_util.callMethod(html.document, 'hasFocus', []); + bool get windowHasFocus => + js_util.callMethod(html.document, 'hasFocus', []) as bool; void _setupHotRestart() { // This persists across hot restarts to clear stale DOM. _staleHotRestartState = - js_util.getProperty(html.window, _staleHotRestartStore); + js_util.getProperty(html.window, _staleHotRestartStore) as List?; if (_staleHotRestartState == null) { _staleHotRestartState = []; js_util.setProperty( @@ -97,14 +128,13 @@ class DomRenderer { _glassPaneElement, _styleElement, _viewportMeta, - _canvasKitScript, ]); }); } void _clearOnHotRestart() { if (_staleHotRestartState!.isNotEmpty) { - for (html.Element? element in _staleHotRestartState!) { + for (final html.Element? element in _staleHotRestartState!) { element?.remove(); } _staleHotRestartState!.clear(); @@ -136,6 +166,10 @@ class DomRenderer { html.Element? get glassPaneElement => _glassPaneElement; html.Element? _glassPaneElement; + /// The [HostNode] of the [glassPaneElement], which contains the whole Flutter app. + HostNode? get glassPaneShadow => _glassPaneShadow; + HostNode? _glassPaneShadow; + final html.Element rootElement = html.document.body!; void addElementClass(html.Element element, String className) { @@ -172,7 +206,8 @@ class DomRenderer { js_util.setProperty(element, name, value); } - void setElementStyle(html.Element element, String name, String? value) { + static void setElementStyle( + html.Element element, String name, String? value) { if (value == null) { element.style.removeProperty(name); } else { @@ -180,6 +215,29 @@ class DomRenderer { } } + static void setClipPath(html.Element element, String? value) { + if (browserEngine == BrowserEngine.webkit) { + if (value == null) { + element.style.removeProperty('-webkit-clip-path'); + } else { + element.style.setProperty('-webkit-clip-path', value); + } + } + if (value == null) { + element.style.removeProperty('clip-path'); + } else { + element.style.setProperty('clip-path', value); + } + } + + static void setElementTransform(html.Element element, String transformValue) { + js_util.setProperty( + js_util.getProperty(element, 'style') as Object, + 'transform', + transformValue, + ); + } + void setText(html.Element element, String text) { element.text = text; } @@ -195,7 +253,8 @@ class DomRenderer { } void setThemeColor(ui.Color color) { - html.MetaElement? theme = html.document.querySelector('#flutterweb-theme') as html.MetaElement?; + html.MetaElement? theme = + html.document.querySelector('#flutterweb-theme') as html.MetaElement?; if (theme == null) { theme = html.MetaElement() ..id = 'flutterweb-theme' @@ -213,101 +272,29 @@ class DomRenderer { '$defaultFontStyle $defaultFontWeight ${defaultFontSize}px $defaultFontFamily'; void reset() { + final bool isWebKit = browserEngine == BrowserEngine.webkit; + _styleElement?.remove(); _styleElement = html.StyleElement(); + _resourcesHost?.remove(); + _resourcesHost = null; html.document.head!.append(_styleElement!); - final html.CssStyleSheet sheet = _styleElement!.sheet as html.CssStyleSheet; - final bool isWebKit = browserEngine == BrowserEngine.webkit; - final bool isFirefox = browserEngine == BrowserEngine.firefox; - // TODO(butterfly): use more efficient CSS selectors; descendant selectors - // are slow. More info: - // - // https://csswizardry.com/2011/09/writing-efficient-css-selectors/ - - // This undoes browser's default layout attributes for paragraphs. We - // compute paragraph layout ourselves. - if (isFirefox) { - // For firefox set line-height, otherwise textx at same font-size will - // measure differently in ruler. - sheet.insertRule( - 'flt-ruler-host p, flt-scene p ' - '{ margin: 0; line-height: 100%;}', - sheet.cssRules.length); - } else { - sheet.insertRule( - 'flt-ruler-host p, flt-scene p ' - '{ margin: 0; }', - sheet.cssRules.length); - } - - // This undoes browser's default painting and layout attributes of range - // input, which is used in semantics. - sheet.insertRule(''' -flt-semantics input[type=range] { - appearance: none; - -webkit-appearance: none; - width: 100%; - position: absolute; - border: none; - top: 0; - right: 0; - bottom: 0; - left: 0; -}''', sheet.cssRules.length); + final html.CssStyleSheet sheet = _styleElement!.sheet! as html.CssStyleSheet; + applyGlobalCssRulesToSheet( + sheet, + browserEngine: browserEngine, + hasAutofillOverlay: browserHasAutofillOverlay(), + ); - if (isWebKit) { - sheet.insertRule( - 'flt-semantics input[type=range]::-webkit-slider-thumb {' - ' -webkit-appearance: none;' - '}', - sheet.cssRules.length); - } - - if (isFirefox) { - sheet.insertRule( - 'input::-moz-selection {' - ' background-color: transparent;' - '}', - sheet.cssRules.length); - sheet.insertRule( - 'textarea::-moz-selection {' - ' background-color: transparent;' - '}', - sheet.cssRules.length); - } else { - // On iOS, the invisible semantic text field has a visible cursor and - // selection highlight. The following 2 CSS rules force everything to be - // transparent. - sheet.insertRule( - 'input::selection {' - ' background-color: transparent;' - '}', - sheet.cssRules.length); - sheet.insertRule( - 'textarea::selection {' - ' background-color: transparent;' - '}', - sheet.cssRules.length); - } - sheet.insertRule(''' -flt-semantics input, -flt-semantics textarea, -flt-semantics [contentEditable="true"] { - caret-color: transparent; -} -''', sheet.cssRules.length); + final html.BodyElement bodyElement = html.document.body!; - // By default on iOS, Safari would highlight the element that's being tapped - // on using gray background. This CSS rule disables that. - if (isWebKit) { - sheet.insertRule(''' -flt-glass-pane * { - -webkit-tap-highlight-color: transparent; -} -''', sheet.cssRules.length); - } + setElementAttribute( + bodyElement, + 'flt-renderer', + '${useCanvasKit ? 'canvaskit' : 'html'} (${flutterWebAutoDetect ? 'auto-selected' : 'requested explicitly'})', + ); + setElementAttribute(bodyElement, 'flt-build-mode', buildMode); - final html.BodyElement bodyElement = html.document.body!; setElementStyle(bodyElement, 'position', 'fixed'); setElementStyle(bodyElement, 'top', '0'); setElementStyle(bodyElement, 'right', '0'); @@ -334,11 +321,11 @@ flt-glass-pane * { setElementStyle(bodyElement, 'font', defaultCssFont); setElementStyle(bodyElement, 'color', 'red'); - // TODO(flutter_web): Disable spellcheck until changes in the framework and + // TODO(mdebbar): Disable spellcheck until changes in the framework and // engine are complete. bodyElement.spellcheck = false; - for (html.Element viewportMeta + for (final html.Element viewportMeta in html.document.head!.querySelectorAll('meta[name="viewport"]')) { if (assertionsEnabled) { // Filter out the meta tag that we ourselves placed on the page. This is @@ -370,7 +357,7 @@ flt-glass-pane * { // IMPORTANT: the glass pane element must come after the scene element in the DOM node list so // it can intercept input events. _glassPaneElement?.remove(); - final html.Element glassPaneElement = createElement('flt-glass-pane'); + final html.Element glassPaneElement = createElement(_glassPaneTagName); _glassPaneElement = glassPaneElement; glassPaneElement.style ..position = 'absolute' @@ -378,28 +365,45 @@ flt-glass-pane * { ..right = '0' ..bottom = '0' ..left = '0'; + + // This must be appended to the body, so we can create a host node properly. bodyElement.append(glassPaneElement); - _sceneHostElement = createElement('flt-scene-host'); + // Create a [HostNode] under the glass pane element, and attach everything + // there, instead of directly underneath the glass panel. + final HostNode glassPaneElementHostNode = _createHostNode(glassPaneElement); + _glassPaneShadow = glassPaneElementHostNode; // Don't allow the scene to receive pointer events. - _sceneHostElement!.style.pointerEvents = 'none'; + _sceneHostElement = createElement('flt-scene-host') + ..style.pointerEvents = 'none'; - glassPaneElement.append(_sceneHostElement!); + final html.Element semanticsHostElement = + createElement('flt-semantics-host'); + semanticsHostElement.style + ..position = 'absolute' + ..transformOrigin = '0 0 0'; + _semanticsHostElement = semanticsHostElement; + updateSemanticsScreenProperties(); - final html.Element _accesibilityPlaceholder = EngineSemanticsOwner + final html.Element _accessibilityPlaceholder = EngineSemanticsOwner .instance.semanticsHelper - .prepareAccesibilityPlaceholder(); - - // Insert the semantics placeholder after the scene host. For all widgets - // in the scene, except for platform widgets, the scene host will pass the - // pointer events through to the semantics tree. However, for platform - // views, the pointer events will not pass through, and will be handled - // by the platform view. - glassPaneElement - .insertBefore(_accesibilityPlaceholder, _sceneHostElement); + .prepareAccessibilityPlaceholder(); + + glassPaneElementHostNode.nodes.addAll([ + semanticsHostElement, + _accessibilityPlaceholder, + _sceneHostElement!, + ]); + + // When debugging semantics, make the scene semi-transparent so that the + // semantics tree is visible. + if (debugShowSemanticsNodes) { + _sceneHostElement!.style.opacity = '0.3'; + } PointerBinding.initInstance(glassPaneElement); + KeyboardBinding.initInstance(glassPaneElement); // Hide the DOM nodes used to render the scene from accessibility, because // the accessibility tree is built from the SemanticsNode tree as a parallel @@ -407,11 +411,11 @@ flt-glass-pane * { setElementAttribute(_sceneHostElement!, 'aria-hidden', 'true'); if (html.window.visualViewport == null && isWebKit) { - // Safari sometimes gives us bogus innerWidth/innerHeight values when the - // page loads. When it changes the values to correct ones it does not - // notify of the change via `onResize`. As a workaround, we setup a - // temporary periodic timer that polls innerWidth and triggers the - // resizeListener so that the framework can react to the change. + // Older Safari versions sometimes give us bogus innerWidth/innerHeight + // values when the page loads. When it changes the values to correct ones + // it does not notify of the change via `onResize`. As a workaround, we + // set up a temporary periodic timer that polls innerWidth and triggers + // the resizeListener so that the framework can react to the change. // // Safari 13 has implemented visualViewport API so it doesn't need this // timer. @@ -419,7 +423,7 @@ flt-glass-pane * { // VisualViewport API is not enabled in Firefox as well. On the other hand // Firefox returns correct values for innerHeight, innerWidth. // Firefox also triggers html.window.onResize therefore we don't need this - // timer setup for Firefox. + // timer to be set up for Firefox. final int initialInnerWidth = html.window.innerWidth!; // Counts how many times we checked screen size. We check up to 5 times. int checkCount = 0; @@ -436,48 +440,60 @@ flt-glass-pane * { }); } - if (experimentalUseSkia) { - _canvasKitScript?.remove(); - _canvasKitScript = html.ScriptElement(); - _canvasKitScript!.src = canvasKitBaseUrl + 'canvaskit.js'; - html.document.head!.append(_canvasKitScript!); - } - if (html.window.visualViewport != null) { _resizeSubscription = html.window.visualViewport!.onResize.listen(_metricsDidChange); } else { _resizeSubscription = html.window.onResize.listen(_metricsDidChange); } - _localeSubscription = languageChangeEvent.forTarget(html.window) - .listen(_languageDidChange); - window._updateLocales(); + _localeSubscription = + languageChangeEvent.forTarget(html.window).listen(_languageDidChange); + EnginePlatformDispatcher.instance.updateLocales(); + } + + // Creates a [HostNode] into a `root` [html.Element]. + HostNode _createHostNode(html.Element root) { + if (js_util.getProperty(root, 'attachShadow') != null) { + return ShadowDomHostNode(root); + } else { + // attachShadow not available, fall back to ElementHostNode. + return ElementHostNode(root); + } + } + + /// The framework specifies semantics in physical pixels, but CSS uses + /// logical pixels. To compensate, we inject an inverse scale at the root + /// level. + void updateSemanticsScreenProperties() { + _semanticsHostElement!.style.transform = + 'scale(${1 / html.window.devicePixelRatio})'; } /// Called immediately after browser window metrics change. /// /// When there is a text editing going on in mobile devices, do not change /// the physicalSize, change the [window.viewInsets]. See: - /// https://api.flutter.dev/flutter/dart-ui/Window/viewInsets.html - /// https://api.flutter.dev/flutter/dart-ui/Window/physicalSize.html + /// https://api.flutter.dev/flutter/dart-ui/FlutterView/viewInsets.html + /// https://api.flutter.dev/flutter/dart-ui/FlutterView/physicalSize.html /// /// Note: always check for rotations for a mobile device. Update the physical /// size if the change is caused by a rotation. void _metricsDidChange(html.Event? event) { - if(isMobile && !window.isRotation() && textEditing.isEditing) { - window.computeOnScreenKeyboardInsets(); - window.invokeOnMetricsChanged(); + updateSemanticsScreenProperties(); + if (isMobile && !window.isRotation() && textEditing.isEditing) { + window.computeOnScreenKeyboardInsets(true); + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); } else { - window._computePhysicalSize(); + window.computePhysicalSize(); // When physical size changes this value has to be recalculated. - window.computeOnScreenKeyboardInsets(); - window.invokeOnMetricsChanged(); + window.computeOnScreenKeyboardInsets(false); + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); } } /// Called immediately after browser window language change. void _languageDidChange(html.Event event) { - window._updateLocales(); + EnginePlatformDispatcher.instance.updateLocales(); if (ui.window.onLocaleChanged != null) { ui.window.onLocaleChanged!(); } @@ -497,13 +513,20 @@ flt-glass-pane * { static bool? _ellipseFeatureDetected; /// Draws CanvasElement ellipse with fallback. - static void ellipse(html.CanvasRenderingContext2D context, - double centerX, double centerY, double radiusX, double radiusY, - double rotation, double startAngle, double endAngle, bool antiClockwise) { + static void ellipse( + html.CanvasRenderingContext2D context, + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise) { _ellipseFeatureDetected ??= js_util.getProperty(context, 'ellipse') != null; if (_ellipseFeatureDetected!) { - context.ellipse(centerX, centerY, radiusX, radiusY, - rotation, startAngle, endAngle, antiClockwise); + context.ellipse(centerX, centerY, radiusX, radiusY, rotation, startAngle, + endAngle, antiClockwise); } else { context.save(); context.translate(centerX, centerY); @@ -519,9 +542,11 @@ flt-glass-pane * { static const String orientationLockTypeLandscape = 'landscape'; static const String orientationLockTypePortrait = 'portrait'; static const String orientationLockTypePortraitPrimary = 'portrait-primary'; - static const String orientationLockTypePortraitSecondary = 'portrait-secondary'; + static const String orientationLockTypePortraitSecondary = + 'portrait-secondary'; static const String orientationLockTypeLandscapePrimary = 'landscape-primary'; - static const String orientationLockTypeLandscapeSecondary = 'landscape-secondary'; + static const String orientationLockTypeLandscapeSecondary = + 'landscape-secondary'; /// Sets preferred screen orientation. /// @@ -533,17 +558,17 @@ flt-glass-pane * { /// defer to the operating system default. /// /// See w3c screen api: https://www.w3.org/TR/screen-orientation/ - Future setPreferredOrientation(List? orientations) { + Future setPreferredOrientation(List orientations) { final html.Screen screen = html.window.screen!; - if (!_unsafeIsNull(screen)) { - final html.ScreenOrientation? screenOrientation = - screen.orientation; - if (!_unsafeIsNull(screenOrientation)) { - if (orientations!.isEmpty) { + if (!unsafeIsNull(screen)) { + final html.ScreenOrientation? screenOrientation = screen.orientation; + if (!unsafeIsNull(screenOrientation)) { + if (orientations.isEmpty) { screenOrientation!.unlock(); - return Future.value(true); + return Future.value(true); } else { - String? lockType = _deviceOrientationToLockType(orientations.first); + final String? lockType = + _deviceOrientationToLockType(orientations.first as String?); if (lockType != null) { final Completer completer = Completer(); try { @@ -555,7 +580,7 @@ flt-glass-pane * { completer.complete(false); }); } catch (_) { - return Future.value(false); + return Future.value(false); } return completer.future; } @@ -563,12 +588,12 @@ flt-glass-pane * { } } // API is not supported on this browser return false. - return Future.value(false); + return Future.value(false); } // Converts device orientation to w3c OrientationLockType enum. - static String? _deviceOrientationToLockType(String deviceOrientation) { - switch(deviceOrientation) { + static String? _deviceOrientationToLockType(String? deviceOrientation) { + switch (deviceOrientation) { case 'DeviceOrientation.portraitUp': return orientationLockTypePortraitPrimary; case 'DeviceOrientation.landscapeLeft': @@ -585,7 +610,7 @@ flt-glass-pane * { /// The element corresponding to the only child of the root surface. html.Element? get _rootApplicationElement { final html.Element lastElement = rootElement.children.last; - for (html.Element child in lastElement.children) { + for (final html.Element child in lastElement.children) { if (child.tagName == 'FLT-SCENE') { return child; } @@ -593,6 +618,33 @@ flt-glass-pane * { return null; } + /// Add an element as a global resource to be referenced by CSS. + /// + /// This call create a global resource host element on demand and either + /// place it as first element of body(webkit), or as a child of + /// glass pane element for other browsers to make sure url resolution + /// works correctly when content is inside a shadow root. + void addResource(html.Element element) { + final bool isWebKit = browserEngine == BrowserEngine.webkit; + if (_resourcesHost == null) { + _resourcesHost = html.DivElement() + ..style.visibility = 'hidden'; + if (isWebKit) { + final html.Node bodyNode = html.document.body!; + bodyNode.insertBefore(_resourcesHost!, bodyNode.firstChild); + } else { + _glassPaneShadow!.node.insertBefore( + _resourcesHost!, _glassPaneShadow!.node.firstChild); + } + } + _resourcesHost!.append(element); + } + + /// Removes a global resource element. + void removeResource(html.Element? element) { + element?.remove(); + } + /// Provides haptic feedback. void vibrate(int durationMs) { final html.Navigator navigator = html.window.navigator; @@ -621,6 +673,116 @@ flt-glass-pane * { void debugPlainTextLayout() => _debugFrameStatistics!.plainTextLayouts++; } +// Applies the required global CSS to an incoming [html.CssStyleSheet] `sheet`. +void applyGlobalCssRulesToSheet( + html.CssStyleSheet sheet, { + required BrowserEngine browserEngine, + required bool hasAutofillOverlay, + String glassPaneTagName = DomRenderer._glassPaneTagName, +}) { + final bool isWebKit = browserEngine == BrowserEngine.webkit; + final bool isFirefox = browserEngine == BrowserEngine.firefox; + // TODO(web): use more efficient CSS selectors; descendant selectors are slow. + // More info: https://csswizardry.com/2011/09/writing-efficient-css-selectors + + // This undoes browser's default layout attributes for paragraphs. We + // compute paragraph layout ourselves. + if (isFirefox) { + // For firefox set line-height, otherwise textx at same font-size will + // measure differently in ruler. + sheet.insertRule( + 'flt-ruler-host p, flt-scene p ' + '{ margin: 0; line-height: 100%;}', + sheet.cssRules.length); + } else { + sheet.insertRule( + 'flt-ruler-host p, flt-scene p ' + '{ margin: 0; }', + sheet.cssRules.length); + } + + // This undoes browser's default painting and layout attributes of range + // input, which is used in semantics. + sheet.insertRule(''' +flt-semantics input[type=range] { +appearance: none; +-webkit-appearance: none; +width: 100%; +position: absolute; +border: none; +top: 0; +right: 0; +bottom: 0; +left: 0; +}''', sheet.cssRules.length); + + if (isWebKit) { + sheet.insertRule( + 'flt-semantics input[type=range]::-webkit-slider-thumb {' + ' -webkit-appearance: none;' + '}', + sheet.cssRules.length); + } + + if (isFirefox) { + sheet.insertRule( + 'input::-moz-selection {' + ' background-color: transparent;' + '}', + sheet.cssRules.length); + sheet.insertRule( + 'textarea::-moz-selection {' + ' background-color: transparent;' + '}', + sheet.cssRules.length); + } else { + // On iOS, the invisible semantic text field has a visible cursor and + // selection highlight. The following 2 CSS rules force everything to be + // transparent. + sheet.insertRule( + 'input::selection {' + ' background-color: transparent;' + '}', + sheet.cssRules.length); + sheet.insertRule( + 'textarea::selection {' + ' background-color: transparent;' + '}', + sheet.cssRules.length); + } + sheet.insertRule(''' +flt-semantics input, +flt-semantics textarea, +flt-semantics [contentEditable="true"] { +caret-color: transparent; +} +''', sheet.cssRules.length); + + // By default on iOS, Safari would highlight the element that's being tapped + // on using gray background. This CSS rule disables that. + if (isWebKit) { + sheet.insertRule(''' +$glassPaneTagName * { +-webkit-tap-highlight-color: transparent; +} +''', sheet.cssRules.length); + } + + // This css prevents an autofill overlay brought by the browser during + // text field autofill by delaying the transition effect. + // See: https://github.com/flutter/flutter/issues/61132. + if (browserHasAutofillOverlay()) { + sheet.insertRule(''' +.transparentTextEditing:-webkit-autofill, +.transparentTextEditing:-webkit-autofill:hover, +.transparentTextEditing:-webkit-autofill:focus, +.transparentTextEditing:-webkit-autofill:active { + -webkit-transition-delay: 99999s; +} +''', sheet.cssRules.length); + } +} + /// Miscellaneous statistics collecting during a single frame's execution. /// /// This is useful when profiling the app. This class should only be used when @@ -660,8 +822,9 @@ Frame statistics: } } -// TODO(yjbanov): Replace this with an explicit initialization function. The -// lazy initialization of statics makes it very unpredictable, as -// the constructor has side-effects. /// Singleton DOM renderer. -final DomRenderer domRenderer = DomRenderer(); +DomRenderer get domRenderer => ensureDomRendererInitialized(); + +/// Initializes the [DomRenderer], if it's not already initialized. +DomRenderer ensureDomRendererInitialized() => _domRenderer ??= DomRenderer(); +DomRenderer? _domRenderer; diff --git a/lib/web_ui/lib/src/engine/engine_canvas.dart b/lib/web_ui/lib/src/engine/engine_canvas.dart index 5b0f7d31d8167..258ced6479440 100644 --- a/lib/web_ui/lib/src/engine/engine_canvas.dart +++ b/lib/web_ui/lib/src/engine/engine_canvas.dart @@ -2,8 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +// For member documentation see https://api.flutter.dev/flutter/dart-ui/Canvas-class.html +// ignore_for_file: public_member_api_docs + +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'html/painting.dart'; +import 'html/render_vertices.dart'; +import 'text/paragraph.dart'; +import 'util.dart'; +import 'vector_math.dart'; /// Defines canvas interface common across canvases that the [SceneBuilder] /// renders to. @@ -33,7 +44,7 @@ abstract class EngineCanvas { void transform(Float32List matrix4); - void clipRect(ui.Rect rect); + void clipRect(ui.Rect rect, ui.ClipOp clipOp); void clipRRect(ui.RRect rrect); @@ -93,29 +104,29 @@ Matrix4 transformWithOffset(Matrix4 transform, ui.Offset offset) { return effectiveTransform; } -class _SaveStackEntry { - _SaveStackEntry({ +class SaveStackEntry { + SaveStackEntry({ required this.transform, required this.clipStack, }); final Matrix4 transform; - final List<_SaveClipEntry>? clipStack; + final List? clipStack; } /// Tagged union of clipping parameters used for canvas. -class _SaveClipEntry { +class SaveClipEntry { final ui.Rect? rect; final ui.RRect? rrect; final ui.Path? path; final Matrix4 currentTransform; - _SaveClipEntry.rect(this.rect, this.currentTransform) + SaveClipEntry.rect(this.rect, this.currentTransform) : rrect = null, path = null; - _SaveClipEntry.rrect(this.rrect, this.currentTransform) + SaveClipEntry.rrect(this.rrect, this.currentTransform) : rect = null, path = null; - _SaveClipEntry.path(this.path, this.currentTransform) + SaveClipEntry.path(this.path, this.currentTransform) : rect = null, rrect = null; } @@ -125,11 +136,11 @@ class _SaveClipEntry { mixin SaveStackTracking on EngineCanvas { static final Vector3 _unitZ = Vector3(0.0, 0.0, 1.0); - final List<_SaveStackEntry> _saveStack = <_SaveStackEntry>[]; + final List _saveStack = []; /// The stack that maintains clipping operations used when text is painted /// onto bitmap canvas but is composited as separate element. - List<_SaveClipEntry>? _clipStack; + List? _clipStack; /// Returns whether there are active clipping regions on the canvas. bool get isClipped => _clipStack != null; @@ -154,10 +165,10 @@ mixin SaveStackTracking on EngineCanvas { /// Classes that override this method must call `super.save()`. @override void save() { - _saveStack.add(_SaveStackEntry( + _saveStack.add(SaveStackEntry( transform: _currentTransform.clone(), clipStack: - _clipStack == null ? null : List<_SaveClipEntry>.from(_clipStack!), + _clipStack == null ? null : List.from(_clipStack!), )); } @@ -169,7 +180,7 @@ mixin SaveStackTracking on EngineCanvas { if (_saveStack.isEmpty) { return; } - final _SaveStackEntry entry = _saveStack.removeLast(); + final SaveStackEntry entry = _saveStack.removeLast(); _currentTransform = entry.transform; _clipStack = entry.clipStack; } @@ -222,9 +233,9 @@ mixin SaveStackTracking on EngineCanvas { /// /// Classes that override this method must call `super.clipRect()`. @override - void clipRect(ui.Rect rect) { - _clipStack ??= <_SaveClipEntry>[]; - _clipStack!.add(_SaveClipEntry.rect(rect, _currentTransform.clone())); + void clipRect(ui.Rect rect, ui.ClipOp op) { + _clipStack ??= []; + _clipStack!.add(SaveClipEntry.rect(rect, _currentTransform.clone())); } /// Adds a round rectangle to clipping stack. @@ -232,8 +243,8 @@ mixin SaveStackTracking on EngineCanvas { /// Classes that override this method must call `super.clipRRect()`. @override void clipRRect(ui.RRect rrect) { - _clipStack ??= <_SaveClipEntry>[]; - _clipStack!.add(_SaveClipEntry.rrect(rrect, _currentTransform.clone())); + _clipStack ??= []; + _clipStack!.add(SaveClipEntry.rrect(rrect, _currentTransform.clone())); } /// Adds a path to clipping stack. @@ -241,28 +252,19 @@ mixin SaveStackTracking on EngineCanvas { /// Classes that override this method must call `super.clipPath()`. @override void clipPath(ui.Path path) { - _clipStack ??= <_SaveClipEntry>[]; - _clipStack!.add(_SaveClipEntry.path(path, _currentTransform.clone())); + _clipStack ??= []; + _clipStack!.add(SaveClipEntry.path(path, _currentTransform.clone())); } } -html.Element _drawParagraphElement( +html.Element drawParagraphElement( EngineParagraph paragraph, ui.Offset offset, { Matrix4? transform, }) { - assert(paragraph._isLaidOut); - - final html.Element paragraphElement = paragraph._paragraphElement.clone(true) as html.Element; + assert(paragraph.isLaidOut); - final html.CssStyleDeclaration paragraphStyle = paragraphElement.style; - paragraphStyle - ..position = 'absolute' - ..whiteSpace = 'pre-wrap' - ..overflowWrap = 'break-word' - ..overflow = 'hidden' - ..height = '${paragraph.height}px' - ..width = '${paragraph.width}px'; + final html.HtmlElement paragraphElement = paragraph.toDomElement(); if (transform != null) { setElementTransform( @@ -270,16 +272,6 @@ html.Element _drawParagraphElement( transformWithOffset(transform, offset).storage, ); } - - final ParagraphGeometricStyle style = paragraph._geometricStyle; - - // TODO(flutter_web): https://github.com/flutter/flutter/issues/33223 - if (style.ellipsis != null && - (style.maxLines == null || style.maxLines == 1)) { - paragraphStyle - ..whiteSpace = 'pre' - ..textOverflow = 'ellipsis'; - } return paragraphElement; } diff --git a/lib/web_ui/lib/src/engine/font_change_util.dart b/lib/web_ui/lib/src/engine/font_change_util.dart new file mode 100644 index 0000000000000..14cfcf0a5bf1d --- /dev/null +++ b/lib/web_ui/lib/src/engine/font_change_util.dart @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'platform_dispatcher.dart'; +import 'services.dart'; + +final ByteData? _fontChangeMessage = + const JSONMessageCodec().encodeMessage( + {'type': 'fontsChange'}); + +// Font load callbacks will typically arrive in sequence, we want to prevent +// sendFontChangeMessage of causing multiple synchronous rebuilds. +// This flag ensures we properly schedule a single call to framework. +bool _fontChangeScheduled = false; + +FutureOr sendFontChangeMessage() async { + if (!_fontChangeScheduled) { + _fontChangeScheduled = true; + // Batch updates into next animationframe. + html.window.requestAnimationFrame((num _) { + _fontChangeScheduled = false; + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/system', + _fontChangeMessage, + (_) {}, + ); + }); + } +} diff --git a/lib/web_ui/lib/src/engine/frame_reference.dart b/lib/web_ui/lib/src/engine/frame_reference.dart index 0df43a53c1b44..c711b18100e14 100644 --- a/lib/web_ui/lib/src/engine/frame_reference.dart +++ b/lib/web_ui/lib/src/engine/frame_reference.dart @@ -2,15 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - /// A monotonically increasing frame number being rendered. /// /// Used for debugging only. -int _debugFrameNumber = 1; +int debugFrameNumber = 1; -List> _frameReferences = >[]; +List> frameReferences = >[]; /// A temporary reference to a value of type [V]. /// @@ -22,7 +19,7 @@ List> _frameReferences = >[]; class FrameReference { /// Creates a frame reference to a value. FrameReference([this.value]) { - _frameReferences.add(this); + frameReferences.add(this); } /// The current value of this reference. @@ -49,8 +46,8 @@ class CrossFrameCache { void commitFrame() { // Evict unused items from prior frame. if (_reusablePool != null) { - for (List<_CrossFrameCacheItem> items in _reusablePool!.values) { - for (_CrossFrameCacheItem item in items) { + for (final List<_CrossFrameCacheItem> items in _reusablePool!.values) { + for (final _CrossFrameCacheItem item in items) { item.evict(); } } @@ -69,8 +66,8 @@ class CrossFrameCache { } void _addToCache(String key, _CrossFrameCacheItem item) { - _cache ??= {}; - (_cache![key] ??= [])..add(item); + _cache ??= >>{}; + (_cache![key] ??= <_CrossFrameCacheItem>[]).add(item); } /// Given a key, consumes an item that has been cached in a prior frame. @@ -78,11 +75,11 @@ class CrossFrameCache { if (_reusablePool == null) { return null; } - List<_CrossFrameCacheItem>? items = _reusablePool![key]; + final List<_CrossFrameCacheItem>? items = _reusablePool![key]; if (items == null || items.isEmpty) { return null; } - _CrossFrameCacheItem item = items.removeAt(0); + final _CrossFrameCacheItem item = items.removeAt(0); _addToCache(key, item); return item.value; } diff --git a/lib/web_ui/lib/src/engine/history.dart b/lib/web_ui/lib/src/engine/history.dart deleted file mode 100644 index 59e1ba5fddf6d..0000000000000 --- a/lib/web_ui/lib/src/engine/history.dart +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.10 -part of engine; - -/// An abstract class that provides the API for [EngineWindow] to delegate its -/// navigating events. -/// -/// Subclasses will have access to [BrowserHistory.locationStrategy] to -/// interact with the html browser history and should come up with their own -/// ways to manage the states in the browser history. -/// -/// There should only be one global instance among all all subclasses. -/// -/// See also: -/// -/// * [SingleEntryBrowserHistory]: which creates a single fake browser history -/// entry and delegates all browser navigating events to the flutter -/// framework. -/// * [MultiEntriesBrowserHistory]: which creates a set of states that records -/// the navigating events happened in the framework. -abstract class BrowserHistory { - late ui.VoidCallback _unsubscribe; - - /// The strategy to interact with html browser history. - LocationStrategy? get locationStrategy => _locationStrategy; - LocationStrategy? _locationStrategy; - /// Updates the strategy. - /// - /// This method will also remove any previous modifications to the html - /// browser history and start anew. - Future setLocationStrategy(LocationStrategy? strategy) async { - if (strategy != _locationStrategy) { - await _tearoffStrategy(_locationStrategy); - _locationStrategy = strategy; - await _setupStrategy(_locationStrategy); - } - } - - Future _setupStrategy(LocationStrategy? strategy) async { - if (strategy == null) { - return; - } - _unsubscribe = strategy.onPopState(onPopState as dynamic Function(html.Event)); - await setup(); - } - - Future _tearoffStrategy(LocationStrategy? strategy) async { - if (strategy == null) { - return; - } - _unsubscribe(); - - await tearDown(); - } - - /// Exit this application and return to the previous page. - Future exit() async { - if (_locationStrategy != null) { - await _tearoffStrategy(_locationStrategy); - // Now the history should be in the original state, back one more time to - // exit the application. - await _locationStrategy!.back(); - _locationStrategy = null; - } - } - - /// This method does the same thing as the browser back button. - Future back() { - if (locationStrategy != null) { - return locationStrategy!.back(); - } - return Future.value(); - } - - /// The path of the current location of the user's browser. - String get currentPath => locationStrategy?.path ?? '/'; - - /// The state of the current location of the user's browser. - dynamic get currentState => locationStrategy?.state; - - /// Update the url with the given [routeName] and [state]. - void setRouteName(String? routeName, {dynamic? state}); - - /// A callback method to handle browser backward or forward buttons. - /// - /// Subclasses should send appropriate system messages to update the flutter - /// applications accordingly. - void onPopState(covariant html.PopStateEvent event); - - /// Sets up any prerequisites to use this browser history class. - Future setup() => Future.value(); - - /// Restore any modifications to the html browser history during the lifetime - /// of this class. - Future tearDown() => Future.value(); -} - -/// A browser history class that creates a set of browser history entries to -/// support browser backward and forward button natively. -/// -/// This class pushes a browser history entry every time the framework reports -/// a route change and sends a `pushRouteInformation` method call to the -/// framework when the browser jumps to a specific browser history entry. -/// -/// The web engine uses this class to manage its browser history when the -/// framework uses a Router for routing. -/// -/// See also: -/// -/// * [SingleEntryBrowserHistory], which is used when the framework does not use -/// a Router for routing. -class MultiEntriesBrowserHistory extends BrowserHistory { - late int _lastSeenSerialCount; - int get _currentSerialCount { - if (_hasSerialCount(currentState)) { - return currentState['serialCount'] as int; - } - return 0; - } - - dynamic _tagWithSerialCount(dynamic originialState, int count) { - return { - 'serialCount': count, - 'state': originialState, - }; - } - - bool _hasSerialCount(dynamic state) { - return state is Map && state['serialCount'] != null; - } - - @override - void setRouteName(String? routeName, {dynamic? state}) { - if (locationStrategy != null) { - assert(routeName != null); - _lastSeenSerialCount += 1; - locationStrategy!.pushState( - _tagWithSerialCount(state, _lastSeenSerialCount), - 'flutter', - routeName!, - ); - } - } - - @override - void onPopState(covariant html.PopStateEvent event) { - assert(locationStrategy != null); - // May be a result of direct url access while the flutter application is - // already running. - if (!_hasSerialCount(event.state)) { - // In this case we assume this will be the next history entry from the - // last seen entry. - locationStrategy!.replaceState( - _tagWithSerialCount(event.state, _lastSeenSerialCount + 1), - 'flutter', - currentPath); - } - _lastSeenSerialCount = _currentSerialCount; - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall( - MethodCall('pushRouteInformation', { - 'location': currentPath, - 'state': event.state?['state'], - }) - ), - (_) {}, - ); - } - } - - @override - Future setup() { - if (!_hasSerialCount(currentState)) { - locationStrategy!.replaceState( - _tagWithSerialCount(currentState, 0), - 'flutter', - currentPath - ); - } - // If we retore from a page refresh, the _currentSerialCount may not be 0. - _lastSeenSerialCount = _currentSerialCount; - return Future.value(); - } - - @override - Future tearDown() async { - // Restores the html browser history. - assert(_hasSerialCount(currentState)); - int backCount = _currentSerialCount; - if (backCount > 0) { - await locationStrategy!.back(count: backCount); - } - // Unwrap state. - assert(_hasSerialCount(currentState) && _currentSerialCount == 0); - locationStrategy!.replaceState( - currentState['state'], - 'flutter', - currentPath, - ); - } -} - -/// The browser history class is responsible for integrating Flutter Web apps -/// with the browser history so that the back button works as expected. -/// -/// It does that by always keeping a single entry (conventionally called the -/// "flutter" entry) at the top of the browser history. That way, the browser's -/// back button always triggers a `popstate` event and never closes the app (we -/// close the app programmatically by calling [SystemNavigator.pop] when there -/// are no more app routes to be popped). -/// -/// The web engine uses this class when the framework does not use Router for -/// routing, and it does not support browser forward button. -/// -/// See also: -/// -/// * [MultiEntriesBrowserHistory], which is used when the framework uses a -/// Router for routing. -class SingleEntryBrowserHistory extends BrowserHistory { - static const MethodCall _popRouteMethodCall = MethodCall('popRoute'); - static const String _kFlutterTag = 'flutter'; - static const String _kOriginTag = 'origin'; - - Map _wrapOriginState(dynamic state) { - return {_kOriginTag: true, 'state': state}; - } - dynamic _unwrapOriginState(dynamic state) { - assert(_isOriginEntry(state)); - final Map originState = state as Map; - return originState['state']; - } - Map _flutterState = {_kFlutterTag: true}; - - /// The origin entry is the history entry that the Flutter app landed on. It's - /// created by the browser when the user navigates to the url of the app. - bool _isOriginEntry(dynamic state) { - return state is Map && state[_kOriginTag] == true; - } - - /// The flutter entry is a history entry that we maintain on top of the origin - /// entry. It allows us to catch popstate events when the user hits the back - /// button. - bool _isFlutterEntry(dynamic state) { - return state is Map && state[_kFlutterTag] == true; - } - - @override - void setRouteName(String? routeName, {dynamic? state}) { - if (locationStrategy != null) { - _setupFlutterEntry(locationStrategy!, replace: true, path: routeName); - } - } - - String? _userProvidedRouteName; - @override - void onPopState(covariant html.PopStateEvent event) { - if (_isOriginEntry(event.state)) { - _setupFlutterEntry(_locationStrategy!); - - // 2. Send a 'popRoute' platform message so the app can handle it accordingly. - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(_popRouteMethodCall), - (_) {}, - ); - } - } else if (_isFlutterEntry(event.state)) { - // We get into this scenario when the user changes the url manually. It - // causes a new entry to be pushed on top of our "flutter" one. When this - // happens it first goes to the "else" section below where we capture the - // path into `_userProvidedRouteName` then trigger a history back which - // brings us here. - assert(_userProvidedRouteName != null); - - final String newRouteName = _userProvidedRouteName!; - _userProvidedRouteName = null; - - // Send a 'pushRoute' platform message so the app handles it accordingly. - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall( - MethodCall('pushRoute', newRouteName), - ), - (_) {}, - ); - } - } else { - // The user has pushed a new entry on top of our flutter entry. This could - // happen when the user modifies the hash part of the url directly, for - // example. - - // 1. We first capture the user's desired path. - _userProvidedRouteName = currentPath; - - // 2. Then we remove the new entry. - // This will take us back to our "flutter" entry and it causes a new - // popstate event that will be handled in the "else if" section above. - _locationStrategy!.back(); - } - } - - /// This method should be called when the Origin Entry is active. It just - /// replaces the state of the entry so that we can recognize it later using - /// [_isOriginEntry] inside [_popStateListener]. - void _setupOriginEntry(LocationStrategy strategy) { - assert(strategy != null); // ignore: unnecessary_null_comparison - strategy.replaceState(_wrapOriginState(currentState), 'origin', ''); - } - - /// This method is used manipulate the Flutter Entry which is always the - /// active entry while the Flutter app is running. - void _setupFlutterEntry( - LocationStrategy strategy, { - bool replace = false, - String? path, - }) { - assert(strategy != null); // ignore: unnecessary_null_comparison - path ??= currentPath; - if (replace) { - strategy.replaceState(_flutterState, 'flutter', path); - } else { - strategy.pushState(_flutterState, 'flutter', path); - } - } - - @override - Future setup() { - final String path = currentPath; - if (_isFlutterEntry(html.window.history.state)) { - // This could happen if the user, for example, refreshes the page. They - // will land directly on the "flutter" entry, so there's no need to setup - // the "origin" and "flutter" entries, we can safely assume they are - // already setup. - } else { - _setupOriginEntry(locationStrategy!); - _setupFlutterEntry(locationStrategy!, replace: false, path: path); - } - return Future.value(); - } - - @override - Future tearDown() async { - if (locationStrategy != null) { - // We need to remove the flutter entry that we pushed in setup. - await locationStrategy!.back(); - // Restores original state. - locationStrategy!.replaceState(_unwrapOriginState(currentState), 'flutter', currentPath); - } - } -} diff --git a/lib/web_ui/lib/src/engine/host_node.dart b/lib/web_ui/lib/src/engine/host_node.dart new file mode 100644 index 0000000000000..f8f41c2b3cd9f --- /dev/null +++ b/lib/web_ui/lib/src/engine/host_node.dart @@ -0,0 +1,188 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'browser_detection.dart'; +import 'dom_renderer.dart'; +import 'text_editing/text_editing.dart'; + +/// The interface required to host a flutter app in the DOM, and its tests. +/// +/// Consider this as the intersection in functionality between [html.ShadowRoot] +/// (preferred Flutter rendering method) and [html.Document] (fallback). +/// +/// Not to be confused with [html.DocumentOrShadowRoot]. +abstract class HostNode { + /// Retrieves the [html.Element] that currently has focus. + /// + /// See: + /// * [Document.activeElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement) + html.Element? get activeElement; + + /// Adds a node to the end of the child [nodes] list of this node. + /// + /// If the node already exists in this document, it will be removed from its + /// current parent node, then added to this node. + /// + /// This method is more efficient than `nodes.add`, and is the preferred + /// way of appending a child node. + /// + /// See: + /// * [Node.appendChild](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild) + html.Node append(html.Node node); + + /// Returns true if this node contains the specified node. + /// See: + /// * [Node.contains](https://developer.mozilla.org/en-US/docs/Web/API/Node.contains) + bool contains(html.Node? other); + + /// Returns the currently wrapped [html.Node]. + html.Node get node; + + /// A modifiable list of this node's children. + List get nodes; + + /// Finds the first descendant element of this document that matches the + /// specified group of selectors. + /// + /// [selectors] should be a string using CSS selector syntax. + /// + /// ```dart + /// var element1 = document.querySelector('.className'); + /// var element2 = document.querySelector('#id'); + /// ``` + /// + /// For details about CSS selector syntax, see the + /// [CSS selector specification](http://www.w3.org/TR/css3-selectors/). + /// + /// See: + /// * [Document.querySelector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) + html.Element? querySelector(String selectors); + + /// Finds all descendant elements of this document that match the specified + /// group of selectors. + /// + /// [selectors] should be a string using CSS selector syntax. + /// + /// ```dart + /// var items = document.querySelectorAll('.itemClassName'); + /// ``` + /// + /// For details about CSS selector syntax, see the + /// [CSS selector specification](http://www.w3.org/TR/css3-selectors/). + /// + /// See: + /// * [Document.querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) + List querySelectorAll(String selectors); +} + +/// A [HostNode] implementation, backed by a [html.ShadowRoot]. +/// +/// This is the preferred flutter implementation, but it might not be supported +/// by all browsers yet. +/// +/// The constructor might throw when calling `attachShadow`, if ShadowDOM is not +/// supported in the current environment. In this case, a fallback [ElementHostNode] +/// should be created instead. +class ShadowDomHostNode implements HostNode { + late html.ShadowRoot _shadow; + + /// Build a HostNode by attaching a [html.ShadowRoot] to the `root` element. + /// + /// This also calls [applyGlobalCssRulesToSheet], defined in dom_renderer. + ShadowDomHostNode(html.Element root) : + assert( + root.isConnected ?? true, + 'The `root` of a ShadowDomHostNode must be connected to the Document object or a ShadowRoot.', + ) { + _shadow = root.attachShadow({ + 'mode': 'open', + 'delegatesFocus': 'true', + }); + + final html.StyleElement shadowRootStyleElement = html.StyleElement(); + // The shadowRootStyleElement must be appended to the DOM, or its `sheet` will be null later. + _shadow.append(shadowRootStyleElement); + + // TODO(dit): Apply only rules for the shadow root + applyGlobalCssRulesToSheet( + shadowRootStyleElement.sheet! as html.CssStyleSheet, + browserEngine: browserEngine, + hasAutofillOverlay: browserHasAutofillOverlay(), + ); + } + + @override + html.Element? get activeElement => _shadow.activeElement; + + @override + html.Element? querySelector(String selectors) { + return _shadow.querySelector(selectors); + } + + @override + List querySelectorAll(String selectors) { + return _shadow.querySelectorAll(selectors); + } + + @override + html.Node append(html.Node node) { + return _shadow.append(node); + } + + @override + bool contains(html.Node? other) { + return _shadow.contains(other); + } + + @override + html.Node get node => _shadow; + + @override + List get nodes => _shadow.nodes; +} + +/// A [HostNode] implementation, backed by a [html.Element]. +/// +/// This is a fallback implementation, in case [ShadowDomHostNode] fails when +/// being constructed. +class ElementHostNode implements HostNode { + late html.Element _element; + + /// Build a HostNode by attaching a child [html.Element] to the `root` element. + ElementHostNode(html.Element root) { + _element = html.document.createElement('flt-element-host-node'); + root.append(_element); + } + + @override + html.Element? get activeElement => _element.ownerDocument?.activeElement; + + @override + html.Element? querySelector(String selectors) { + return _element.querySelector(selectors); + } + + @override + List querySelectorAll(String selectors) { + return _element.querySelectorAll(selectors); + } + + @override + html.Node append(html.Node node) { + return _element.append(node); + } + + @override + bool contains(html.Node? other) { + return _element.contains(other); + } + + @override + html.Node get node => _element; + + @override + List get nodes => _element.nodes; +} diff --git a/lib/web_ui/lib/src/engine/html/backdrop_filter.dart b/lib/web_ui/lib/src/engine/html/backdrop_filter.dart index 654bf724b4a68..4758ad33e2d5b 100644 --- a/lib/web_ui/lib/src/engine/html/backdrop_filter.dart +++ b/lib/web_ui/lib/src/engine/html/backdrop_filter.dart @@ -2,8 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'shaders/shader.dart'; +import 'surface.dart'; +import 'surface_stats.dart'; /// A surface that applies an image filter to background. class PersistedBackdropFilter extends PersistedContainerSurface @@ -21,7 +30,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface html.Element? _childContainer; html.Element? _filterElement; ui.Rect? _activeClipBounds; - // Cached inverted transform for _transform. + // Cached inverted transform for [transform]. late Matrix4 _invertedTransform; // Reference to transform last used to cache [_invertedTransform]. Matrix4? _previousTransform; @@ -40,9 +49,9 @@ class PersistedBackdropFilter extends PersistedContainerSurface ..style.transformOrigin = '0 0 0'; _childContainer = html.Element.tag('flt-backdrop-interior'); _childContainer!.style.position = 'absolute'; - if (_debugExplainSurfaceStats) { + if (debugExplainSurfaceStats) { // This creates an additional interior element. Count it too. - _surfaceStatsFor(this).allocatedDomNodeCount++; + surfaceStatsFor(this).allocatedDomNodeCount++; } _filterElement = defaultCreateElement('flt-backdrop-filter'); _filterElement!.style.transformOrigin = '0 0 0'; @@ -62,9 +71,9 @@ class PersistedBackdropFilter extends PersistedContainerSurface @override void apply() { - if (_previousTransform != _transform) { - _invertedTransform = Matrix4.inverted(_transform!); - _previousTransform = _transform; + if (_previousTransform != transform) { + _invertedTransform = Matrix4.inverted(transform!); + _previousTransform = transform; } // https://api.flutter.dev/flutter/widgets/BackdropFilter-class.html // Defines the effective area as the parent/ancestor clip or if not @@ -75,7 +84,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface // Therefore we need to use parent clip element bounds for // backdrop boundary. final double dpr = ui.window.devicePixelRatio; - ui.Rect rect = transformRect(_invertedTransform, ui.Rect.fromLTRB(0, 0, + final ui.Rect rect = transformRect(_invertedTransform, ui.Rect.fromLTRB(0, 0, ui.window.physicalSize.width * dpr, ui.window.physicalSize.height * dpr)); double left = rect.left; @@ -85,7 +94,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface PersistedContainerSurface? parentSurface = parent; while (parentSurface != null) { if (parentSurface.isClipping) { - final ui.Rect activeClipBounds = (_activeClipBounds = parentSurface._localClipBounds)!; + final ui.Rect activeClipBounds = (_activeClipBounds = parentSurface.localClipBounds)!; left = activeClipBounds.left; top = activeClipBounds.top; width = activeClipBounds.width; @@ -103,7 +112,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface ..height = '${height}px'; if (browserEngine == BrowserEngine.firefox) { // For FireFox for now render transparent black background. - // TODO(flutter_web): Switch code to use filter when + // TODO(ferhat): Switch code to use filter when // See https://caniuse.com/#feat=css-backdrop-filter. filterElementStyle ..backgroundColor = '#000' @@ -113,10 +122,10 @@ class PersistedBackdropFilter extends PersistedContainerSurface // Gaussian blur with standard deviation (normal distribution), // the blur will fall within 2 * sigma pixels. if (browserEngine == BrowserEngine.webkit) { - domRenderer.setElementStyle(_filterElement!, '-webkit-backdrop-filter', - _imageFilterToCss(filter)); + DomRenderer.setElementStyle(_filterElement!, '-webkit-backdrop-filter', + filter.filterAttribute); } - domRenderer.setElementStyle(_filterElement!, 'backdrop-filter', _imageFilterToCss(filter)); + DomRenderer.setElementStyle(_filterElement!, 'backdrop-filter', filter.filterAttribute); } } @@ -135,7 +144,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface PersistedContainerSurface? parentSurface = parent; while (parentSurface != null) { if (parentSurface.isClipping) { - if (parentSurface._localClipBounds != _activeClipBounds) { + if (parentSurface.localClipBounds != _activeClipBounds) { apply(); } break; diff --git a/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart new file mode 100644 index 0000000000000..e856684c14e10 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart @@ -0,0 +1,1326 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show NullTreeSanitizer; +import '../browser_detection.dart'; +import '../canvas_pool.dart'; +import '../canvaskit/color_filter.dart'; +import '../color_filter.dart'; +import '../dom_renderer.dart'; +import '../engine_canvas.dart'; +import '../frame_reference.dart'; +import '../html_image_codec.dart'; +import '../platform_dispatcher.dart'; +import '../text/paragraph.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import '../window.dart'; +import 'clip.dart'; +import 'color_filter.dart'; +import 'dom_canvas.dart'; +import 'painting.dart'; +import 'path/path.dart'; +import 'recording_canvas.dart'; +import 'render_vertices.dart'; +import 'shaders/image_shader.dart'; + +/// A raw HTML canvas that is directly written to. +class BitmapCanvas extends EngineCanvas { + /// The rectangle positioned relative to the parent layer's coordinate + /// system's origin, within which this canvas paints. + /// + /// Painting outside these bounds will result in cropping. + ui.Rect get bounds => _bounds; + set bounds(ui.Rect newValue) { + assert(newValue != null); // ignore: unnecessary_null_comparison + _bounds = newValue; + final int newCanvasPositionX = _bounds.left.floor() - kPaddingPixels; + final int newCanvasPositionY = _bounds.top.floor() - kPaddingPixels; + if (_canvasPositionX != newCanvasPositionX || + _canvasPositionY != newCanvasPositionY) { + _canvasPositionX = newCanvasPositionX; + _canvasPositionY = newCanvasPositionY; + _updateRootElementTransform(); + } + } + + ui.Rect _bounds; + CrossFrameCache? _elementCache; + + /// The amount of padding to add around the edges of this canvas to + /// ensure that anti-aliased arcs are not clipped. + static const int kPaddingPixels = 1; + + @override + final html.Element rootElement = html.Element.tag('flt-canvas'); + + final CanvasPool _canvasPool; + + /// The size of the paint [bounds]. + ui.Size get size => _bounds.size; + + /// The last CSS font string is cached to optimize the case where the font + /// styles hasn't changed. + String? _cachedLastCssFont; + + /// List of extra sibling elements created for paragraphs and clipping. + final List _children = []; + + /// The number of pixels along the width of the bitmap that the canvas element + /// renders into. + /// + /// These pixels are different from the logical CSS pixels. Here a pixel + /// literally means 1 point with a RGBA color. + final int widthInBitmapPixels; + + /// The number of pixels along the width of the bitmap that the canvas element + /// renders into. + /// + /// These pixels are different from the logical CSS pixels. Here a pixel + /// literally means 1 point with a RGBA color. + final int heightInBitmapPixels; + + /// The number of pixels in the bitmap that the canvas element renders into. + /// + /// These pixels are different from the logical CSS pixels. Here a pixel + /// literally means 1 point with a RGBA color. + int get bitmapPixelCount => widthInBitmapPixels * heightInBitmapPixels; + + int _saveCount = 0; + + /// Keeps track of what device pixel ratio was used when this [BitmapCanvas] + /// was created. + final double _devicePixelRatio = + EnginePlatformDispatcher.browserDevicePixelRatio; + + // Compensation for [_initializeViewport] snapping canvas position to 1 pixel. + int? _canvasPositionX, _canvasPositionY; + + // Indicates the instructions following drawImage or drawParagraph that + // a child element was created to paint. + // TODO(yjbanov): When childElements are created by + // drawImage/drawParagraph commands, compositing order is not correctly + // handled when we interleave these with other paint commands. + // To solve this, recording canvas will have to check the paint queue + // and send a hint to EngineCanvas that additional canvas layers need + // to be used to composite correctly. In practice this is very rare + // with Widgets but CustomPainter(s) can hit this code path. + bool _childOverdraw = false; + + /// Forces text to be drawn using HTML rather than bitmap. + /// + /// Use this for tests only. + set debugChildOverdraw(bool value) { + _childOverdraw = value; + } + + /// Indicates bitmap canvas contains a 3d transform. + /// WebKit fails to preserve paint order when this happens and therefore + /// requires insertion of
to be + /// used for each child to force correct rendering order. + bool _contains3dTransform = false; + + /// Indicates that contents should be rendered into canvas so a dataUrl + /// can be constructed from contents. + bool _preserveImageData = false; + + /// Canvas pixel to screen pixel ratio. Similar to dpi but + /// uses global transform of canvas to compute ratio. + double get density => _density; + final double _density; + + final RenderStrategy _renderStrategy; + + /// Allocates a canvas with enough memory to paint a picture within the given + /// [bounds]. + /// + /// This canvas can be reused by pictures with different paint bounds as long + /// as the [Rect.size] of the bounds fully fit within the size used to + /// initialize this canvas. + BitmapCanvas(this._bounds, RenderStrategy renderStrategy, + {double density = 1.0}) + : assert(_bounds != null), // ignore: unnecessary_null_comparison + _density = density, + _renderStrategy = renderStrategy, + widthInBitmapPixels = widthToPhysical(_bounds.width), + heightInBitmapPixels = heightToPhysical(_bounds.height), + _canvasPool = CanvasPool(widthToPhysical(_bounds.width), + heightToPhysical(_bounds.height), density) { + rootElement.style.position = 'absolute'; + // Adds one extra pixel to the requested size. This is to compensate for + // _initializeViewport() snapping canvas position to 1 pixel, causing + // painting to overflow by at most 1 pixel. + _canvasPositionX = _bounds.left.floor() - kPaddingPixels; + _canvasPositionY = _bounds.top.floor() - kPaddingPixels; + _updateRootElementTransform(); + _canvasPool.mount(rootElement as html.HtmlElement); + _setupInitialTransform(); + } + + /// Constructs bitmap canvas to capture image data. + factory BitmapCanvas.imageData(ui.Rect bounds) { + final BitmapCanvas bitmapCanvas = BitmapCanvas(bounds, RenderStrategy()); + bitmapCanvas._preserveImageData = true; + return bitmapCanvas; + } + + /// Setup cache for reusing DOM elements across frames. + void setElementCache(CrossFrameCache? cache) { + _elementCache = cache; + } + + void _updateRootElementTransform() { + // Flutter emits paint operations positioned relative to the parent layer's + // coordinate system. However, canvas' coordinate system's origin is always + // in the top-left corner of the canvas. We therefore need to inject an + // initial translation so the paint operations are positioned as expected. + // + // The flooring of the value is to ensure that canvas' top-left corner + // lands on the physical pixel. + // TODO(yjbanov): !This is not accurate if there are + // transforms higher up in the stack. + rootElement.style.transform = + 'translate(${_canvasPositionX}px, ${_canvasPositionY}px)'; + } + + void _setupInitialTransform() { + final double canvasPositionCorrectionX = _bounds.left - + BitmapCanvas.kPaddingPixels - + _canvasPositionX!.toDouble(); + final double canvasPositionCorrectionY = _bounds.top - + BitmapCanvas.kPaddingPixels - + _canvasPositionY!.toDouble(); + // This compensates for the translate on the `rootElement`. + _canvasPool.initialTransform = ui.Offset( + -_bounds.left + canvasPositionCorrectionX + BitmapCanvas.kPaddingPixels, + -_bounds.top + canvasPositionCorrectionY + BitmapCanvas.kPaddingPixels, + ); + } + + static int widthToPhysical(double width) { + final double boundsWidth = width + 1; + return (boundsWidth * EnginePlatformDispatcher.browserDevicePixelRatio) + .ceil() + + 2 * kPaddingPixels; + } + + static int heightToPhysical(double height) { + final double boundsHeight = height + 1; + return (boundsHeight * EnginePlatformDispatcher.browserDevicePixelRatio) + .ceil() + + 2 * kPaddingPixels; + } + + // Used by picture to assess if canvas is large enough to reuse as is. + bool doesFitBounds(ui.Rect newBounds, double newDensity) { + assert(newBounds != null); // ignore: unnecessary_null_comparison + return widthInBitmapPixels >= widthToPhysical(newBounds.width) && + heightInBitmapPixels >= heightToPhysical(newBounds.height) && + _density == newDensity; + } + + @override + void dispose() { + _canvasPool.dispose(); + } + + /// Prepare to reuse this canvas by clearing it's current contents. + @override + void clear() { + _contains3dTransform = false; + _canvasPool.clear(); + final int len = _children.length; + for (int i = 0; i < len; i++) { + final html.Element child = _children[i]; + // Don't remove children that have been reused by CrossFrameCache. + if (child.parent == rootElement) { + child.remove(); + } + } + _children.clear(); + _childOverdraw = false; + _cachedLastCssFont = null; + _setupInitialTransform(); + } + + /// Checks whether this [BitmapCanvas] can still be recycled and reused. + /// + /// See also: + /// + /// * [PersistedPicture._applyBitmapPaint] which uses this method to + /// decide whether to reuse this canvas or not. + /// * [PersistedPicture._recycleCanvas] which also uses this method + /// for the same reason. + bool isReusable() { + return _devicePixelRatio == + EnginePlatformDispatcher.browserDevicePixelRatio; + } + + /// Returns a "data://" URI containing a representation of the image in this + /// canvas in PNG format. + String toDataUrl() { + return _canvasPool.toDataUrl(); + } + + /// Sets the global paint styles to correspond to [paint]. + void setUpPaint(SurfacePaintData paint, ui.Rect? shaderBounds) { + _canvasPool.contextHandle.setUpPaint(paint, shaderBounds); + } + + void tearDownPaint() { + _canvasPool.contextHandle.tearDownPaint(); + } + + @override + int save() { + _canvasPool.save(); + return _saveCount++; + } + + void saveLayer(ui.Rect bounds, ui.Paint paint) { + save(); + } + + @override + void restore() { + _canvasPool.restore(); + _saveCount--; + _cachedLastCssFont = null; + } + + // TODO(yjbanov): not sure what this is attempting to do, but it is probably + // wrong because some clips and transforms are expressed using + // HTML DOM elements. + void restoreToCount(int count) { + assert(_saveCount >= count); + final int restores = _saveCount - count; + for (int i = 0; i < restores; i++) { + _canvasPool.restore(); + } + _saveCount = count; + } + + @override + void translate(double dx, double dy) { + _canvasPool.translate(dx, dy); + } + + @override + void scale(double sx, double sy) { + _canvasPool.scale(sx, sy); + } + + @override + void rotate(double radians) { + _canvasPool.rotate(radians); + } + + @override + void skew(double sx, double sy) { + _canvasPool.skew(sx, sy); + } + + @override + void transform(Float32List matrix4) { + final TransformKind transformKind = transformKindOf(matrix4); + if (transformKind == TransformKind.complex) { + _contains3dTransform = true; + } + _canvasPool.transform(matrix4); + } + + @override + void clipRect(ui.Rect rect, ui.ClipOp clipOp) { + if (clipOp == ui.ClipOp.difference) { + // Create 2 rectangles inside each other that represents + // clip area difference using even-odd fill rule. + final SurfacePath path = SurfacePath(); + path.fillType = ui.PathFillType.evenOdd; + path.addRect(ui.Rect.fromLTWH(0, 0, _bounds.width, _bounds.height)); + path.addRect(rect); + _canvasPool.clipPath(path); + } else { + _canvasPool.clipRect(rect); + } + } + + @override + void clipRRect(ui.RRect rrect) { + _canvasPool.clipRRect(rrect); + } + + @override + void clipPath(ui.Path path) { + _canvasPool.clipPath(path); + } + + /// Whether drawing operation should use DOM node instead of Canvas. + /// + /// - Perspective transforms are not supported by canvas and require + /// DOM to render correctly. + /// - Pictures typically have large rect/rounded rectangles as background + /// prefer DOM if canvas has not been allocated yet. + /// + bool _useDomForRenderingFill(SurfacePaintData paint) => + _renderStrategy.isInsideSvgFilterTree || + (_preserveImageData == false && _contains3dTransform) || + (_childOverdraw && + _canvasPool.isEmpty && + paint.maskFilter == null && + paint.shader == null && + paint.style != ui.PaintingStyle.stroke); + + /// Same as [_useDomForRenderingFill] but allows stroke as well. + /// + /// DOM canvas is generated for simple strokes using borders. + bool _useDomForRenderingFillAndStroke(SurfacePaintData paint) => + _renderStrategy.isInsideSvgFilterTree || + (_preserveImageData == false && _contains3dTransform) || + ((_childOverdraw || + _renderStrategy.hasImageElements || + _renderStrategy.hasParagraphs) && + _canvasPool.isEmpty && + paint.maskFilter == null && + paint.shader == null); + + @override + void drawColor(ui.Color color, ui.BlendMode blendMode) { + final SurfacePaintData paintData = SurfacePaintData() + ..color = color + ..blendMode = blendMode; + if (_useDomForRenderingFill(paintData)) { + drawRect(_computeScreenBounds(_canvasPool.currentTransform), paintData); + } else { + _canvasPool.drawColor(color, blendMode); + } + } + + @override + void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaintData paint) { + if (_useDomForRenderingFill(paint)) { + final SurfacePath path = SurfacePath() + ..moveTo(p1.dx, p1.dy) + ..lineTo(p2.dx, p2.dy); + drawPath(path, paint); + } else { + final ui.Rect? shaderBounds = + (paint.shader != null) ? ui.Rect.fromPoints(p1, p2) : null; + setUpPaint(paint, shaderBounds); + _canvasPool.strokeLine(p1, p2); + tearDownPaint(); + } + } + + @override + void drawPaint(SurfacePaintData paint) { + if (_useDomForRenderingFill(paint)) { + drawRect(_computeScreenBounds(_canvasPool.currentTransform), paint); + } else { + final ui.Rect? shaderBounds = + (paint.shader != null) ? _computePictureBounds() : null; + setUpPaint(paint, shaderBounds); + _canvasPool.fill(); + tearDownPaint(); + } + } + + @override + void drawRect(ui.Rect rect, SurfacePaintData paint) { + if (_useDomForRenderingFillAndStroke(paint)) { + final html.HtmlElement element = buildDrawRectElement( + rect, paint, 'draw-rect', _canvasPool.currentTransform); + _drawElement( + element, + ui.Offset( + math.min(rect.left, rect.right), math.min(rect.top, rect.bottom)), + paint); + } else { + setUpPaint(paint, rect); + _canvasPool.drawRect(rect, paint.style); + tearDownPaint(); + } + } + + /// Inserts a dom element at [offset] creating stack of divs for clipping + /// if required. + void _drawElement( + html.Element element, ui.Offset offset, SurfacePaintData paint) { + if (_canvasPool.isClipped) { + final List clipElements = _clipContent( + _canvasPool.clipStack!, + element, + ui.Offset.zero, + transformWithOffset(_canvasPool.currentTransform, offset)); + for (final html.Element clipElement in clipElements) { + rootElement.append(clipElement); + _children.add(clipElement); + } + } else { + rootElement.append(element); + _children.add(element); + } + final ui.BlendMode? blendMode = paint.blendMode; + if (blendMode != null) { + element.style.mixBlendMode = stringForBlendMode(blendMode) ?? ''; + } + // Switch to preferring DOM from now on, and close the current canvas. + _closeCurrentCanvas(); + } + + @override + void drawRRect(ui.RRect rrect, SurfacePaintData paint) { + final ui.Rect rect = rrect.outerRect; + if (_useDomForRenderingFillAndStroke(paint)) { + final html.HtmlElement element = buildDrawRectElement( + rect, paint, 'draw-rrect', _canvasPool.currentTransform); + applyRRectBorderRadius(element.style, rrect); + _drawElement( + element, + ui.Offset( + math.min(rect.left, rect.right), math.min(rect.top, rect.bottom)), + paint); + } else { + setUpPaint(paint, rrect.outerRect); + _canvasPool.drawRRect(rrect, paint.style); + tearDownPaint(); + } + } + + @override + void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaintData paint) { + setUpPaint(paint, outer.outerRect); + _canvasPool.drawDRRect(outer, inner, paint.style); + tearDownPaint(); + } + + @override + void drawOval(ui.Rect rect, SurfacePaintData paint) { + if (_useDomForRenderingFill(paint)) { + final html.HtmlElement element = buildDrawRectElement( + rect, paint, 'draw-oval', _canvasPool.currentTransform); + _drawElement( + element, + ui.Offset( + math.min(rect.left, rect.right), math.min(rect.top, rect.bottom)), + paint); + element.style.borderRadius = + '${rect.width / 2.0}px / ${rect.height / 2.0}px'; + } else { + setUpPaint(paint, rect); + _canvasPool.drawOval(rect, paint.style); + tearDownPaint(); + } + } + + @override + void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) { + final ui.Rect rect = ui.Rect.fromCircle(center: c, radius: radius); + if (_useDomForRenderingFillAndStroke(paint)) { + final html.HtmlElement element = buildDrawRectElement( + rect, paint, 'draw-circle', _canvasPool.currentTransform); + _drawElement( + element, + ui.Offset( + math.min(rect.left, rect.right), math.min(rect.top, rect.bottom)), + paint); + element.style.borderRadius = '50%'; + } else { + setUpPaint( + paint, + paint.shader != null + ? ui.Rect.fromCircle(center: c, radius: radius) + : null); + _canvasPool.drawCircle(c, radius, paint.style); + tearDownPaint(); + } + } + + @override + void drawPath(ui.Path path, SurfacePaintData paint) { + if (_useDomForRenderingFill(paint)) { + final Matrix4 transform = _canvasPool.currentTransform; + final SurfacePath surfacePath = path as SurfacePath; + final ui.Rect? pathAsLine = surfacePath.toStraightLine(); + if (pathAsLine != null) { + final ui.Rect rect = (pathAsLine.top == pathAsLine.bottom) + ? ui.Rect.fromLTWH( + pathAsLine.left, pathAsLine.top, pathAsLine.width, 1) + : ui.Rect.fromLTWH( + pathAsLine.left, pathAsLine.top, 1, pathAsLine.height); + + final html.HtmlElement element = buildDrawRectElement( + rect, paint, 'draw-rect', _canvasPool.currentTransform); + _drawElement( + element, + ui.Offset(math.min(rect.left, rect.right), + math.min(rect.top, rect.bottom)), + paint); + return; + } + final ui.Rect? pathAsRect = surfacePath.toRect(); + if (pathAsRect != null) { + drawRect(pathAsRect, paint); + return; + } + final ui.RRect? pathAsRRect = surfacePath.toRoundedRect(); + if (pathAsRRect != null) { + drawRRect(pathAsRRect, paint); + return; + } + final ui.Rect pathBounds = surfacePath.getBounds(); + final html.Element svgElm = pathToSvgElement( + surfacePath, paint, '${pathBounds.right}', '${pathBounds.bottom}'); + if (!_canvasPool.isClipped) { + final html.CssStyleDeclaration style = svgElm.style; + style.position = 'absolute'; + if (!transform.isIdentity()) { + style + ..transform = matrix4ToCssTransform(transform) + ..transformOrigin = '0 0 0'; + } + } + _applyFilter(svgElm, paint); + _drawElement(svgElm, const ui.Offset(0, 0), paint); + } else { + setUpPaint(paint, paint.shader != null ? path.getBounds() : null); + if (paint.style == null && paint.strokeWidth != null) { + _canvasPool.drawPath(path, ui.PaintingStyle.stroke); + } else { + _canvasPool.drawPath(path, paint.style); + } + tearDownPaint(); + } + } + + void _applyFilter(html.Element element, SurfacePaintData paint) { + if (paint.maskFilter != null) { + final bool isStroke = paint.style == ui.PaintingStyle.stroke; + final String cssColor = + paint.color == null ? '#000000' : colorToCssString(paint.color)!; + final double sigma = paint.maskFilter!.webOnlySigma; + if (browserEngine == BrowserEngine.webkit && !isStroke) { + // A bug in webkit leaves artifacts when this element is animated + // with filter: blur, we use boxShadow instead. + element.style.boxShadow = '0px 0px ${sigma * 2.0}px $cssColor'; + } else { + element.style.filter = 'blur(${sigma}px)'; + } + } + } + + @override + void drawShadow(ui.Path path, ui.Color color, double elevation, + bool transparentOccluder) { + _canvasPool.drawShadow(path, color, elevation, transparentOccluder); + } + + @override + void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { + final html.HtmlElement imageElement = _drawImage(image, p, paint); + if (paint.colorFilter != null) { + _applyTargetSize( + imageElement, image.width.toDouble(), image.height.toDouble()); + } + _closeCurrentCanvas(); + } + + html.ImageElement _reuseOrCreateImage(HtmlImage htmlImage) { + final String cacheKey = htmlImage.imgElement.src!; + if (_elementCache != null) { + final html.ImageElement? imageElement = + _elementCache!.reuse(cacheKey) as html.ImageElement?; + if (imageElement != null) { + return imageElement; + } + } + // Can't reuse, create new instance. + final html.ImageElement newImageElement = htmlImage.cloneImageElement(); + if (_elementCache != null) { + _elementCache!.cache(cacheKey, newImageElement, _onEvictElement); + } + return newImageElement; + } + + static void _onEvictElement(html.HtmlElement element) { + element.remove(); + } + + html.HtmlElement _drawImage( + ui.Image image, ui.Offset p, SurfacePaintData paint) { + final HtmlImage htmlImage = image as HtmlImage; + final ui.BlendMode? blendMode = paint.blendMode; + final EngineColorFilter? colorFilter = + paint.colorFilter as EngineColorFilter?; + html.HtmlElement imgElement; + if (colorFilter is CkBlendModeColorFilter) { + imgElement = _createImageElementWithBlend( + image, colorFilter.color, colorFilter.blendMode, paint); + } else if (colorFilter is CkMatrixColorFilter) { + imgElement = _createImageElementWithSvgColorMatrixFilter( + image, colorFilter.matrix, paint); + } else { + // No Blending, create an image by cloning original loaded image. + imgElement = _reuseOrCreateImage(htmlImage); + } + imgElement.style.mixBlendMode = stringForBlendMode(blendMode) ?? ''; + if (_canvasPool.isClipped) { + // Reset width/height since they may have been previously set. + imgElement.style..removeProperty('width')..removeProperty('height'); + final List clipElements = _clipContent( + _canvasPool.clipStack!, imgElement, p, _canvasPool.currentTransform); + for (final html.Element clipElement in clipElements) { + rootElement.append(clipElement); + _children.add(clipElement); + } + } else { + final String cssTransform = float64ListToCssTransform( + transformWithOffset(_canvasPool.currentTransform, p).storage); + imgElement.style + ..transformOrigin = '0 0 0' + ..transform = cssTransform + // Reset width/height since they may have been previously set. + ..removeProperty('width') + ..removeProperty('height'); + rootElement.append(imgElement); + _children.add(imgElement); + } + return imgElement; + } + + html.HtmlElement _createImageElementWithBlend(HtmlImage image, ui.Color color, + ui.BlendMode blendMode, SurfacePaintData paint) { + switch (blendMode) { + case ui.BlendMode.colorBurn: + case ui.BlendMode.colorDodge: + case ui.BlendMode.hue: + case ui.BlendMode.modulate: + case ui.BlendMode.overlay: + case ui.BlendMode.plus: + case ui.BlendMode.srcIn: + case ui.BlendMode.srcATop: + case ui.BlendMode.srcOut: + case ui.BlendMode.saturation: + case ui.BlendMode.color: + case ui.BlendMode.luminosity: + case ui.BlendMode.xor: + case ui.BlendMode.dstATop: + return _createImageElementWithSvgBlendFilter( + image, color, blendMode, paint); + default: + return _createBackgroundImageWithBlend(image, color, blendMode, paint); + } + } + + @override + void drawImageRect( + ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { + final bool requiresClipping = src.left != 0 || + src.top != 0 || + src.width != image.width || + src.height != image.height; + // If source and destination sizes are identical, we can skip the longer + // code path that sets the size of the element and clips. + // + // If there is a color filter set however, we maybe using background-image + // to render therefore we have to explicitly set width/height of the + // element for blending to work with background-color. + if (dst.width == image.width && + dst.height == image.height && + !requiresClipping && + paint.colorFilter == null) { + _drawImage(image, dst.topLeft, paint); + } else { + if (requiresClipping) { + save(); + clipRect(dst, ui.ClipOp.intersect); + } + double targetLeft = dst.left; + double targetTop = dst.top; + if (requiresClipping) { + if (src.width != image.width) { + final double leftMargin = -src.left * (dst.width / src.width); + targetLeft += leftMargin; + } + if (src.height != image.height) { + final double topMargin = -src.top * (dst.height / src.height); + targetTop += topMargin; + } + } + + final html.Element imgElement = + _drawImage(image, ui.Offset(targetLeft, targetTop), paint); + // To scale set width / height on destination image. + // For clipping we need to scale according to + // clipped-width/full image width and shift it according to left/top of + // source rectangle. + double targetWidth = dst.width; + double targetHeight = dst.height; + if (requiresClipping) { + targetWidth *= image.width / src.width; + targetHeight *= image.height / src.height; + } + _applyTargetSize( + imgElement as html.HtmlElement, targetWidth, targetHeight); + if (requiresClipping) { + restore(); + } + } + _closeCurrentCanvas(); + } + + void _applyTargetSize( + html.HtmlElement imageElement, double targetWidth, double targetHeight) { + final html.CssStyleDeclaration imageStyle = imageElement.style; + final String widthPx = '${targetWidth.toStringAsFixed(2)}px'; + final String heightPx = '${targetHeight.toStringAsFixed(2)}px'; + imageStyle + // left,top are set to 0 (although position is absolute) because + // Chrome will glitch if you leave them out, reproducible with + // canvas_image_blend_test on row 6, MacOS / Chrome 81.04. + ..left = '0px' + ..top = '0px' + ..width = widthPx + ..height = heightPx; + if (imageElement is! html.ImageElement) { + imageElement.style.backgroundSize = '$widthPx $heightPx'; + } + } + + // Creates a Div element to render an image using background-image css + // attribute to be able to use background blend mode(s) when possible. + // + // Example:
+ // + // Special cases: + // For clear,dstOut it generates a blank element. + // For src,srcOver it only sets background-color attribute. + // For dst,dstIn , it only sets source not background color. + html.HtmlElement _createBackgroundImageWithBlend( + HtmlImage image, + ui.Color? filterColor, + ui.BlendMode colorFilterBlendMode, + SurfacePaintData paint) { + // When blending with color we can't use an image element. + // Instead use a div element with background image, color and + // background blend mode. + final html.HtmlElement imgElement = html.DivElement(); + final html.CssStyleDeclaration style = imgElement.style; + switch (colorFilterBlendMode) { + case ui.BlendMode.clear: + case ui.BlendMode.dstOut: + style.position = 'absolute'; + break; + case ui.BlendMode.src: + case ui.BlendMode.srcOver: + style + ..position = 'absolute' + ..backgroundColor = colorToCssString(filterColor); + break; + case ui.BlendMode.dst: + case ui.BlendMode.dstIn: + style + ..position = 'absolute' + ..backgroundImage = "url('${image.imgElement.src}')"; + break; + default: + style + ..position = 'absolute' + ..backgroundImage = "url('${image.imgElement.src}')" + ..backgroundBlendMode = stringForBlendMode(colorFilterBlendMode) ?? '' + ..backgroundColor = colorToCssString(filterColor); + break; + } + return imgElement; + } + + // Creates an image element and an svg filter to apply on the element. + html.HtmlElement _createImageElementWithSvgBlendFilter( + HtmlImage image, + ui.Color? filterColor, + ui.BlendMode colorFilterBlendMode, + SurfacePaintData paint) { + // For srcIn blendMode, we use an svg filter to apply to image element. + final String? svgFilter = + svgFilterFromBlendMode(filterColor, colorFilterBlendMode); + final html.Element filterElement = + html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer()); + rootElement.append(filterElement); + _children.add(filterElement); + final html.HtmlElement imgElement = _reuseOrCreateImage(image); + imgElement.style.filter = 'url(#_fcf$filterIdCounter)'; + if (colorFilterBlendMode == ui.BlendMode.saturation) { + imgElement.style.backgroundColor = colorToCssString(filterColor); + } + return imgElement; + } + + // Creates an image element and an svg color matrix filter to apply on the element. + html.HtmlElement _createImageElementWithSvgColorMatrixFilter( + HtmlImage image, List matrix, SurfacePaintData paint) { + // For srcIn blendMode, we use an svg filter to apply to image element. + final String? svgFilter = svgFilterFromColorMatrix(matrix); + final html.Element filterElement = + html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer()); + rootElement.append(filterElement); + _children.add(filterElement); + final html.HtmlElement imgElement = _reuseOrCreateImage(image); + imgElement.style.filter = 'url(#_fcf$filterIdCounter)'; + return imgElement; + } + + // Should be called when we add new html elements into rootElement so that + // paint order is preserved. + // + // For example if we draw a path and then a paragraph and image: + // - rootElement + // |--- + // |---

+ // |--- + // Any drawing operations after these tags should allocate a new canvas, + // instead of drawing into earlier canvas. + void _closeCurrentCanvas() { + _canvasPool.closeCurrentCanvas(); + _childOverdraw = true; + _cachedLastCssFont = null; + } + + void setCssFont(String cssFont) { + if (cssFont != _cachedLastCssFont) { + final html.CanvasRenderingContext2D ctx = _canvasPool.context; + ctx.font = cssFont; + _cachedLastCssFont = cssFont; + } + } + + /// Measures the given [text] and returns a [html.TextMetrics] object that + /// contains information about the measurement. + /// + /// The text is measured using the font set by the most recent call to + /// [setCssFont]. + html.TextMetrics measureText(String text) { + return _canvasPool.context.measureText(text); + } + + /// Draws text to the canvas starting at coordinate ([x], [y]). + /// + /// The text is drawn starting at coordinates ([x], [y]). It uses the current + /// font set by the most recent call to [setCssFont]. + void fillText(String text, double x, double y, {List? shadows}) { + final html.CanvasRenderingContext2D ctx = _canvasPool.context; + if (shadows != null) { + ctx.save(); + for (final ui.Shadow shadow in shadows) { + ctx.shadowColor = colorToCssString(shadow.color)!; + ctx.shadowBlur = shadow.blurRadius; + ctx.shadowOffsetX = shadow.offset.dx; + ctx.shadowOffsetY = shadow.offset.dy; + + ctx.fillText(text, x, y); + } + ctx.restore(); + } + ctx.fillText(text, x, y); + } + + @override + void drawParagraph(EngineParagraph paragraph, ui.Offset offset) { + assert(paragraph.isLaidOut); + + /// - paragraph.drawOnCanvas checks that the text styling doesn't include + /// features that prevent text from being rendered correctly using canvas. + /// - _childOverdraw check prevents sandwitching multiple canvas elements + /// when we have alternating paragraphs and other drawing commands that are + /// suitable for canvas. + /// - To make sure an svg filter is applied correctly to paragraph we + /// check isInsideSvgFilterTree to make sure dom node doesn't have any + /// parents that apply one. + if (paragraph.drawOnCanvas && _childOverdraw == false && + !_renderStrategy.isInsideSvgFilterTree) { + paragraph.paint(this, offset); + return; + } + + final html.Element paragraphElement = + drawParagraphElement(paragraph, offset); + if (_canvasPool.isClipped) { + final List clipElements = _clipContent( + _canvasPool.clipStack!, + paragraphElement as html.HtmlElement, + offset, + _canvasPool.currentTransform); + for (final html.Element clipElement in clipElements) { + rootElement.append(clipElement); + _children.add(clipElement); + } + } else { + setElementTransform( + paragraphElement, + transformWithOffset(_canvasPool.currentTransform, offset).storage, + ); + rootElement.append(paragraphElement); + } + _children.add(paragraphElement); + // If there is a prior sibling such as img prevent left/top shift. + paragraphElement.style + ..left = '0px' + ..top = '0px'; + _closeCurrentCanvas(); + } + + /// Draws vertices on a gl context. + /// + /// If both colors and textures is specified in paint data, + /// for [BlendMode.source] we skip colors and use textures, + /// for [BlendMode.dst] we only use colors and ignore textures. + /// We also skip paint shader when no texture is specified. + /// + /// If no colors or textures are specified, stroke hairlines with + /// [Paint.color]. + /// + /// If colors is specified, convert colors to premultiplied (alpha) colors + /// and use a SkTriColorShader to render. + @override + void drawVertices(SurfaceVertices vertices, ui.BlendMode blendMode, + SurfacePaintData paint) { + // TODO(ferhat): Implement shaders for [Paint.shader] and + // blendMode. https://github.com/flutter/flutter/issues/40096 + // Move rendering to OffscreenCanvas so that transform is preserved + // as well. + assert(paint.shader == null || paint.shader is EngineImageShader, + 'Linear/Radial/SweepGradient not supported yet'); + final Int32List? colors = vertices.colors; + final ui.VertexMode mode = vertices.mode; + final html.CanvasRenderingContext2D ctx = _canvasPool.context; + if (colors == null && + paint.style != ui.PaintingStyle.fill && + paint.shader == null) { + final Float32List positions = mode == ui.VertexMode.triangles + ? vertices.positions + : convertVertexPositions(mode, vertices.positions); + // Draw hairline for vertices if no vertex colors are specified. + save(); + final ui.Color color = paint.color ?? const ui.Color(0xFF000000); + _canvasPool.contextHandle + ..fillStyle = null + ..strokeStyle = colorToCssString(color); + glRenderer!.drawHairline(ctx, positions); + restore(); + return; + } + glRenderer!.drawVertices(ctx, widthInBitmapPixels, heightInBitmapPixels, + _canvasPool.currentTransform, vertices, blendMode, paint); + } + + /// Stores paint data used by [drawPoints]. We cannot use the original paint + /// data object because painting style is determined by [ui.PointMode] and + /// not by [SurfacePointData.style]. + static SurfacePaintData _drawPointsPaint = SurfacePaintData() + ..strokeCap = ui.StrokeCap.round + ..strokeJoin = ui.StrokeJoin.round + ..blendMode = ui.BlendMode.srcOver; + + @override + void drawPoints( + ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { + if (pointMode == ui.PointMode.points) { + _drawPointsPaint.style = ui.PaintingStyle.stroke; + } else { + _drawPointsPaint.style = ui.PaintingStyle.fill; + } + _drawPointsPaint.color = paint.color ?? const ui.Color(0xFF000000); + _drawPointsPaint.maskFilter = paint.maskFilter; + + final double dpr = ui.window.devicePixelRatio; + // Use hairline (device pixel when strokeWidth is not specified). + final double strokeWidth = + paint.strokeWidth == null ? 1.0 / dpr : paint.strokeWidth!; + _drawPointsPaint.strokeWidth = strokeWidth; + setUpPaint(_drawPointsPaint, null); + // Draw point using circle with half radius. + _canvasPool.drawPoints(pointMode, points, strokeWidth / 2.0); + tearDownPaint(); + } + + @override + void endOfPaint() { + _canvasPool.endOfPaint(); + _elementCache?.commitFrame(); + // Wrap all elements in translate3d (workaround for webkit paint order bug). + if (_contains3dTransform && browserEngine == BrowserEngine.webkit) { + for (final html.Element element in rootElement.children) { + final html.DivElement paintOrderElement = html.DivElement() + ..style.transform = 'translate3d(0,0,0)'; + paintOrderElement.append(element); + rootElement.append(paintOrderElement); + _children.add(paintOrderElement); + } + } + final html.Node? firstChild = rootElement.firstChild; + if (firstChild != null && firstChild is html.HtmlElement && + firstChild.tagName.toLowerCase() == + 'canvas') { + firstChild.style.zIndex = '-1'; + } + } + + /// Computes paint bounds given [targetTransform] to completely cover window + /// viewport. + ui.Rect _computeScreenBounds(Matrix4 targetTransform) { + final Matrix4 inverted = targetTransform.clone()..invert(); + final double dpr = ui.window.devicePixelRatio; + final double width = ui.window.physicalSize.width * dpr; + final double height = ui.window.physicalSize.height * dpr; + final Vector3 topLeft = inverted.perspectiveTransform(Vector3(0, 0, 0)); + final Vector3 topRight = inverted.perspectiveTransform(Vector3(width, 0, 0)); + final Vector3 bottomRight = + inverted.perspectiveTransform(Vector3(width, height, 0)); + final Vector3 bottomLeft = inverted.perspectiveTransform(Vector3(0, height, 0)); + return ui.Rect.fromLTRB( + math.min(topLeft.x, + math.min(topRight.x, math.min(bottomRight.x, bottomLeft.x))), + math.min(topLeft.y, + math.min(topRight.y, math.min(bottomRight.y, bottomLeft.y))), + math.max(topLeft.x, + math.max(topRight.x, math.max(bottomRight.x, bottomLeft.x))), + math.max(topLeft.y, + math.max(topRight.y, math.max(bottomRight.y, bottomLeft.y))), + ); + } + + /// Computes paint bounds to completely cover picture. + ui.Rect _computePictureBounds() { + return ui.Rect.fromLTRB(0, 0, _bounds.width, _bounds.height); + } +} + +String? stringForBlendMode(ui.BlendMode? blendMode) { + if (blendMode == null) { + return null; + } + switch (blendMode) { + case ui.BlendMode.srcOver: + return 'source-over'; + case ui.BlendMode.srcIn: + return 'source-in'; + case ui.BlendMode.srcOut: + return 'source-out'; + case ui.BlendMode.srcATop: + return 'source-atop'; + case ui.BlendMode.dstOver: + return 'destination-over'; + case ui.BlendMode.dstIn: + return 'destination-in'; + case ui.BlendMode.dstOut: + return 'destination-out'; + case ui.BlendMode.dstATop: + return 'destination-atop'; + case ui.BlendMode.plus: + return 'lighten'; + case ui.BlendMode.src: + return 'copy'; + case ui.BlendMode.xor: + return 'xor'; + case ui.BlendMode.multiply: + // Falling back to multiply, ignoring alpha channel. + // TODO(ferhat): only used for debug, find better fallback for web. + case ui.BlendMode.modulate: + return 'multiply'; + case ui.BlendMode.screen: + return 'screen'; + case ui.BlendMode.overlay: + return 'overlay'; + case ui.BlendMode.darken: + return 'darken'; + case ui.BlendMode.lighten: + return 'lighten'; + case ui.BlendMode.colorDodge: + return 'color-dodge'; + case ui.BlendMode.colorBurn: + return 'color-burn'; + case ui.BlendMode.hardLight: + return 'hard-light'; + case ui.BlendMode.softLight: + return 'soft-light'; + case ui.BlendMode.difference: + return 'difference'; + case ui.BlendMode.exclusion: + return 'exclusion'; + case ui.BlendMode.hue: + return 'hue'; + case ui.BlendMode.saturation: + return 'saturation'; + case ui.BlendMode.color: + return 'color'; + case ui.BlendMode.luminosity: + return 'luminosity'; + default: + throw UnimplementedError( + 'Flutter Web does not support the blend mode: $blendMode'); + } +} + +String? stringForStrokeCap(ui.StrokeCap? strokeCap) { + if (strokeCap == null) { + return null; + } + switch (strokeCap) { + case ui.StrokeCap.butt: + return 'butt'; + case ui.StrokeCap.round: + return 'round'; + case ui.StrokeCap.square: + default: + return 'square'; + } +} + +String stringForStrokeJoin(ui.StrokeJoin strokeJoin) { + assert(strokeJoin != null); // ignore: unnecessary_null_comparison + switch (strokeJoin) { + case ui.StrokeJoin.round: + return 'round'; + case ui.StrokeJoin.bevel: + return 'bevel'; + case ui.StrokeJoin.miter: + default: + return 'miter'; + } +} + +/// Clips the content element against a stack of clip operations and returns +/// root of a tree that contains content node. +/// +/// The stack of clipping rectangles generate an element that either uses +/// overflow:hidden with bounds to clip child or sets a clip-path to clip +/// it's contents. The clipping rectangles are nested and returned together +/// with a list of svg elements that provide clip-paths. +List _clipContent(List clipStack, + html.Element content, ui.Offset offset, Matrix4 currentTransform) { + html.Element? root, curElement; + final List clipDefs = []; + final int len = clipStack.length; + for (int clipIndex = 0; clipIndex < len; clipIndex++) { + final SaveClipEntry entry = clipStack[clipIndex]; + final html.HtmlElement newElement = html.DivElement(); + newElement.style.position = 'absolute'; + applyWebkitClipFix(newElement); + if (root == null) { + root = newElement; + } else { + domRenderer.append(curElement!, newElement); + } + curElement = newElement; + final ui.Rect? rect = entry.rect; + Matrix4 newClipTransform = entry.currentTransform; + final TransformKind transformKind = + transformKindOf(newClipTransform.storage); + final bool requiresTransformStyle = transformKind == TransformKind.complex; + if (rect != null) { + final double clipOffsetX = rect.left; + final double clipOffsetY = rect.top; + newClipTransform = newClipTransform.clone() + ..translate(clipOffsetX, clipOffsetY); + curElement.style + ..overflow = 'hidden' + ..width = '${rect.right - clipOffsetX}px' + ..height = '${rect.bottom - clipOffsetY}px'; + setElementTransform(curElement, newClipTransform.storage); + } else if (entry.rrect != null) { + final ui.RRect roundRect = entry.rrect!; + final String borderRadius = + '${roundRect.tlRadiusX}px ${roundRect.trRadiusX}px ' + '${roundRect.brRadiusX}px ${roundRect.blRadiusX}px'; + final double clipOffsetX = roundRect.left; + final double clipOffsetY = roundRect.top; + newClipTransform = newClipTransform.clone() + ..translate(clipOffsetX, clipOffsetY); + curElement.style + ..borderRadius = borderRadius + ..overflow = 'hidden' + ..width = '${roundRect.right - clipOffsetX}px' + ..height = '${roundRect.bottom - clipOffsetY}px'; + setElementTransform(curElement, newClipTransform.storage); + } else if (entry.path != null) { + // Clipping optimization when we know that the path is an oval. + // We use a div with border-radius set to 50% with a size that is + // set to path bounds and set overflow to hidden. + final SurfacePath surfacePath = entry.path! as SurfacePath; + if (surfacePath.pathRef.isOval != -1) { + final ui.Rect ovalBounds = surfacePath.getBounds(); + final double clipOffsetX = ovalBounds.left; + final double clipOffsetY = ovalBounds.top; + newClipTransform = newClipTransform.clone() + ..translate(clipOffsetX, clipOffsetY); + curElement.style + ..overflow = 'hidden' + ..width = '${ovalBounds.width}px' + ..height = '${ovalBounds.height}px' + ..borderRadius = '50%'; + setElementTransform(curElement, newClipTransform.storage); + } else { + // Abitrary path clipping. + curElement.style + ..transform = matrix4ToCssTransform(newClipTransform) + ..transformOrigin = '0 0 0'; + final String svgClipPath = + createSvgClipDef(curElement as html.HtmlElement, entry.path!); + final html.Element clipElement = + html.Element.html(svgClipPath, treeSanitizer: NullTreeSanitizer()); + clipDefs.add(clipElement); + } + } + // Reverse the transform of the clipping element so children can use + // effective transform to render. + // TODO(ferhat): When we have more than a single clip element, + // reduce number of div nodes by merging (multiplying transforms). + final html.Element reverseTransformDiv = html.DivElement(); + reverseTransformDiv.style.position = 'absolute'; + setElementTransform( + reverseTransformDiv, + (newClipTransform.clone()..invert()).storage, + ); + if (requiresTransformStyle) { + // Instead of flattening matrix3d, preserve so it can be reversed. + curElement.style.transformStyle = 'preserve-3d'; + reverseTransformDiv.style.transformStyle = 'preserve-3d'; + } + curElement.append(reverseTransformDiv); + curElement = reverseTransformDiv; + } + + root!.style.position = 'absolute'; + domRenderer.append(curElement!, content); + setElementTransform( + content, + transformWithOffset(currentTransform, offset).storage, + ); + return [root, ...clipDefs]; +} + +/// Converts a [maskFilter] to the value to be used on a ``. +/// +/// Only supported in non-WebKit browsers. +String maskFilterToCanvasFilter(ui.MaskFilter? maskFilter) { + assert( + browserEngine != BrowserEngine.webkit, + 'WebKit (Safari) does not support `filter` canvas property.', + ); + if (maskFilter != null) { + // Multiply by device-pixel ratio because the canvas' pixel width and height + // are larger than its CSS width and height by device-pixel ratio. + return 'blur(${maskFilter.webOnlySigma * window.devicePixelRatio}px)'; + } else { + return 'none'; + } +} diff --git a/lib/web_ui/lib/src/engine/html/canvas.dart b/lib/web_ui/lib/src/engine/html/canvas.dart index 40788b1139744..4c8a43e878a55 100644 --- a/lib/web_ui/lib/src/engine/html/canvas.dart +++ b/lib/web_ui/lib/src/engine/html/canvas.dart @@ -2,8 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show toMatrix32; +import '../picture.dart'; +import '../util.dart'; +import '../validators.dart'; +import 'painting.dart'; +import 'recording_canvas.dart'; +import 'render_vertices.dart'; class SurfaceCanvas implements ui.Canvas { RecordingCanvas _canvas; @@ -288,6 +298,47 @@ class SurfaceCanvas implements ui.Canvas { _canvas.drawImageRect(image, src, dst, paint as SurfacePaint); } + // Return a list of slice coordinates based on the size of the nine-slice parameters in + // one dimension. Each set of slice coordinates contains a begin/end pair for each of the + // source (image) and dest (screen) in the order (src0, dst0, src1, dst1). + // The area from src0 => src1 of the image is painted on the screen from dst0 => dst1 + // The slices for each dimension are generated independently. + List _initSlices(double img0, double imgC0, double imgC1, double img1, double dst0, double dst1) { + final double imageDim = img1 - img0; + final double destDim = dst1 - dst0; + + if (imageDim == destDim) { + // If the src and dest are the same size then we do not need scaling + // We return 4 values for a single slice + return [ img0, dst0, img1, dst1 ]; + } + + final double edge0Dim = imgC0 - img0; + final double edge1Dim = img1 - imgC1; + final double edgesDim = edge0Dim + edge1Dim; + + if (edgesDim >= destDim) { + // the center portion has disappeared, leaving only the edges to scale to a common + // center position in the destination + // this produces only 2 slices which is 8 values + final double dstC = dst0 + destDim * edge0Dim / edgesDim; + return [ + img0, dst0, imgC0, dstC, + imgC1, dstC, img1, dst1, + ]; + } + + // center portion is nonEmpty and only that part is scaled + // we need 3 slices which is 12 values + final double dstC0 = dst0 + edge0Dim; + final double dstC1 = dst1 - edge1Dim; + return [ + img0, dst0, imgC0, dstC0, + imgC0, dstC0, imgC1, dstC1, + imgC1, dstC1, img1, dst1 + ]; + } + @override void drawImageNine( ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { @@ -297,155 +348,51 @@ class SurfaceCanvas implements ui.Canvas { assert(rectIsValid(dst)); assert(paint != null); // ignore: unnecessary_null_comparison - // Assert you can fit the scaled part of the image (exluding the - // center source). - assert(image.width - center.width < dst.width); - assert(image.height - center.height < dst.height); + if (dst.isEmpty) + return; - // The four unscaled corner rectangles in the from the src. - final ui.Rect srcTopLeft = ui.Rect.fromLTWH( - 0, + final List hSlices = _initSlices( 0, center.left, - center.top, - ); - final ui.Rect srcTopRight = ui.Rect.fromLTWH( center.right, - 0, - image.width - center.right, - center.top, + image.width.toDouble(), + dst.left, + dst.right, ); - final ui.Rect srcBottomLeft = ui.Rect.fromLTWH( + final List vSlices = _initSlices( 0, + center.top, center.bottom, - center.left, - image.height - center.bottom, - ); - final ui.Rect srcBottomRight = ui.Rect.fromLTWH( - center.right, - center.bottom, - image.width - center.right, - image.height - center.bottom, - ); - - final ui.Rect dstTopLeft = srcTopLeft.shift(dst.topLeft); - - // The center rectangle in the dst region - final ui.Rect dstCenter = ui.Rect.fromLTWH( - dstTopLeft.right, - dstTopLeft.bottom, - dst.width - (srcTopLeft.width + srcTopRight.width), - dst.height - (srcTopLeft.height + srcBottomLeft.height), - ); - - drawImageRect(image, srcTopLeft, dstTopLeft, paint); - - final ui.Rect dstTopRight = ui.Rect.fromLTWH( - dstCenter.right, + image.height.toDouble(), dst.top, - srcTopRight.width, - srcTopRight.height, + dst.bottom, ); - drawImageRect(image, srcTopRight, dstTopRight, paint); - final ui.Rect dstBottomLeft = ui.Rect.fromLTWH( - dst.left, - dstCenter.bottom, - srcBottomLeft.width, - srcBottomLeft.height, - ); - drawImageRect(image, srcBottomLeft, dstBottomLeft, paint); - - final ui.Rect dstBottomRight = ui.Rect.fromLTWH( - dstCenter.right, - dstCenter.bottom, - srcBottomRight.width, - srcBottomRight.height, - ); - drawImageRect(image, srcBottomRight, dstBottomRight, paint); - - // Draw the top center rectangle. - drawImageRect( - image, - ui.Rect.fromLTRB( - srcTopLeft.right, - srcTopLeft.top, - srcTopRight.left, - srcTopRight.bottom, - ), - ui.Rect.fromLTRB( - dstTopLeft.right, - dstTopLeft.top, - dstTopRight.left, - dstTopRight.bottom, - ), - paint, - ); - - // Draw the middle left rectangle. - drawImageRect( - image, - ui.Rect.fromLTRB( - srcTopLeft.left, - srcTopLeft.bottom, - srcBottomLeft.right, - srcBottomLeft.top, - ), - ui.Rect.fromLTRB( - dstTopLeft.left, - dstTopLeft.bottom, - dstBottomLeft.right, - dstBottomLeft.top, - ), - paint, - ); - - // Draw the center rectangle. - drawImageRect(image, center, dstCenter, paint); - - // Draw the middle right rectangle. - drawImageRect( - image, - ui.Rect.fromLTRB( - srcTopRight.left, - srcTopRight.bottom, - srcBottomRight.right, - srcBottomRight.top, - ), - ui.Rect.fromLTRB( - dstTopRight.left, - dstTopRight.bottom, - dstBottomRight.right, - dstBottomRight.top, - ), - paint, - ); - - // Draw the bottom center rectangle. - drawImageRect( - image, - ui.Rect.fromLTRB( - srcBottomLeft.right, - srcBottomLeft.top, - srcBottomRight.left, - srcBottomRight.bottom, - ), - ui.Rect.fromLTRB( - dstBottomLeft.right, - dstBottomLeft.top, - dstBottomRight.left, - dstBottomRight.bottom, - ), - paint, - ); + for (int yi = 0; yi < vSlices.length; yi += 4) { + final double srcY0 = vSlices[yi]; + final double dstY0 = vSlices[yi + 1]; + final double srcY1 = vSlices[yi + 2]; + final double dstY1 = vSlices[yi + 3]; + for (int xi = 0; xi < hSlices.length; xi += 4) { + final double srcX0 = hSlices[xi]; + final double dstX0 = hSlices[xi + 1]; + final double srcX1 = hSlices[xi + 2]; + final double dstX1 = hSlices[xi + 3]; + drawImageRect( + image, + ui.Rect.fromLTRB(srcX0, srcY0, srcX1, srcY1), + ui.Rect.fromLTRB(dstX0, dstY0, dstX1, dstY1), + paint, + ); + } + } } @override void drawPicture(ui.Picture picture) { // ignore: unnecessary_null_comparison assert(picture != null); // picture is checked on the engine side - // TODO(het): Support this - throw UnimplementedError(); + _canvas.drawPicture(picture); } @override diff --git a/lib/web_ui/lib/src/engine/html/clip.dart b/lib/web_ui/lib/src/engine/html/clip.dart index 0cab237946277..13fd6c2819c5b 100644 --- a/lib/web_ui/lib/src/engine/html/clip.dart +++ b/lib/web_ui/lib/src/engine/html/clip.dart @@ -2,8 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show NullTreeSanitizer; +import '../dom_renderer.dart'; +import '../shadow.dart'; +import '../util.dart'; +import 'dom_canvas.dart'; +import 'painting.dart'; +import 'path/path.dart'; +import 'path_to_svg_clip.dart'; +import 'surface.dart'; +import 'surface_stats.dart'; /// Mixin used by surfaces that clip their contents using an overflowing DOM /// element. @@ -25,22 +37,10 @@ mixin _DomClip on PersistedContainerSurface { @override html.Element createElement() { final html.Element element = defaultCreateElement('flt-clip'); - if (!debugShowClipLayers) { - // Hide overflow in production mode. When debugging we want to see the - // clipped picture in full. - element.style - ..overflow = 'hidden' - ..zIndex = '0'; - } else { - // Display the outline of the clipping region. When debugShowClipLayers is - // `true` we don't hide clip overflow (see above). This outline helps - // visualizing clip areas. - element.style.boxShadow = 'inset 0 0 10px green'; - } _childContainer = html.Element.tag('flt-clip-interior'); - if (_debugExplainSurfaceStats) { + if (debugExplainSurfaceStats) { // This creates an additional interior element. Count it too. - _surfaceStatsFor(this).allocatedDomNodeCount++; + surfaceStatsFor(this).allocatedDomNodeCount++; } _childContainer!.style.position = 'absolute'; @@ -57,22 +57,43 @@ mixin _DomClip on PersistedContainerSurface { // together. _childContainer = null; } + + void applyOverflow(html.Element element, ui.Clip? clipBehaviour) { + if (!debugShowClipLayers) { + // Hide overflow in production mode. When debugging we want to see the + // clipped picture in full. + if (clipBehaviour != ui.Clip.none) { + element.style + ..overflow = 'hidden' + ..zIndex = '0'; + } + } else { + // Display the outline of the clipping region. When debugShowClipLayers is + // `true` we don't hide clip overflow (see above). This outline helps + // visualizing clip areas. + element.style.boxShadow = 'inset 0 0 10px green'; + } + } } /// A surface that creates a rectangular clip. class PersistedClipRect extends PersistedContainerSurface with _DomClip implements ui.ClipRectEngineLayer { - PersistedClipRect(PersistedClipRect? oldLayer, this.rect) : super(oldLayer); - + PersistedClipRect(PersistedClipRect? oldLayer, this.rect, this.clipBehavior) + : super(oldLayer); + final ui.Clip? clipBehavior; final ui.Rect rect; @override void recomputeTransformAndClip() { - _transform = parent!._transform; - _localClipBounds = rect; - _localTransformInverse = null; - _projectedClip = null; + transform = parent!.transform; + if (clipBehavior != ui.Clip.none) { + localClipBounds = rect; + } else { + localClipBounds = null; + } + projectedClip = null; } @override @@ -87,6 +108,7 @@ class PersistedClipRect extends PersistedContainerSurface ..top = '${rect.top}px' ..width = '${rect.right - rect.left}px' ..height = '${rect.bottom - rect.top}px'; + applyOverflow(rootElement!, clipBehavior); // Translate the child container in the opposite direction to compensate for // the shift in the coordinate system introduced by the translation of the @@ -99,7 +121,8 @@ class PersistedClipRect extends PersistedContainerSurface @override void update(PersistedClipRect oldSurface) { super.update(oldSurface); - if (rect != oldSurface.rect) { + if (rect != oldSurface.rect || clipBehavior != oldSurface.clipBehavior) { + localClipBounds = null; apply(); } } @@ -121,10 +144,13 @@ class PersistedClipRRect extends PersistedContainerSurface @override void recomputeTransformAndClip() { - _transform = parent!._transform; - _localClipBounds = rrect.outerRect; - _localTransformInverse = null; - _projectedClip = null; + transform = parent!.transform; + if (clipBehavior != ui.Clip.none) { + localClipBounds = rrect.outerRect; + } else { + localClipBounds = null; + } + projectedClip = null; } @override @@ -134,7 +160,8 @@ class PersistedClipRRect extends PersistedContainerSurface @override void apply() { - rootElement!.style + final html.CssStyleDeclaration style = rootElement!.style; + style ..left = '${rrect.left}px' ..top = '${rrect.top}px' ..width = '${rrect.width}px' @@ -143,6 +170,7 @@ class PersistedClipRRect extends PersistedContainerSurface ..borderTopRightRadius = '${rrect.trRadiusX}px' ..borderBottomRightRadius = '${rrect.brRadiusX}px' ..borderBottomLeftRadius = '${rrect.blRadiusX}px'; + applyOverflow(rootElement!, clipBehavior); // Translate the child container in the opposite direction to compensate for // the shift in the coordinate system introduced by the translation of the @@ -155,7 +183,8 @@ class PersistedClipRRect extends PersistedContainerSurface @override void update(PersistedClipRRect oldSurface) { super.update(oldSurface); - if (rrect != oldSurface.rrect) { + if (rrect != oldSurface.rrect || clipBehavior != oldSurface.clipBehavior) { + localClipBounds = null; apply(); } } @@ -181,50 +210,58 @@ class PersistedPhysicalShape extends PersistedContainerSurface final ui.Color shadowColor; final ui.Clip clipBehavior; html.Element? _clipElement; + html.Element? _svgElement; @override void recomputeTransformAndClip() { - _transform = parent!._transform; + transform = parent!.transform; - final ui.RRect? roundRect = path.webOnlyPathAsRoundedRect; - if (roundRect != null) { - _localClipBounds = roundRect.outerRect; - } else { - final ui.Rect? rect = path.webOnlyPathAsRect; - if (rect != null) { - _localClipBounds = rect; + if (clipBehavior != ui.Clip.none) { + final ui.RRect? roundRect = path.toRoundedRect(); + if (roundRect != null) { + localClipBounds = roundRect.outerRect; } else { - _localClipBounds = null; + final ui.Rect? rect = path.toRect(); + if (rect != null) { + localClipBounds = rect; + } else { + localClipBounds = null; + } } + } else { + localClipBounds = null; } - _localTransformInverse = null; - _projectedClip = null; + projectedClip = null; } void _applyColor() { rootElement!.style.backgroundColor = colorToCssString(color); } - void _applyShadow() { - applyCssShadow(rootElement, pathBounds, elevation, shadowColor); - } - @override html.Element createElement() { return super.createElement()..setAttribute('clip-type', 'physical-shape'); } + @override + void discard() { + super.discard(); + _clipElement?.remove(); + _clipElement = null; + _svgElement?.remove(); + _svgElement = null; + } + @override void apply() { - _applyColor(); - _applyShadow(); _applyShape(); } void _applyShape() { + _applyColor(); // Handle special case of round rect physical shape mapping to // rounded div. - final ui.RRect? roundRect = path.webOnlyPathAsRoundedRect; + final ui.RRect? roundRect = path.toRoundedRect(); if (roundRect != null) { final String borderRadius = '${roundRect.tlRadiusX}px ${roundRect.trRadiusX}px ' @@ -242,9 +279,10 @@ class PersistedPhysicalShape extends PersistedContainerSurface if (clipBehavior != ui.Clip.none) { style.overflow = 'hidden'; } + applyCssShadow(rootElement, pathBounds, elevation, shadowColor); return; } else { - final ui.Rect? rect = path.webOnlyPathAsRect; + final ui.Rect? rect = path.toRect(); if (rect != null) { final html.CssStyleDeclaration style = rootElement!.style; style @@ -259,9 +297,10 @@ class PersistedPhysicalShape extends PersistedContainerSurface if (clipBehavior != ui.Clip.none) { style.overflow = 'hidden'; } + applyCssShadow(rootElement, pathBounds, elevation, shadowColor); return; } else { - final ui.Rect? ovalRect = path.webOnlyPathAsCircle; + final ui.Rect? ovalRect = path.toCircle(); if (ovalRect != null) { final double rx = ovalRect.width / 2.0; final double ry = ovalRect.height / 2.0; @@ -282,26 +321,65 @@ class PersistedPhysicalShape extends PersistedContainerSurface if (clipBehavior != ui.Clip.none) { style.overflow = 'hidden'; } + applyCssShadow(rootElement, pathBounds, elevation, shadowColor); return; } } } - final String svgClipPath = _pathToSvgClipPath(path, - offsetX: -pathBounds.left, - offsetY: -pathBounds.top, - scaleX: 1.0 / pathBounds.width, - scaleY: 1.0 / pathBounds.height); - // If apply is called multiple times (without update) , remove prior - // svg clip element. + /// If code reaches this point, we have a path we want to clip against and + /// potentially have a shadow due to material surface elevation. + /// + /// When there is no shadow we can simply clip a div with a background + /// color using a svg clip path. + /// + /// Otherwise we need to paint svg element for the path and clip + /// contents against same path for shadow to work since box-shadow doesn't + /// take clip-path into account. + /// + /// Webkit has a bug when applying clip-path on an element that has + /// position: absolute and transform + /// (https://bugs.webkit.org/show_bug.cgi?id=141731). + /// To place clipping rectangle correctly + /// we size the inner container to cover full pathBounds instead of sizing + /// to clipping rect bounds (which is the case for elevation == 0.0 where + /// we shift outer/inner clip area instead to position clip-path). + final String svgClipPath = elevation == 0.0 + ? pathToSvgClipPath(path, + offsetX: -pathBounds.left, + offsetY: -pathBounds.top, + scaleX: 1.0 / pathBounds.width, + scaleY: 1.0 / pathBounds.height) + : pathToSvgClipPath(path, + offsetX: 0.0, + offsetY: 0.0, + scaleX: 1.0 / pathBounds.right, + scaleY: 1.0 / pathBounds.bottom); + + /// If apply is called multiple times (without update), remove prior + /// svg clip and render elements. _clipElement?.remove(); + _svgElement?.remove(); _clipElement = - html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer()); + html.Element.html(svgClipPath, treeSanitizer: NullTreeSanitizer()); domRenderer.append(rootElement!, _clipElement!); - domRenderer.setElementStyle( - rootElement!, 'clip-path', 'url(#svgClip$_clipIdCounter)'); - domRenderer.setElementStyle( - rootElement!, '-webkit-clip-path', 'url(#svgClip$_clipIdCounter)'); + if (elevation == 0.0) { + DomRenderer.setClipPath(rootElement!, createSvgClipUrl()); + final html.CssStyleDeclaration rootElementStyle = rootElement!.style; + rootElementStyle + ..overflow = '' + ..left = '${pathBounds.left}px' + ..top = '${pathBounds.top}px' + ..width = '${pathBounds.width}px' + ..height = '${pathBounds.height}px' + ..borderRadius = ''; + childContainer!.style + ..left = '-${pathBounds.left}px' + ..top = '-${pathBounds.top}px'; + return; + } + + DomRenderer.setClipPath(childContainer!, createSvgClipUrl()); final html.CssStyleDeclaration rootElementStyle = rootElement!.style; rootElementStyle ..overflow = '' @@ -312,28 +390,56 @@ class PersistedPhysicalShape extends PersistedContainerSurface ..borderRadius = ''; childContainer!.style ..left = '-${pathBounds.left}px' - ..top = '-${pathBounds.top}px'; + ..top = '-${pathBounds.top}px' + ..width = '${pathBounds.right}px' + ..height = '${pathBounds.bottom}px'; + + final ui.Rect pathBounds2 = path.getBounds(); + _svgElement = pathToSvgElement( + path, + SurfacePaintData() + ..style = ui.PaintingStyle.fill + ..color = color, + '${pathBounds2.right}', + '${pathBounds2.bottom}'); + + /// Render element behind the clipped content. + rootElement!.insertBefore(_svgElement!, childContainer); + + final SurfaceShadowData shadow = computeShadow(pathBounds, elevation)!; + final ui.Color boxShadowColor = toShadowColor(shadowColor); + _svgElement!.style + ..filter = 'drop-shadow(${shadow.offset.dx}px ${shadow.offset.dy}px ' + '${shadow.blurWidth}px ' + 'rgba(${boxShadowColor.red}, ${boxShadowColor.green}, ' + '${boxShadowColor.blue}, ${boxShadowColor.alpha / 255}))' + ..transform = 'translate(-${pathBounds2.left}px, -${pathBounds2.top}px)'; + + rootElement!.style.backgroundColor = ''; } @override void update(PersistedPhysicalShape oldSurface) { super.update(oldSurface); - if (oldSurface.color != color) { - _applyColor(); - } - if (oldSurface.elevation != elevation || - oldSurface.shadowColor != shadowColor) { - _applyShadow(); + final bool pathChanged = oldSurface.path != path; + if (pathChanged) { + localClipBounds = null; } - if (oldSurface.path != path) { + if (pathChanged || + oldSurface.elevation != elevation || + oldSurface.shadowColor != shadowColor || + oldSurface.color != color) { oldSurface._clipElement?.remove(); oldSurface._clipElement = null; + oldSurface._svgElement?.remove(); + oldSurface._svgElement = null; _clipElement?.remove(); _clipElement = null; + _svgElement?.remove(); + _svgElement = null; // Reset style on prior element since we may have switched between // rect/rrect and arbitrary path. - domRenderer.setElementStyle(rootElement!, 'clip-path', ''); - domRenderer.setElementStyle(rootElement!, '-webkit-clip-path', ''); + DomRenderer.setClipPath(rootElement!, ''); _applyShape(); } else { // Reuse clipElement from prior surface. @@ -342,6 +448,11 @@ class PersistedPhysicalShape extends PersistedContainerSurface domRenderer.append(rootElement!, _clipElement!); } oldSurface._clipElement = null; + _svgElement = oldSurface._svgElement; + if (_svgElement != null) { + rootElement!.insertBefore(_svgElement!, childContainer); + } + oldSurface._svgElement = null; } } } @@ -365,16 +476,20 @@ class PersistedClipPath extends PersistedContainerSurface @override void recomputeTransformAndClip() { super.recomputeTransformAndClip(); - _localClipBounds ??= clipPath.getBounds(); + if (clipBehavior != ui.Clip.none) { + localClipBounds ??= clipPath.getBounds(); + } else { + localClipBounds = null; + } } @override void apply() { _clipElement?.remove(); final String svgClipPath = - createSvgClipDef(childContainer as html.HtmlElement, clipPath); + createSvgClipDef(childContainer! as html.HtmlElement, clipPath); _clipElement = - html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer()); + html.Element.html(svgClipPath, treeSanitizer: NullTreeSanitizer()); domRenderer.append(childContainer!, _clipElement!); } @@ -382,7 +497,7 @@ class PersistedClipPath extends PersistedContainerSurface void update(PersistedClipPath oldSurface) { super.update(oldSurface); if (oldSurface.clipPath != clipPath) { - _localClipBounds = null; + localClipBounds = null; oldSurface._clipElement?.remove(); apply(); } else { @@ -405,12 +520,9 @@ class PersistedClipPath extends PersistedContainerSurface /// Creates an svg clipPath and applies it to [element]. String createSvgClipDef(html.HtmlElement element, ui.Path clipPath) { final ui.Rect pathBounds = clipPath.getBounds(); - final String svgClipPath = _pathToSvgClipPath(clipPath, + final String svgClipPath = pathToSvgClipPath(clipPath, scaleX: 1.0 / pathBounds.right, scaleY: 1.0 / pathBounds.bottom); - domRenderer.setElementStyle( - element, 'clip-path', 'url(#svgClip$_clipIdCounter)'); - domRenderer.setElementStyle( - element, '-webkit-clip-path', 'url(#svgClip$_clipIdCounter)'); + DomRenderer.setClipPath(element, createSvgClipUrl()); // We need to set width and height for the clipElement to cover the // bounds of the path since browsers such as Safari and Edge // seem to incorrectly intersect the element bounding rect with diff --git a/lib/web_ui/lib/src/engine/html/color_filter.dart b/lib/web_ui/lib/src/engine/html/color_filter.dart index 89c1960c27655..feb1df8ef2f46 100644 --- a/lib/web_ui/lib/src/engine/html/color_filter.dart +++ b/lib/web_ui/lib/src/engine/html/color_filter.dart @@ -2,8 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show NullTreeSanitizer; +import '../canvaskit/color_filter.dart'; +import '../color_filter.dart'; +import '../dom_renderer.dart'; +import '../util.dart'; +import 'bitmap_canvas.dart'; +import 'path_to_svg_clip.dart'; +import 'surface.dart'; /// A surface that applies an [ColorFilter] to its children. class PersistedColorFilter extends PersistedContainerSurface @@ -19,6 +29,7 @@ class PersistedColorFilter extends PersistedContainerSurface /// introduced by the [rootElement] translation. html.Element? _childContainer; + /// Color filter to apply to this surface. final ui.ColorFilter filter; html.Element? _filterElement; bool containerVisible = true; @@ -27,12 +38,21 @@ class PersistedColorFilter extends PersistedContainerSurface void adoptElements(PersistedColorFilter oldSurface) { super.adoptElements(oldSurface); _childContainer = oldSurface._childContainer; + _filterElement = oldSurface._filterElement; oldSurface._childContainer = null; } + @override + void preroll(PrerollSurfaceContext prerollContext) { + ++prerollContext.activeColorFilterCount; + super.preroll(prerollContext); + --prerollContext.activeColorFilterCount; + } + @override void discard() { super.discard(); + domRenderer.removeResource(_filterElement); // Do not detach the child container from the root. It is permanently // attached. The elements are reused together and are detached from the DOM // together. @@ -41,8 +61,8 @@ class PersistedColorFilter extends PersistedContainerSurface @override html.Element createElement() { - html.Element element = defaultCreateElement('flt-color-filter'); - html.Element container = html.Element.tag('flt-filter-interior'); + final html.Element element = defaultCreateElement('flt-color-filter'); + final html.Element container = html.Element.tag('flt-filter-interior'); container.style.position = 'absolute'; _childContainer = container; element.append(_childContainer!); @@ -51,86 +71,96 @@ class PersistedColorFilter extends PersistedContainerSurface @override void apply() { - if (_filterElement != null) { - _filterElement?.remove(); - } + domRenderer.removeResource(_filterElement); + _filterElement = null; final EngineColorFilter? engineValue = filter as EngineColorFilter?; if (engineValue == null) { rootElement!.style.backgroundColor = ''; childContainer?.style.visibility = 'visible'; return; } - if (engineValue._blendMode == null) { - rootElement!.style.backgroundColor = - colorToCssString(engineValue._color!); + if (engineValue is CkBlendModeColorFilter) { + _applyBlendModeFilter(engineValue); + } else if (engineValue is CkMatrixColorFilter) { + _applyMatrixColorFilter(engineValue); + } else { childContainer?.style.visibility = 'visible'; - return; } + } - ui.Color filterColor = engineValue._color!; - ui.BlendMode? colorFilterBlendMode = engineValue._blendMode; - html.CssStyleDeclaration style = rootElement!.style; - if (colorFilterBlendMode != null) { - switch (colorFilterBlendMode) { - case ui.BlendMode.clear: - case ui.BlendMode.dstOut: - case ui.BlendMode.srcOut: - childContainer?.style.visibility = 'hidden'; - return; - case ui.BlendMode.dst: - case ui.BlendMode.dstIn: - // Noop. - return; - case ui.BlendMode.src: - case ui.BlendMode.srcOver: - // Uses source filter color. - // Since we don't have a size, we can't use background color. - // Use svg filter srcIn instead. - colorFilterBlendMode = ui.BlendMode.srcIn; - break; - case ui.BlendMode.dstOver: - case ui.BlendMode.srcIn: - case ui.BlendMode.srcATop: - case ui.BlendMode.dstATop: - case ui.BlendMode.xor: - case ui.BlendMode.plus: - case ui.BlendMode.modulate: - case ui.BlendMode.screen: - case ui.BlendMode.overlay: - case ui.BlendMode.darken: - case ui.BlendMode.lighten: - case ui.BlendMode.colorDodge: - case ui.BlendMode.colorBurn: - case ui.BlendMode.hardLight: - case ui.BlendMode.softLight: - case ui.BlendMode.difference: - case ui.BlendMode.exclusion: - case ui.BlendMode.multiply: - case ui.BlendMode.hue: - case ui.BlendMode.saturation: - case ui.BlendMode.color: - case ui.BlendMode.luminosity: - break; - } - - // Use SVG filter for blend mode. - String? svgFilter = - svgFilterFromBlendMode(filterColor, colorFilterBlendMode); - if (svgFilter != null) { - _filterElement = - html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer()); - rootElement!.append(_filterElement!); - rootElement!.style.filter = 'url(#_fcf${_filterIdCounter})'; - if (colorFilterBlendMode == ui.BlendMode.saturation || - colorFilterBlendMode == ui.BlendMode.multiply || - colorFilterBlendMode == ui.BlendMode.modulate) { - style.backgroundColor = colorToCssString(filterColor); - } + void _applyBlendModeFilter(CkBlendModeColorFilter colorFilter) { + final ui.Color filterColor = colorFilter.color; + ui.BlendMode colorFilterBlendMode = colorFilter.blendMode; + final html.CssStyleDeclaration style = childContainer!.style; + switch (colorFilterBlendMode) { + case ui.BlendMode.clear: + case ui.BlendMode.dstOut: + case ui.BlendMode.srcOut: + style.visibility = 'hidden'; + return; + case ui.BlendMode.dst: + case ui.BlendMode.dstIn: + // Noop. return; + case ui.BlendMode.src: + case ui.BlendMode.srcOver: + // Uses source filter color. + // Since we don't have a size, we can't use background color. + // Use svg filter srcIn instead. + colorFilterBlendMode = ui.BlendMode.srcIn; + break; + case ui.BlendMode.dstOver: + case ui.BlendMode.srcIn: + case ui.BlendMode.srcATop: + case ui.BlendMode.dstATop: + case ui.BlendMode.xor: + case ui.BlendMode.plus: + case ui.BlendMode.modulate: + case ui.BlendMode.screen: + case ui.BlendMode.overlay: + case ui.BlendMode.darken: + case ui.BlendMode.lighten: + case ui.BlendMode.colorDodge: + case ui.BlendMode.colorBurn: + case ui.BlendMode.hardLight: + case ui.BlendMode.softLight: + case ui.BlendMode.difference: + case ui.BlendMode.exclusion: + case ui.BlendMode.multiply: + case ui.BlendMode.hue: + case ui.BlendMode.saturation: + case ui.BlendMode.color: + case ui.BlendMode.luminosity: + break; + } + + // Use SVG filter for blend mode. + final String? svgFilter = + svgFilterFromBlendMode(filterColor, colorFilterBlendMode); + if (svgFilter != null) { + _filterElement = + html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer()); + //rootElement!.insertBefore(_filterElement!, childContainer!); + domRenderer.addResource(_filterElement!); + style.filter = 'url(#_fcf$filterIdCounter)'; + if (colorFilterBlendMode == ui.BlendMode.saturation || + colorFilterBlendMode == ui.BlendMode.multiply || + colorFilterBlendMode == ui.BlendMode.modulate) { + style.backgroundColor = colorToCssString(filterColor); } } } + void _applyMatrixColorFilter(CkMatrixColorFilter colorFilter) { + final String? svgFilter = svgFilterFromColorMatrix(colorFilter.matrix); + if (svgFilter != null) { + _filterElement = + html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer()); + domRenderer.addResource(_filterElement!); + childContainer!.style.filter = 'url(#_fcf$filterIdCounter)'; + } + } + @override void update(PersistedColorFilter oldSurface) { super.update(oldSurface); @@ -152,6 +182,9 @@ String? svgFilterFromBlendMode( case ui.BlendMode.srcOut: svgFilter = _srcOutColorFilterToSvg(filterColor); break; + case ui.BlendMode.dstATop: + svgFilter = _dstATopColorFilterToSvg(filterColor); + break; case ui.BlendMode.xor: svgFilter = _xorColorFilterToSvg(filterColor); break; @@ -188,34 +221,49 @@ String? svgFilterFromBlendMode( case ui.BlendMode.luminosity: case ui.BlendMode.multiply: case ui.BlendMode.screen: - case ui.BlendMode.overlay: case ui.BlendMode.darken: case ui.BlendMode.lighten: - case ui.BlendMode.colorDodge: - case ui.BlendMode.colorBurn: case ui.BlendMode.hardLight: case ui.BlendMode.softLight: case ui.BlendMode.difference: case ui.BlendMode.exclusion: svgFilter = _blendColorFilterToSvg( - filterColor, _stringForBlendMode(colorFilterBlendMode)); + filterColor, stringForBlendMode(colorFilterBlendMode)!); break; case ui.BlendMode.src: case ui.BlendMode.dst: - case ui.BlendMode.dstATop: case ui.BlendMode.dstIn: case ui.BlendMode.dstOut: case ui.BlendMode.dstOver: case ui.BlendMode.clear: case ui.BlendMode.srcOver: - assert(false, 'Invalid svg filter request for blend-mode ' + assert( + false, + 'Invalid svg filter request for blend-mode ' '$colorFilterBlendMode'); break; } return svgFilter; } -int _filterIdCounter = 0; +String? svgFilterFromColorMatrix(List matrix) { + filterIdCounter += 1; + final StringBuffer sbMatrix = StringBuffer(); + assert(matrix.length == 20); + for (int i = 0; i < 20; i++) { + if (i != 0) { + sbMatrix.write(' '); + } + sbMatrix.write(matrix[i]); + } + return '$kSvgResourceHeader' + '' + '' + ''; +} + +int filterIdCounter = 0; // The color matrix for feColorMatrix element changes colors based on // the following: @@ -231,14 +279,14 @@ int _filterIdCounter = 0; // B' = b1*R + b2*G + b3*B + b4*A + b5 // A' = a1*R + a2*G + a3*B + a4*A + a5 String _srcInColorFilterToSvg(ui.Color? color) { - _filterIdCounter += 1; - return '' - '' - '' // Just take alpha channel of destination + '>' // Just take alpha channel of destination '' '' ''; } +/// The destination that overlaps the source is composited with the source and +/// replaces the destination. dst-atop CR = CB*αB*αA+CA*αA*(1-αB) αR=αA +String _dstATopColorFilterToSvg(ui.Color? color) { + filterIdCounter += 1; + return '$kSvgResourceHeader' + '' + '' + '' + '' + '' + ''; +} + String _srcOutColorFilterToSvg(ui.Color? color) { - _filterIdCounter += 1; - return '' - '' '' '' @@ -260,9 +322,9 @@ String _srcOutColorFilterToSvg(ui.Color? color) { } String _xorColorFilterToSvg(ui.Color? color) { - _filterIdCounter += 1; - return '' - '' '' '' @@ -275,9 +337,9 @@ String _xorColorFilterToSvg(ui.Color? color) { // result = k1 *in*in2 + k2*in + k3*in2 + k4. String _compositeColorFilterToSvg( ui.Color? color, double k1, double k2, double k3, double k4) { - _filterIdCounter += 1; - return '' - '' '' '' @@ -291,12 +353,12 @@ String _compositeColorFilterToSvg( // First apply color filter to source to change it to [color], then // composite using multiplication. String _modulateColorFilterToSvg(ui.Color color) { - _filterIdCounter += 1; + filterIdCounter += 1; final double r = color.red / 255.0; final double b = color.blue / 255.0; final double g = color.green / 255.0; - return '' - '' '' - '' '' '' + diff --git a/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart b/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart index 3211ecf3bef61..79a2fd99c37fc 100644 --- a/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart +++ b/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import '../util.dart'; html.HtmlElement _createContainer() { final html.HtmlElement container = html.DivElement(); diff --git a/lib/web_ui/lib/src/engine/html/dom_canvas.dart b/lib/web_ui/lib/src/engine/html/dom_canvas.dart new file mode 100644 index 0000000000000..4156df30bf89a --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/dom_canvas.dart @@ -0,0 +1,303 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show NullTreeSanitizer; +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../engine_canvas.dart'; +import '../html_image_codec.dart'; +import '../text/paragraph.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'painting.dart'; +import 'path/path.dart'; +import 'path/path_to_svg.dart'; +import 'shaders/image_shader.dart'; + +/// A canvas that renders to DOM elements and CSS properties. +class DomCanvas extends EngineCanvas with SaveElementStackTracking { + @override + final html.Element rootElement; + + DomCanvas(this.rootElement); + + /// Prepare to reuse this canvas by clearing it's current contents. + @override + void clear() { + super.clear(); + // TODO(yjbanov): we should measure if reusing old elements is beneficial. + domRenderer.clearDom(rootElement); + } + + @override + void clipRect(ui.Rect rect, ui.ClipOp clipOp) { + throw UnimplementedError(); + } + + @override + void clipRRect(ui.RRect rrect) { + throw UnimplementedError(); + } + + @override + void clipPath(ui.Path path) { + throw UnimplementedError(); + } + + @override + void drawColor(ui.Color color, ui.BlendMode blendMode) { + // TODO(yjbanov): implement blendMode + final html.Element box = html.Element.tag('draw-color'); + box.style + ..position = 'absolute' + ..top = '0' + ..right = '0' + ..bottom = '0' + ..left = '0' + ..backgroundColor = colorToCssString(color); + currentElement.append(box); + } + + @override + void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawPaint(SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawRect(ui.Rect rect, SurfacePaintData paint) { + currentElement.append( + buildDrawRectElement(rect, paint, 'draw-rect', currentTransform)); + } + + @override + void drawRRect(ui.RRect rrect, SurfacePaintData paint) { + final html.Element element = buildDrawRectElement( + rrect.outerRect, paint, 'draw-rrect', currentTransform); + applyRRectBorderRadius(element.style, rrect); + currentElement.append(element); + } + + @override + void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawOval(ui.Rect rect, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawPath(ui.Path path, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawShadow(ui.Path path, ui.Color color, double elevation, + bool transparentOccluder) { + throw UnimplementedError(); + } + + @override + void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawImageRect( + ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { + final html.Element paragraphElement = drawParagraphElement( + paragraph as EngineParagraph, offset, + transform: currentTransform); + currentElement.append(paragraphElement); + } + + @override + void drawVertices( + ui.Vertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void drawPoints( + ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { + throw UnimplementedError(); + } + + @override + void endOfPaint() { + // No reuse of elements yet to handle here. Noop. + } +} + +/// Converts a shadow color specified by the framework to the color that should +/// actually be applied when rendering the element. +/// +/// Returns a color for box-shadow based on blur filter at sigma. +ui.Color blurColor(ui.Color color, double sigma) { + final double strength = math.min(math.sqrt(sigma) / (math.pi * 2.0), 1.0); + final int reducedAlpha = ((1.0 - strength) * color.alpha).round(); + return ui.Color((reducedAlpha & 0xff) << 24 | (color.value & 0x00ffffff)); +} + +html.HtmlElement buildDrawRectElement( + ui.Rect rect, SurfacePaintData paint, String tagName, Matrix4 transform) { + assert(paint.shader == null); + final html.HtmlElement rectangle = + domRenderer.createElement(tagName) as html.HtmlElement; + assert(() { + rectangle.setAttribute('flt-rect', '$rect'); + rectangle.setAttribute('flt-paint', '$paint'); + return true; + }()); + String effectiveTransform; + final bool isStroke = paint.style == ui.PaintingStyle.stroke; + final double strokeWidth = paint.strokeWidth ?? 0.0; + final double left = math.min(rect.left, rect.right); + final double right = math.max(rect.left, rect.right); + final double top = math.min(rect.top, rect.bottom); + final double bottom = math.max(rect.top, rect.bottom); + if (transform.isIdentity()) { + if (isStroke) { + effectiveTransform = + 'translate(${left - (strokeWidth / 2.0)}px, ${top - (strokeWidth / 2.0)}px)'; + } else { + effectiveTransform = 'translate(${left}px, ${top}px)'; + } + } else { + // Clone to avoid mutating _transform. + final Matrix4 translated = transform.clone(); + if (isStroke) { + translated.translate( + left - (strokeWidth / 2.0), top - (strokeWidth / 2.0)); + } else { + translated.translate(left, top); + } + effectiveTransform = matrix4ToCssTransform(translated); + } + final html.CssStyleDeclaration style = rectangle.style; + style + ..position = 'absolute' + ..transformOrigin = '0 0 0' + ..transform = effectiveTransform; + + String cssColor = + paint.color == null ? '#000000' : colorToCssString(paint.color)!; + + if (paint.maskFilter != null) { + final double sigma = paint.maskFilter!.webOnlySigma; + if (browserEngine == BrowserEngine.webkit && !isStroke) { + // A bug in webkit leaves artifacts when this element is animated + // with filter: blur, we use boxShadow instead. + style.boxShadow = '0px 0px ${sigma * 2.0}px $cssColor'; + cssColor = colorToCssString( + blurColor(paint.color ?? const ui.Color(0xFF000000), sigma))!; + } else { + style.filter = 'blur(${sigma}px)'; + } + } + + if (isStroke) { + style + ..width = '${right - left - strokeWidth}px' + ..height = '${bottom - top - strokeWidth}px' + ..border = '${_borderStrokeToCssUnit(strokeWidth)} solid $cssColor'; + } else { + style + ..width = '${right - left}px' + ..height = '${bottom - top}px' + ..backgroundColor = cssColor; + + if (paint.shader != null && paint.shader is EngineImageShader) { + _applyImageShaderToElement(rectangle, paint.shader! as EngineImageShader); + } + } + return rectangle; +} + +void _applyImageShaderToElement(html.HtmlElement targetElement, + EngineImageShader imageShader) { + final HtmlImage image = imageShader.image; + targetElement.style.backgroundImage = image.imgElement.src; +} + +void applyRRectBorderRadius(html.CssStyleDeclaration style, ui.RRect rrect) { + if (rrect.tlRadiusX == rrect.trRadiusX && + rrect.tlRadiusX == rrect.blRadiusX && + rrect.tlRadiusX == rrect.brRadiusX && + rrect.tlRadiusX == rrect.tlRadiusY && + rrect.trRadiusX == rrect.trRadiusY && + rrect.blRadiusX == rrect.blRadiusY && + rrect.brRadiusX == rrect.brRadiusY) { + style.borderRadius = _borderStrokeToCssUnit(rrect.blRadiusX); + return; + } + // Non-uniform. Apply each corner radius. + style.borderTopLeftRadius = '${_borderStrokeToCssUnit(rrect.tlRadiusX)} ' + '${_borderStrokeToCssUnit(rrect.tlRadiusY)}'; + style.borderTopRightRadius = '${_borderStrokeToCssUnit(rrect.trRadiusX)} ' + '${_borderStrokeToCssUnit(rrect.trRadiusY)}'; + style.borderBottomLeftRadius = '${_borderStrokeToCssUnit(rrect.blRadiusX)} ' + '${_borderStrokeToCssUnit(rrect.blRadiusY)}'; + style.borderBottomRightRadius = '${_borderStrokeToCssUnit(rrect.brRadiusX)} ' + '${_borderStrokeToCssUnit(rrect.brRadiusY)}'; +} + +String _borderStrokeToCssUnit(double value) { + if (value == 0) { + // TODO(ferhat): hairline nees to take into account both dpi and density. + value = 1.0; + } + return '${value.toStringAsFixed(3)}px'; +} + +html.Element pathToSvgElement( + SurfacePath path, SurfacePaintData paint, String width, String height) { + final StringBuffer sb = StringBuffer(); + sb.write( + ''); + sb.write(''); + sb.write(''); + return html.Element.html(sb.toString(), treeSanitizer: NullTreeSanitizer()); +} diff --git a/lib/web_ui/lib/src/engine/html/image_filter.dart b/lib/web_ui/lib/src/engine/html/image_filter.dart index fe49ac83c8a87..4183703b1ecb0 100644 --- a/lib/web_ui/lib/src/engine/html/image_filter.dart +++ b/lib/web_ui/lib/src/engine/html/image_filter.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import 'shaders/shader.dart'; +import 'surface.dart'; /// A surface that applies an [imageFilter] to its children. class PersistedImageFilter extends PersistedContainerSurface @@ -19,7 +23,8 @@ class PersistedImageFilter extends PersistedContainerSurface @override void apply() { - rootElement!.style.filter = _imageFilterToCss(filter as EngineImageFilter); + rootElement!.style.filter = (filter as EngineImageFilter).filterAttribute; + rootElement!.style.transform = (filter as EngineImageFilter).transformAttribute; } @override diff --git a/lib/web_ui/lib/src/engine/html/offscreen_canvas.dart b/lib/web_ui/lib/src/engine/html/offscreen_canvas.dart new file mode 100644 index 0000000000000..3fae5d1096ad3 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/offscreen_canvas.dart @@ -0,0 +1,100 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import '../platform_dispatcher.dart'; + +/// Polyfill for html.OffscreenCanvas that is not supported on some browsers. +class OffScreenCanvas { + html.OffscreenCanvas? offScreenCanvas; + html.CanvasElement? canvasElement; + int width; + int height; + static bool? _supported; + + OffScreenCanvas(this.width, this.height) { + if (OffScreenCanvas.supported) { + offScreenCanvas = html.OffscreenCanvas(width, height); + } else { + canvasElement = html.CanvasElement( + width: width, + height: height, + ); + canvasElement!.className = 'gl-canvas'; + final double cssWidth = width / EnginePlatformDispatcher.browserDevicePixelRatio; + final double cssHeight = height / EnginePlatformDispatcher.browserDevicePixelRatio; + canvasElement!.style + ..position = 'absolute' + ..width = '${cssWidth}px' + ..height = '${cssHeight}px'; + } + } + + void dispose() { + offScreenCanvas = null; + canvasElement = null; + } + + /// Returns CanvasRenderContext2D or OffscreenCanvasRenderingContext2D to + /// paint into. + Object? getContext2d() { + return offScreenCanvas != null + ? offScreenCanvas!.getContext('2d') + : canvasElement!.getContext('2d'); + } + + /// Feature detection for transferToImageBitmap on OffscreenCanvas. + bool get transferToImageBitmapSupported => + js_util.hasProperty(offScreenCanvas!, 'transferToImageBitmap'); + + /// Creates an ImageBitmap object from the most recently rendered image + /// of the OffscreenCanvas. + /// + /// !Warning API still in experimental status, feature detect before using. + Object? transferToImageBitmap() { + return js_util.callMethod(offScreenCanvas!, 'transferToImageBitmap', + []); + } + + /// Draws canvas contents to a rendering context. + void transferImage(Object targetContext) { + // Actual size of canvas may be larger than viewport size. Use + // source/destination to draw part of the image data. + js_util.callMethod(targetContext, 'drawImage', + [offScreenCanvas ?? canvasElement!, 0, 0, width, height, + 0, 0, width, height]); + } + + /// Converts canvas contents to an image and returns as data URL. + Future toDataUrl() { + final Completer completer = Completer(); + if (offScreenCanvas != null) { + offScreenCanvas!.convertToBlob().then((html.Blob value) { + final html.FileReader fileReader = html.FileReader(); + fileReader.onLoad.listen((html.ProgressEvent event) { + completer.complete( + js_util.getProperty(js_util.getProperty(event, 'target') as Object, 'result') as String, + ); + }); + fileReader.readAsDataUrl(value); + }); + return completer.future; + } else { + return Future.value(canvasElement!.toDataUrl()); + } + } + + /// Draws an image to canvas for both offscreen canvas canvas context2d. + void drawImage(Object image, int x, int y, int width, int height) { + js_util.callMethod( + getContext2d()!, 'drawImage', [image, x, y, width, height]); + } + + /// Feature detects OffscreenCanvas. + static bool get supported => _supported ??= + js_util.hasProperty(html.window, 'OffscreenCanvas'); +} diff --git a/lib/web_ui/lib/src/engine/html/offset.dart b/lib/web_ui/lib/src/engine/html/offset.dart index e512a0f368e67..953253fcf5ee6 100644 --- a/lib/web_ui/lib/src/engine/html/offset.dart +++ b/lib/web_ui/lib/src/engine/html/offset.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../dom_renderer.dart'; +import '../vector_math.dart'; +import 'surface.dart'; /// A surface that translates its children using CSS transform and translate. class PersistedOffset extends PersistedContainerSurface @@ -18,27 +23,33 @@ class PersistedOffset extends PersistedContainerSurface @override void recomputeTransformAndClip() { - _transform = parent!._transform; + transform = parent!.transform; if (dx != 0.0 || dy != 0.0) { - _transform = _transform!.clone(); - _transform!.translate(dx, dy); + transform = transform!.clone(); + transform!.translate(dx, dy); } - _projectedClip = null; - _localTransformInverse = null; + projectedClip = null; } + /// Cached inverse of transform on this node. Unlike transform, this + /// Matrix only contains local transform (not chain multiplied since root). + Matrix4? _localTransformInverse; + @override Matrix4 get localTransformInverse => _localTransformInverse ??= Matrix4.translationValues(-dx, -dy, 0); @override html.Element createElement() { - return defaultCreateElement('flt-offset')..style.transformOrigin = '0 0 0'; + final html.Element element = html.document.createElement('flt-offset'); + DomRenderer.setElementStyle(element, 'position', 'absolute'); + DomRenderer.setElementStyle(element, 'transform-origin', '0 0 0'); + return element; } @override void apply() { - rootElement!.style.transform = 'translate(${dx}px, ${dy}px)'; + DomRenderer.setElementTransform(rootElement!, 'translate(${dx}px, ${dy}px)'); } @override diff --git a/lib/web_ui/lib/src/engine/html/opacity.dart b/lib/web_ui/lib/src/engine/html/opacity.dart index 75e8722cd64c9..7612fdad7c7b8 100644 --- a/lib/web_ui/lib/src/engine/html/opacity.dart +++ b/lib/web_ui/lib/src/engine/html/opacity.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../dom_renderer.dart'; +import '../vector_math.dart'; +import 'surface.dart'; /// A surface that makes its children transparent. class PersistedOpacity extends PersistedContainerSurface @@ -16,35 +21,39 @@ class PersistedOpacity extends PersistedContainerSurface @override void recomputeTransformAndClip() { - _transform = parent!._transform; + transform = parent!.transform; final double dx = offset.dx; final double dy = offset.dy; if (dx != 0.0 || dy != 0.0) { - _transform = _transform!.clone(); - _transform!.translate(dx, dy); + transform = transform!.clone(); + transform!.translate(dx, dy); } - _localTransformInverse = null; - _projectedClip = null; + projectedClip = null; } + /// Cached inverse of transform on this node. Unlike transform, this + /// Matrix only contains local transform (not chain multiplied since root). + Matrix4? _localTransformInverse; + @override Matrix4 get localTransformInverse => _localTransformInverse ??= Matrix4.translationValues(-offset.dx, -offset.dy, 0); @override html.Element createElement() { - return defaultCreateElement('flt-opacity')..style.transformOrigin = '0 0 0'; + final html.Element element = domRenderer.createElement('flt-opacity'); + DomRenderer.setElementStyle(element, 'position', 'absolute'); + DomRenderer.setElementStyle(element, 'transform-origin', '0 0 0'); + return element; } @override void apply() { - // TODO(yjbanov): evaluate using `filter: opacity(X)`. It is a longer string - // but it reportedly has better hardware acceleration, so may - // be worth the trade-off. - rootElement!.style.opacity = '${alpha / 255}'; - rootElement!.style.transform = 'translate(${offset.dx}px, ${offset.dy}px)'; + final html.Element element = rootElement!; + DomRenderer.setElementStyle(element, 'opacity', '${alpha / 255}'); + DomRenderer.setElementTransform(element, 'translate(${offset.dx}px, ${offset.dy}px)'); } @override diff --git a/lib/web_ui/lib/src/engine/html/painting.dart b/lib/web_ui/lib/src/engine/html/painting.dart index 0a955d2bb15d0..f3a70396ce604 100644 --- a/lib/web_ui/lib/src/engine/html/painting.dart +++ b/lib/web_ui/lib/src/engine/html/painting.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; /// Implementation of [ui.Paint] used by the HTML rendering backend. class SurfacePaint implements ui.Paint { @@ -152,7 +153,7 @@ class SurfacePaint implements ui.Paint { _paintData.colorFilter = value; } - // TODO(flutter_web): see https://github.com/flutter/flutter/issues/33605 + // TODO(ferhat): see https://github.com/flutter/flutter/issues/33605 @override double get strokeMiterLimit { throw UnsupportedError('SurfacePaint.strokeMiterLimit'); @@ -165,13 +166,13 @@ class SurfacePaint implements ui.Paint { @override ui.ImageFilter? get imageFilter { - // TODO(flutter/flutter#35156): Implement ImageFilter. + // TODO(ferhat): Implement ImageFilter, flutter/flutter#35156. return null; } @override set imageFilter(ui.ImageFilter? value) { - // TODO(flutter/flutter#35156): Implement ImageFilter. + // TODO(ferhat): Implement ImageFilter, flutter/flutter#35156 } // True if Paint instance has used in RecordingCanvas. diff --git a/lib/web_ui/lib/src/engine/html/path/conic.dart b/lib/web_ui/lib/src/engine/html/path/conic.dart index 84e6e0c378f2f..ba5c50d673382 100644 --- a/lib/web_ui/lib/src/engine/html/path/conic.dart +++ b/lib/web_ui/lib/src/engine/html/path/conic.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'path_utils.dart'; /// Converts conic curve to a list of quadratic curves for rendering on /// canvas or conversion to svg. @@ -150,7 +154,7 @@ class Conic { } void chopAtYExtrema(List dst) { - double? t = _findYExtrema(); + final double? t = _findYExtrema(); if (t == null) { dst.add(this); return; @@ -199,8 +203,8 @@ class Conic { final double coeff0 = fW * p20 - p20; final double coeff1 = p20 - 2 * wP10; final double coeff2 = wP10; - final _QuadRoots quadRoots = _QuadRoots(); - int rootCount = quadRoots.findRoots(coeff0, coeff1, coeff2); + final QuadRoots quadRoots = QuadRoots(); + final int rootCount = quadRoots.findRoots(coeff0, coeff1, coeff2); assert(rootCount == 0 || rootCount == 1); if (rootCount == 1) { return quadRoots.root0; @@ -212,13 +216,13 @@ class Conic { // Map conic to 3D. final double tx0 = p0x; final double ty0 = p0y; - final double tz0 = 1; + const double tz0 = 1; final double tx1 = p1x * fW; final double ty1 = p1y * fW; final double tz1 = fW; final double tx2 = p2x; final double ty2 = p2y; - final double tz2 = 1; + const double tz2 = 1; // Now interpolate each dimension. final double dx0 = tx0 + (tx1 - tx0) * t; final double dx2 = tx1 + (tx2 - tx1) * t; @@ -231,12 +235,12 @@ class Conic { final double dz1 = dz0 + (dz2 - dz0) * t; // Compute new weights. final double root = math.sqrt(dz1); - if (_nearlyEqual(root, 0)) { + if (SPath.nearlyEqual(root, 0)) { return false; } final double w0 = dz0 / root; final double w2 = dz2 / root; - if (_nearlyEqual(dz0, 0) || _nearlyEqual(dz1, 0) || _nearlyEqual(dz2, 0)) { + if (SPath.nearlyEqual(dz0, 0) || SPath.nearlyEqual(dz1, 0) || SPath.nearlyEqual(dz2, 0)) { return false; } // Now we can construct the 2 conics by projecting 3D down to 2D. @@ -304,44 +308,45 @@ class Conic { (t == 1 && p1x == p2x && p1y == p2y)) { return ui.Offset(p2x - p0x, p2y - p0y); } - double p20x = p2x - p0x; - double p20y = p2y - p0y; - double p10x = p1x - p0x; - double p10y = p1y - p0y; - - double cx = fW * p10x; - double cy = fW * p10y; - double ax = fW * p20x - p20x; - double ay = fW * p20y - p20y; - double bx = p20x - cx - cx; - double by = p20y - cy - cy; - _SkQuadCoefficients quadC = _SkQuadCoefficients(ax, ay, bx, by, cx, cy); + final double p20x = p2x - p0x; + final double p20y = p2y - p0y; + final double p10x = p1x - p0x; + final double p10y = p1y - p0y; + + final double cx = fW * p10x; + final double cy = fW * p10y; + final double ax = fW * p20x - p20x; + final double ay = fW * p20y - p20y; + final double bx = p20x - cx - cx; + final double by = p20y - cy - cy; + final SkQuadCoefficients quadC = SkQuadCoefficients(ax, ay, bx, by, cx, cy); return ui.Offset(quadC.evalX(t), quadC.evalY(t)); } -} -double _conicEvalNumerator( - double p0, double p1, double p2, double w, double t) { - assert(t >= 0 && t <= 1); - final double src2w = p1 * w; - final C = p0; - final A = p2 - 2 * src2w + C; - final B = 2 * (src2w - C); - return polyEval(A, B, C, t); -} + static double evalNumerator( + double p0, double p1, double p2, double w, double t) { + assert(t >= 0 && t <= 1); + final double src2w = p1 * w; + final double C = p0; + final double A = p2 - 2 * src2w + C; + final double B = 2 * (src2w - C); + return polyEval(A, B, C, t); + } -double _conicEvalDenominator(double w, double t) { - double B = 2 * (w - 1); - double C = 1; - double A = -B; - return polyEval(A, B, C, t); + static double evalDenominator(double w, double t) { + final double B = 2 * (w - 1); + const double C = 1; + final double A = -B; + return polyEval(A, B, C, t); + } } -class _QuadBounds { +class QuadBounds { double minX = 0; double minY = 0; double maxX = 0; double maxY = 0; + void calculateBounds(Float32List points, int pointIndex) { final double x1 = points[pointIndex++]; final double y1 = points[pointIndex++]; @@ -399,7 +404,7 @@ class _QuadBounds { } } -class _ConicBounds { +class ConicBounds { double minX = 0; double minY = 0; double maxX = 0; @@ -421,21 +426,21 @@ class _ConicBounds { // ------------------------------------------------ // {t^2 (2 - 2 w), t (-2 + 2 w), 1} // Calculate coefficients and solve root. - _QuadRoots roots = _QuadRoots(); - final double P20x = x2 - x1; - final double P10x = cpX - x1; - final double wP10x = w * P10x; - double ax = w * P20x - P20x; - double bx = P20x - 2 * wP10x; - double cx = wP10x; + final QuadRoots roots = QuadRoots(); + final double p20x = x2 - x1; + final double p10x = cpX - x1; + final double wP10x = w * p10x; + final double ax = w * p20x - p20x; + final double bx = p20x - 2 * wP10x; + final double cx = wP10x; int n = roots.findRoots(ax, bx, cx); if (n != 0) { final double t1 = roots.root0!; if ((t1 >= 0) && (t1 <= 1.0)) { - final double denom = _conicEvalDenominator(w, t1); - double numerator = _conicEvalNumerator(x1, cpX, x2, w, t1); + final double denom = Conic.evalDenominator(w, t1); + double numerator = Conic.evalNumerator(x1, cpX, x2, w, t1); final double extremaX = numerator / denom; - numerator = _conicEvalNumerator(y1, cpY, y2, w, t1); + numerator = Conic.evalNumerator(y1, cpY, y2, w, t1); final double extremaY = numerator / denom; // Expand bounds. minX = math.min(minX, extremaX); @@ -444,21 +449,21 @@ class _ConicBounds { maxY = math.max(maxY, extremaY); } } - final double P20y = y2 - y1; - final double P10y = cpY - y1; - final double wP10y = w * P10y; - double a = w * P20y - P20y; - double b = P20y - 2 * wP10y; - double c = wP10y; + final double p20y = y2 - y1; + final double p10y = cpY - y1; + final double wP10y = w * p10y; + final double a = w * p20y - p20y; + final double b = p20y - 2 * wP10y; + final double c = wP10y; n = roots.findRoots(a, b, c); if (n != 0) { final double t2 = roots.root0!; if ((t2 >= 0) && (t2 <= 1.0)) { - final double denom = _conicEvalDenominator(w, t2); - double numerator = _conicEvalNumerator(x1, cpX, x2, w, t2); + final double denom = Conic.evalDenominator(w, t2); + double numerator = Conic.evalNumerator(x1, cpX, x2, w, t2); final double extrema2X = numerator / denom; - numerator = _conicEvalNumerator(y1, cpY, y2, w, t2); + numerator = Conic.evalNumerator(y1, cpY, y2, w, t2); final double extrema2Y = numerator / denom; // Expand bounds. minX = math.min(minX, extrema2X); diff --git a/lib/web_ui/lib/src/engine/html/path/cubic.dart b/lib/web_ui/lib/src/engine/html/path/cubic.dart index 1a222e6f4f933..f5835ba2d3869 100644 --- a/lib/web_ui/lib/src/engine/html/path/cubic.dart +++ b/lib/web_ui/lib/src/engine/html/path/cubic.dart @@ -2,18 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import '../../util.dart'; +import 'path_utils.dart'; /// Chops cubic at Y extrema points and writes result to [dest]. /// /// [points] and [dest] are allowed to share underlying storage as long. -int _chopCubicAtYExtrema(Float32List points, Float32List dest) { +int chopCubicAtYExtrema(Float32List points, Float32List dest) { final double y0 = points[1]; final double y1 = points[3]; final double y2 = points[5]; final double y3 = points[7]; - _QuadRoots _quadRoots = _findCubicExtrema(y0, y1, y2, y3); + final QuadRoots _quadRoots = _findCubicExtrema(y0, y1, y2, y3); final List roots = _quadRoots.roots; if (roots.isEmpty) { // No roots, just use input cubic. @@ -31,12 +34,12 @@ int _chopCubicAtYExtrema(Float32List points, Float32List dest) { return rootCount; } -_QuadRoots _findCubicExtrema(double a, double b, double c, double d) { +QuadRoots _findCubicExtrema(double a, double b, double c, double d) { // A,B,C scaled by 1/3 to simplify final double A = d - a + 3 * (b - c); final double B = 2 * (a - b - b + c); final double C = b - a; - return _QuadRoots()..findRoots(A, B, C); + return QuadRoots()..findRoots(A, B, C); } /// Subdivides cubic curve for a list of t values. @@ -55,7 +58,7 @@ void _chopCubicAt( nextTValue > tValue, 'Expecting t value to monotonically increase'); } } - int rootCount = tValues.length; + final int rootCount = tValues.length; if (0 == rootCount) { for (int i = 0; i < 8; i++) { outPts[i] = points[i]; @@ -73,7 +76,7 @@ void _chopCubicAt( bufferPos += 6; // watch out in case the renormalized t isn't in range - if ((t = _validUnitDivide( + if ((t = validUnitDivide( tValues[i + 1] - tValues[i], 1.0 - tValues[i])) == null) { // Can't renormalize last point, just create a degenerate cubic. @@ -100,18 +103,18 @@ void _chopCubicAtT(Float32List points, int bufferPos, Float32List outPts, final double p2y = points[bufferPos + 5]; final double p3x = points[bufferPos + 6]; // If startT == 0 chop at end point and return curve. - final double ab1x = _interpolate(p0x, p1x, t); - final double ab1y = _interpolate(p0y, p1y, t); - final double bc1x = _interpolate(p1x, p2x, t); - final double bc1y = _interpolate(p1y, p2y, t); - final double cd1x = _interpolate(p2x, p3x, t); - final double cd1y = _interpolate(p2y, p3y, t); - final double abc1x = _interpolate(ab1x, bc1x, t); - final double abc1y = _interpolate(ab1y, bc1y, t); - final double bcd1x = _interpolate(bc1x, cd1x, t); - final double bcd1y = _interpolate(bc1y, cd1y, t); - final double abcd1x = _interpolate(abc1x, bcd1x, t); - final double abcd1y = _interpolate(abc1y, bcd1y, t); + final double ab1x = interpolate(p0x, p1x, t); + final double ab1y = interpolate(p0y, p1y, t); + final double bc1x = interpolate(p1x, p2x, t); + final double bc1y = interpolate(p1y, p2y, t); + final double cd1x = interpolate(p2x, p3x, t); + final double cd1y = interpolate(p2y, p3y, t); + final double abc1x = interpolate(ab1x, bc1x, t); + final double abc1y = interpolate(ab1y, bc1y, t); + final double bcd1x = interpolate(bc1x, cd1x, t); + final double bcd1y = interpolate(bc1y, cd1y, t); + final double abcd1x = interpolate(abc1x, bcd1x, t); + final double abcd1y = interpolate(abc1y, bcd1y, t); // Return left side of curve. outPts[outIndex++] = p0x; @@ -135,7 +138,7 @@ void _chopCubicAtT(Float32List points, int bufferPos, Float32List outPts, // // Options are Newton Raphson (quadratic convergence with typically // 3 iterations or bisection with 16 iterations. -double? _chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { +double? chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { // Translate curve points relative to y. final double ycrv0 = _buffer[1 + bufferStartPos] - y; final double ycrv1 = _buffer[3 + bufferStartPos] - y; @@ -160,7 +163,7 @@ double? _chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { } // Bisection / linear convergance. - final double tolerance = 1.0 / 65536; + const double tolerance = 1.0 / 65536; do { final double tMid = (tPos + tNeg) / 2.0; final double y01 = ycrv0 + (ycrv1 - ycrv0) * tMid; @@ -177,20 +180,20 @@ double? _chopMonoAtY(Float32List _buffer, int bufferStartPos, double y) { } else { tPos = tMid; } - } while (((tPos - tNeg).abs() > tolerance)); + } while ((tPos - tNeg).abs() > tolerance); return (tNeg + tPos) / 2; } -double _evalCubicPts(double c0, double c1, double c2, double c3, double t) { - double A = c3 + 3 * (c1 - c2) - c0; - double B = 3 * (c2 - c1 - c1 + c0); - double C = 3 * (c1 - c0); - double D = c0; +double evalCubicPts(double c0, double c1, double c2, double c3, double t) { + final double A = c3 + 3 * (c1 - c2) - c0; + final double B = 3 * (c2 - c1 - c1 + c0); + final double C = 3 * (c1 - c0); + final double D = c0; return polyEval4(A, B, C, D, t); } // Reusable class to compute bounds without object allocation. -class _CubicBounds { +class CubicBounds { double minX = 0.0; double maxX = 0.0; double minY = 0.0; @@ -338,7 +341,7 @@ class _CubicBounds { } /// Chops cubic spline at startT and stopT, writes result to buffer. -void _chopCubicBetweenT( +void chopCubicBetweenT( List points, double startT, double stopT, Float32List buffer) { assert(startT != 0 || stopT != 0); final double p3y = points[7]; @@ -353,18 +356,18 @@ void _chopCubicBetweenT( final bool chopStart = startT != 0; final double t = chopStart ? startT : stopT; - final double ab1x = _interpolate(p0x, p1x, t); - final double ab1y = _interpolate(p0y, p1y, t); - final double bc1x = _interpolate(p1x, p2x, t); - final double bc1y = _interpolate(p1y, p2y, t); - final double cd1x = _interpolate(p2x, p3x, t); - final double cd1y = _interpolate(p2y, p3y, t); - final double abc1x = _interpolate(ab1x, bc1x, t); - final double abc1y = _interpolate(ab1y, bc1y, t); - final double bcd1x = _interpolate(bc1x, cd1x, t); - final double bcd1y = _interpolate(bc1y, cd1y, t); - final double abcd1x = _interpolate(abc1x, bcd1x, t); - final double abcd1y = _interpolate(abc1y, bcd1y, t); + final double ab1x = interpolate(p0x, p1x, t); + final double ab1y = interpolate(p0y, p1y, t); + final double bc1x = interpolate(p1x, p2x, t); + final double bc1y = interpolate(p1y, p2y, t); + final double cd1x = interpolate(p2x, p3x, t); + final double cd1y = interpolate(p2y, p3y, t); + final double abc1x = interpolate(ab1x, bc1x, t); + final double abc1y = interpolate(ab1y, bc1y, t); + final double bcd1x = interpolate(bc1x, cd1x, t); + final double bcd1y = interpolate(bc1y, cd1y, t); + final double abcd1x = interpolate(abc1x, bcd1x, t); + final double abcd1y = interpolate(abc1y, bcd1y, t); if (!chopStart) { // Return left side of curve. buffer[0] = p0x; @@ -392,18 +395,18 @@ void _chopCubicBetweenT( // We chopped at startT, now the right hand side of curve is at // abcd1, bcd1, cd1, p3x, p3y. Chop this part using endT; final double endT = (stopT - startT) / (1 - startT); - final double ab2x = _interpolate(abcd1x, bcd1x, endT); - final double ab2y = _interpolate(abcd1y, bcd1y, endT); - final double bc2x = _interpolate(bcd1x, cd1x, endT); - final double bc2y = _interpolate(bcd1y, cd1y, endT); - final double cd2x = _interpolate(cd1x, p3x, endT); - final double cd2y = _interpolate(cd1y, p3y, endT); - final double abc2x = _interpolate(ab2x, bc2x, endT); - final double abc2y = _interpolate(ab2y, bc2y, endT); - final double bcd2x = _interpolate(bc2x, cd2x, endT); - final double bcd2y = _interpolate(bc2y, cd2y, endT); - final double abcd2x = _interpolate(abc2x, bcd2x, endT); - final double abcd2y = _interpolate(abc2y, bcd2y, endT); + final double ab2x = interpolate(abcd1x, bcd1x, endT); + final double ab2y = interpolate(abcd1y, bcd1y, endT); + final double bc2x = interpolate(bcd1x, cd1x, endT); + final double bc2y = interpolate(bcd1y, cd1y, endT); + final double cd2x = interpolate(cd1x, p3x, endT); + final double cd2y = interpolate(cd1y, p3y, endT); + final double abc2x = interpolate(ab2x, bc2x, endT); + final double abc2y = interpolate(ab2y, bc2y, endT); + final double bcd2x = interpolate(bc2x, cd2x, endT); + final double bcd2y = interpolate(bc2y, cd2y, endT); + final double abcd2x = interpolate(abc2x, bcd2x, endT); + final double abcd2y = interpolate(abc2y, bcd2y, endT); buffer[0] = abcd1x; buffer[1] = abcd1y; buffer[2] = ab2x; diff --git a/lib/web_ui/lib/src/engine/html/path/path.dart b/lib/web_ui/lib/src/engine/html/path/path.dart index ca2cb9662a25d..c0b8a56f64a1a 100644 --- a/lib/web_ui/lib/src/engine/html/path/path.dart +++ b/lib/web_ui/lib/src/engine/html/path/path.dart @@ -2,8 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../../engine.dart' show toMatrix32; +import '../../util.dart'; +import '../../validators.dart'; +import 'conic.dart'; +import 'cubic.dart'; +import 'path_iterator.dart'; +import 'path_metrics.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; +import 'path_windings.dart'; +import 'tangent.dart'; /// A complex, one-dimensional subset of a plane. /// @@ -12,13 +26,13 @@ part of engine; /// self-intersect. /// /// Stores the verbs and points as they are given to us, with exceptions: -/// - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic +/// - we only record "Close" if it was immediately preceded by Move | Line | Quad | Cubic /// - we insert a Move(0,0) if Line | Quad | Cubic is our first command /// /// The iterator does more cleanup, especially if forceClose == true /// 1. If we encounter degenerate segments, remove them /// 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) -/// 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2 +/// 3. if we encounter Move without a preceding Close, and forceClose is true, goto #2 /// 4. if we encounter Line | Quad | Cubic after Close, cons up a Move class SurfacePath implements ui.Path { // Initial valid of last move to index so we can detect if a move to @@ -64,8 +78,8 @@ class SurfacePath implements ui.Path { _copyFields(source); } - SurfacePath._shallowCopy(SurfacePath source) - : pathRef = PathRef._shallowCopy(source.pathRef) { + SurfacePath.shallowCopy(SurfacePath source) + : pathRef = PathRef.shallowCopy(source.pathRef) { _copyFields(source); } @@ -94,7 +108,7 @@ class SurfacePath implements ui.Path { compare.pathRef.countWeights() == pathRef.countWeights(); bool interpolate(SurfacePath ending, double weight, SurfacePath out) { - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); if (pointCount != ending.pathRef.countPoints()) { return false; } @@ -102,7 +116,7 @@ class SurfacePath implements ui.Path { return true; } out.reset(); - out._addPath(this, 0, 0, null, SPathAddPathMode.kAppend); + out.addPathWithMode(this, 0, 0, null, SPathAddPathMode.kAppend); PathRef.interpolate(ending.pathRef, weight, out.pathRef); return true; } @@ -135,10 +149,8 @@ class SurfacePath implements ui.Path { /// close(). When stroked, closed contour draws join instead of cap at first /// and last point. bool get isLastContourClosed { - int verbCount = pathRef.countVerbs(); - return verbCount == 0 - ? false - : (pathRef.atVerb(verbCount - 1) == SPathVerb.kClose); + final int verbCount = pathRef.countVerbs(); + return verbCount > 0 && (pathRef.atVerb(verbCount - 1) == SPathVerb.kClose); } /// Returns true for finite SkPoint array values between negative SK_ScalarMax @@ -150,13 +162,13 @@ class SurfacePath implements ui.Path { } void _debugValidate() { - // TODO. + assert(pathRef.isValid); } /// Return true if path is a single line and returns points in out. bool isLine(Float32List out) { assert(out.length >= 4); - int verbCount = pathRef.countPoints(); + final int verbCount = pathRef.countPoints(); if (2 == verbCount && pathRef.atVerb(0) == SPathVerb.kMove && pathRef.atVerb(1) != SPathVerb.kLine) { @@ -173,7 +185,7 @@ class SurfacePath implements ui.Path { @override void moveTo(double x, double y) { // remember our index - int pointIndex = pathRef.growForVerb(SPathVerb.kMove, 0); + final int pointIndex = pathRef.growForVerb(SPathVerb.kMove, 0); fLastMoveToIndex = pointIndex + 1; pathRef.setPoint(pointIndex, x, y); _resetAfterEdit(); @@ -182,7 +194,7 @@ class SurfacePath implements ui.Path { /// Starts a new subpath at the given offset from the current point. @override void relativeMoveTo(double dx, double dy) { - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); if (pointCount == 0) { moveTo(dx, dy); } else { @@ -214,7 +226,7 @@ class SurfacePath implements ui.Path { if (fLastMoveToIndex <= 0) { _injectMoveToIfNeeded(); } - int pointIndex = pathRef.growForVerb(SPathVerb.kLine, 0); + final int pointIndex = pathRef.growForVerb(SPathVerb.kLine, 0); pathRef.setPoint(pointIndex, x, y); _resetAfterEdit(); } @@ -223,7 +235,7 @@ class SurfacePath implements ui.Path { /// at the given offset from the current point. @override void relativeLineTo(double dx, double dy) { - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); if (pointCount == 0) { lineTo(dx, dy); } else { @@ -249,7 +261,7 @@ class SurfacePath implements ui.Path { /// point. @override void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); if (pointCount == 0) { quadraticBezierTo(x1, y1, x2, y2); } else { @@ -262,7 +274,7 @@ class SurfacePath implements ui.Path { } void _quadTo(double x1, double y1, double x2, double y2) { - int pointIndex = pathRef.growForVerb(SPathVerb.kQuad, 0); + final int pointIndex = pathRef.growForVerb(SPathVerb.kQuad, 0); pathRef.setPoint(pointIndex, x1, y1); pathRef.setPoint(pointIndex + 1, x2, y2); _resetAfterEdit(); @@ -276,7 +288,7 @@ class SurfacePath implements ui.Path { @override void conicTo(double x1, double y1, double x2, double y2, double w) { _injectMoveToIfNeeded(); - int pointIndex = pathRef.growForVerb(SPathVerb.kConic, w); + final int pointIndex = pathRef.growForVerb(SPathVerb.kConic, w); pathRef.setPoint(pointIndex, x1, y1); pathRef.setPoint(pointIndex + 1, x2, y2); _resetAfterEdit(); @@ -290,7 +302,7 @@ class SurfacePath implements ui.Path { /// is less than 1, it is an ellipse. @override void relativeConicTo(double x1, double y1, double x2, double y2, double w) { - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); if (pointCount == 0) { conicTo(x1, y1, x2, y2, w); } else { @@ -309,7 +321,7 @@ class SurfacePath implements ui.Path { void cubicTo( double x1, double y1, double x2, double y2, double x3, double y3) { _injectMoveToIfNeeded(); - int pointIndex = pathRef.growForVerb(SPathVerb.kCubic, 0); + final int pointIndex = pathRef.growForVerb(SPathVerb.kCubic, 0); pathRef.setPoint(pointIndex, x1, y1); pathRef.setPoint(pointIndex + 1, x2, y2); pathRef.setPoint(pointIndex + 2, x3, y3); @@ -323,7 +335,7 @@ class SurfacePath implements ui.Path { @override void relativeCubicTo( double x1, double y1, double x2, double y2, double x3, double y3) { - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); if (pointCount == 0) { cubicTo(x1, y1, x2, y2, x3, y3); } else { @@ -376,15 +388,15 @@ class SurfacePath implements ui.Path { void addRectWithDirection(ui.Rect rect, int direction, int startIndex) { assert(direction != SPathDirection.kUnknown); - bool isRect = _hasOnlyMoveTos(); + final bool isRect = _hasOnlyMoveTos(); // SkAutoDisableDirectionCheck. - int finalDirection = + final int finalDirection = _hasOnlyMoveTos() ? direction : SPathDirection.kUnknown; - int pointIndex0 = pathRef.growForVerb(SPathVerb.kMove, 0); + final int pointIndex0 = pathRef.growForVerb(SPathVerb.kMove, 0); fLastMoveToIndex = pointIndex0 + 1; - int pointIndex1 = pathRef.growForVerb(SPathVerb.kLine, 0); - int pointIndex2 = pathRef.growForVerb(SPathVerb.kLine, 0); - int pointIndex3 = pathRef.growForVerb(SPathVerb.kLine, 0); + final int pointIndex1 = pathRef.growForVerb(SPathVerb.kLine, 0); + final int pointIndex2 = pathRef.growForVerb(SPathVerb.kLine, 0); + final int pointIndex3 = pathRef.growForVerb(SPathVerb.kLine, 0); pathRef.growForVerb(SPathVerb.kClose, 0); if (direction == SPathDirection.kCW) { pathRef.setPoint(pointIndex0, rect.left, rect.top); @@ -401,7 +413,7 @@ class SurfacePath implements ui.Path { _resetAfterEdit(); // SkAutoDisableDirectionCheck. _firstDirection = finalDirection; - // TODO: optimize by setting pathRef bounds if bounds are already computed. + // TODO(ferhat): optimize by setting pathRef bounds if bounds are already computed. } /// If the `forceMoveTo` argument is false, adds a straight line @@ -454,11 +466,11 @@ class SurfacePath implements ui.Path { // e.g. canvas.drawArc(0, 359.99, ...) // -vs- canvas.drawArc(0, 359.9, ...) // Detect this edge case, and tweak the stop vector. - if (_nearlyEqual(cosStart, cosStop) && _nearlyEqual(sinStart, sinStop)) { + if (SPath.nearlyEqual(cosStart, cosStop) && SPath.nearlyEqual(sinStart, sinStop)) { final double sweep = sweepAngle.abs() * 180.0 / math.pi; if (sweep <= 360 && sweep > 359) { // Use tiny angle (in radians) to tweak. - double deltaRad = sweepAngle < 0 ? -1.0 / 512.0 : 1.0 / 512.0; + final double deltaRad = sweepAngle < 0 ? -1.0 / 512.0 : 1.0 / 512.0; do { stopAngle -= deltaRad; cosStop = math.cos(stopAngle); @@ -523,7 +535,7 @@ class SurfacePath implements ui.Path { // 180..270 -> quadrant 2 // 270..360 -> quadrant 3 - const List quadPoints = [ + const List quadPoints = [ ui.Offset(1, 0), ui.Offset(1, 1), ui.Offset(0, 1), @@ -552,12 +564,12 @@ class SurfacePath implements ui.Path { } } - List conics = []; + final List conics = []; const double quadrantWeight = SPath.scalarRoot2Over2; int conicCount = quadrant; for (int i = 0; i < conicCount; i++) { - int quadPointIndex = i * 2; + final int quadPointIndex = i * 2; final ui.Offset p0 = quadPoints[quadPointIndex]; final ui.Offset p1 = quadPoints[quadPointIndex + 1]; final ui.Offset p2 = quadPoints[quadPointIndex + 2]; @@ -568,7 +580,7 @@ class SurfacePath implements ui.Path { // Now compute any remaining ( < 90degree ) arc for last conic. final double finalPx = x; final double finalPy = y; - ui.Offset lastQuadrantPoint = quadPoints[quadrant * 2]; + final ui.Offset lastQuadrantPoint = quadPoints[quadrant * 2]; // Dot product between last quadrant vector and last point on arc. final double dot = (x * lastQuadrantPoint.dx) + (y * lastQuadrantPoint.dy); if (dot < 1) { @@ -579,13 +591,13 @@ class SurfacePath implements ui.Path { double offCurveX = lastQuadrantPoint.dx + x; double offCurveY = lastQuadrantPoint.dy + y; final double cosThetaOver2 = math.sqrt((1.0 + dot) / 2.0); - double unscaledLength = + final double unscaledLength = math.sqrt((offCurveX * offCurveX) + (offCurveY * offCurveY)); assert(unscaledLength > SPath.scalarNearlyZero); offCurveX /= cosThetaOver2 * unscaledLength; offCurveY /= cosThetaOver2 * unscaledLength; - if (!_nearlyEqual(offCurveX, lastQuadrantPoint.dx) || - !_nearlyEqual(offCurveY, lastQuadrantPoint.dy)) { + if (!SPath.nearlyEqual(offCurveX, lastQuadrantPoint.dx) || + !SPath.nearlyEqual(offCurveY, lastQuadrantPoint.dy)) { conics.add(Conic(lastQuadrantPoint.dx, lastQuadrantPoint.dy, offCurveX, offCurveY, finalPx, finalPy, cosThetaOver2)); ++conicCount; @@ -600,7 +612,7 @@ class SurfacePath implements ui.Path { final double scaleY = rect.height / 2; final double centerX = rect.center.dx; final double centerY = rect.center.dy; - for (Conic conic in conics) { + for (final Conic conic in conics) { double x = conic.p0x; double y = ccw ? -conic.p0y : conic.p0y; conic.p0x = (cosStart * x - sinStart * y) * scaleX + centerX; @@ -623,7 +635,7 @@ class SurfacePath implements ui.Path { _lineToIfNotTooCloseToLastPoint(firstConicPx, firstConicPy); } for (int i = 0; i < conicCount; i++) { - Conic conic = conics[i]; + final Conic conic = conics[i]; conicTo(conic.p1x, conic.p1y, conic.p2x, conic.p2y, conic.fW); } _resetAfterEdit(); @@ -635,7 +647,7 @@ class SurfacePath implements ui.Path { final ui.Offset lastPoint = pathRef.atPoint(pointCount - 1); final double lastPointX = lastPoint.dx; final double lastPointY = lastPoint.dy; - if (!_nearlyEqual(px, lastPointX) || !_nearlyEqual(py, lastPointY)) { + if (!SPath.nearlyEqual(px, lastPointX) || !SPath.nearlyEqual(py, lastPointY)) { lineTo(px, py); } } @@ -679,8 +691,8 @@ class SurfacePath implements ui.Path { } // lastPointX, lastPointY are the coordinates of start point on path, // x,y is final point of arc. - double x = arcEnd.dx; - double y = arcEnd.dy; + final double x = arcEnd.dx; + final double y = arcEnd.dy; // rx,ry are the radii of the eclipse (semi-major/semi-minor axis) double rx = radius.x.abs(); @@ -759,8 +771,8 @@ class SurfacePath implements ui.Path { deltaX *= scaleFactor; deltaY *= scaleFactor; // Compute transformed center. eq. 5.2 - double centerPointX = (unitPts0x + unitPts1x) / 2 - deltaY; - double centerPointY = (unitPts0y + unitPts1y) / 2 + deltaX; + final double centerPointX = (unitPts0x + unitPts1x) / 2 - deltaY; + final double centerPointY = (unitPts0y + unitPts1y) / 2 + deltaX; unitPts0x -= centerPointX; unitPts0y -= centerPointY; unitPts1x -= centerPointX; @@ -797,16 +809,16 @@ class SurfacePath implements ui.Path { // to start outside their marks. A round rect may lose convexity as a // result. If the input values are on integers, place the conic on // integers as well. - bool expectIntegers = _nearlyEqual((math.pi / 2 - thetaWidth.abs()), 0) && - _isInteger(rx) && - _isInteger(ry) && - _isInteger(x) && - _isInteger(y); + final bool expectIntegers = SPath.nearlyEqual(math.pi / 2 - thetaWidth.abs(), 0) && + SPath.isInteger(rx) && + SPath.isInteger(ry) && + SPath.isInteger(x) && + SPath.isInteger(y); for (int i = 0; i < segments; i++) { final double endTheta = startTheta + thetaWidth; - final double sinEndTheta = _snapToZero(math.sin(endTheta)); - final double cosEndTheta = _snapToZero(math.cos(endTheta)); + final double sinEndTheta = SPath.snapToZero(math.sin(endTheta)); + final double cosEndTheta = SPath.snapToZero(math.cos(endTheta)); double unitPts1x = cosEndTheta + centerPointX; double unitPts1y = sinEndTheta + centerPointY; double unitPts0x = unitPts1x + t * sinEndTheta; @@ -857,7 +869,7 @@ class SurfacePath implements ui.Path { assert(offsetIsValid(arcEndDelta)); assert(radiusIsValid(radius)); - int pointCount = pathRef.countPoints(); + final int pointCount = pathRef.countPoints(); double lastPointX, lastPointY; if (pointCount == 0) { lastPointX = lastPointY = 0; @@ -888,9 +900,9 @@ class SurfacePath implements ui.Path { void _addOval(ui.Rect oval, int direction, int startIndex) { assert(rectIsValid(oval)); assert(direction != SPathDirection.kUnknown); - bool isOval = _hasOnlyMoveTos(); + final bool isOval = _hasOnlyMoveTos(); - final double weight = SPath.scalarRoot2Over2; + const double weight = SPath.scalarRoot2Over2; final double left = oval.left; final double right = oval.right; final double centerX = (left + right) / 2.0; @@ -944,7 +956,7 @@ class SurfacePath implements ui.Path { final double startOver90 = startAngle / (math.pi / 2.0); final double startOver90I = (startOver90 + 0.5).floorToDouble(); final double error = startOver90 - startOver90I; - if (_nearlyEqual(error, 0)) { + if (SPath.nearlyEqual(error, 0)) { // Index 1 is at startAngle == 0. double startIndex = startOver90I + 1.0 % 4.0; startIndex = startIndex < 0 ? startIndex + 4.0 : startIndex; @@ -971,7 +983,7 @@ class SurfacePath implements ui.Path { if (pointCount <= 0) { return; } - int pointIndex = pathRef.growForVerb(SPathVerb.kMove, 0); + final int pointIndex = pathRef.growForVerb(SPathVerb.kMove, 0); fLastMoveToIndex = pointIndex + 1; pathRef.setPoint(pointIndex, points[0].dx, points[0].dy); pathRef.growForRepeatedVerb(SPathVerb.kLine, pointCount - 1); @@ -997,20 +1009,20 @@ class SurfacePath implements ui.Path { assert(rrectIsValid(rrect)); assert(direction != SPathDirection.kUnknown); - bool isRRect = _hasOnlyMoveTos(); - ui.Rect bounds = rrect.outerRect; + final bool isRRect = _hasOnlyMoveTos(); + final ui.Rect bounds = rrect.outerRect; if (rrect.isRect || rrect.isEmpty) { // degenerate(rect) => radii points are collapsing. addRectWithDirection(bounds, direction, (startIndex + 1) ~/ 2); - } else if (_isRRectOval(rrect)) { + } else if (isRRectOval(rrect)) { // degenerate(oval) => line points are collapsing. _addOval(bounds, direction, startIndex ~/ 2); } else { - final double weight = SPath.scalarRoot2Over2; - double left = bounds.left; - double right = bounds.right; - double top = bounds.top; - double bottom = bounds.bottom; + const double weight = SPath.scalarRoot2Over2; + final double left = bounds.left; + final double right = bounds.right; + final double top = bounds.top; + final double bottom = bounds.bottom; final double width = right - left; final double height = bottom - top; // Proportionally scale down all radii to fit. Find the minimum ratio @@ -1057,11 +1069,16 @@ class SurfacePath implements ui.Path { /// matrix stored in column major order. @override void addPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) { - _addPath(path, offset.dx, offset.dy, + addPathWithMode(path, offset.dx, offset.dy, matrix4 == null ? null : toMatrix32(matrix4), SPathAddPathMode.kAppend); } - void _addPath(ui.Path path, double offsetX, double offsetY, + /// Adds a new subpath that consists of the given `path` offset by the given + /// `offset`, and using the given [mode]. + /// + /// If `matrix4` is not null, the path will be transformed by this matrix + /// after the matrix is translated by the given offset. + void addPathWithMode(ui.Path path, double offsetX, double offsetY, Float32List? matrix4, int mode) { SurfacePath source = path as SurfacePath; if (source.pathRef.isEmpty) { @@ -1078,7 +1095,7 @@ class SurfacePath implements ui.Path { // we are not extending. if (mode == SPathAddPathMode.kAppend && (matrix4 == null || _isSimple2dTransform(matrix4))) { - pathRef._append(source.pathRef); + pathRef.append(source.pathRef); } else { bool firstVerb = true; final PathRefIterator iter = PathRefIterator(source.pathRef); @@ -1087,12 +1104,12 @@ class SurfacePath implements ui.Path { while ((verb = iter.next(outPts)) != SPath.kDoneVerb) { switch (verb) { case SPath.kMoveVerb: - double point0X = matrix4 == null + final double point0X = matrix4 == null ? outPts[0] + offsetX : (matrix4[0] * (outPts[0] + offsetX)) + (matrix4[4] * (outPts[1] + offsetY)) + matrix4[12]; - double point0Y = matrix4 == null + final double point0Y = matrix4 == null ? outPts[1] + offsetY : (matrix4[1] * (outPts[0] + offsetX)) + (matrix4[5] * (outPts[1] + offsetY)) + @@ -1149,7 +1166,7 @@ class SurfacePath implements ui.Path { fLastMoveToIndex = previousPointCount + source.fLastMoveToIndex; } // Translate/transform all points. - int newPointCount = pathRef.countPoints(); + final int newPointCount = pathRef.countPoints(); final Float32List points = pathRef.points; for (int p = previousPointCount * 2; p < (newPointCount * 2); p += 2) { if (matrix4 == null) { @@ -1158,7 +1175,7 @@ class SurfacePath implements ui.Path { } else { final double x = offsetX + points[p]; final double y = offsetY + points[p + 1]; - points[p] = (matrix4[0] * (x)) + (matrix4[4] * y) + matrix4[12]; + points[p] = (matrix4[0] * x) + (matrix4[4] * y) + matrix4[12]; points[p + 1] = (matrix4[1] * x) + (matrix4[5] * y) + matrix4[13]; } } @@ -1174,7 +1191,7 @@ class SurfacePath implements ui.Path { @override void extendWithPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) { assert(offsetIsValid(offset)); - _addPath(path, offset.dx, offset.dy, + addPathWithMode(path, offset.dx, offset.dy, matrix4 == null ? null : toMatrix32(matrix4), SPathAddPathMode.kExtend); } @@ -1192,7 +1209,7 @@ class SurfacePath implements ui.Path { @override bool contains(ui.Offset point) { assert(offsetIsValid(point)); - bool isInverse = _isInverseFillType; + final bool isInverse = _isInverseFillType; if (pathRef.isEmpty) { return isInverse; } @@ -1205,7 +1222,7 @@ class SurfacePath implements ui.Path { return isInverse; } final PathWinding windings = PathWinding(pathRef, point.dx, point.dy); - bool evenOddFill = ui.PathFillType.evenOdd == _fillType; + final bool evenOddFill = ui.PathFillType.evenOdd == _fillType; int w = windings.w; if (evenOddFill) { w &= 1; @@ -1225,10 +1242,10 @@ class SurfacePath implements ui.Path { // points have identical tangents. final PathIterator iter = PathIterator(pathRef, true); final Float32List _buffer = Float32List(8 + 10); - List tangents = []; + final List tangents = []; bool done = false; do { - int oldCount = tangents.length; + final int oldCount = tangents.length; switch (iter.next(_buffer)) { case SPath.kMoveVerb: case SPath.kCloseVerb: @@ -1250,18 +1267,18 @@ class SurfacePath implements ui.Path { break; } if (tangents.length > oldCount) { - int last = tangents.length - 1; + final int last = tangents.length - 1; final ui.Offset tangent = tangents[last]; - if (_nearlyEqual(_lengthSquaredOffset(tangent), 0)) { - tangents.remove(last); + if (SPath.nearlyEqual(lengthSquaredOffset(tangent), 0)) { + tangents.removeAt(last); } else { for (int index = 0; index < last; ++index) { final ui.Offset test = tangents[index]; - double crossProduct = test.dx * tangent.dy - test.dy * tangent.dx; - if (_nearlyEqual(crossProduct, 0) && + final double crossProduct = test.dx * tangent.dy - test.dy * tangent.dx; + if (SPath.nearlyEqual(crossProduct, 0) && SPath.scalarSignedAsInt(tangent.dx * test.dx) <= 0 && SPath.scalarSignedAsInt(tangent.dy * test.dy) <= 0) { - ui.Offset offset = tangents.removeAt(last); + final ui.Offset offset = tangents.removeAt(last); if (index != tangents.length) { tangents[index] = offset; } @@ -1271,7 +1288,7 @@ class SurfacePath implements ui.Path { } } } while (!done); - return tangents.length == 0 ? isInverse : !isInverse; + return tangents.isEmpty ? isInverse : !isInverse; } /// Returns a copy of the path with all the segments of every @@ -1284,7 +1301,7 @@ class SurfacePath implements ui.Path { /// sub path transformed by the given matrix. @override SurfacePath transform(Float64List matrix4) { - SurfacePath newPath = SurfacePath.from(this); + final SurfacePath newPath = SurfacePath.from(this); newPath._transform(matrix4); return newPath; } @@ -1293,7 +1310,8 @@ class SurfacePath implements ui.Path { pathRef.startEdit(); final int pointCount = pathRef.countPoints(); final Float32List points = pathRef.points; - for (int i = 0, len = pointCount * 2; i < len; i += 2) { + final int len = pointCount * 2; + for (int i = 0; i < len; i += 2) { final double x = points[i]; final double y = points[i + 1]; final double w = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); @@ -1302,7 +1320,7 @@ class SurfacePath implements ui.Path { points[i] = transformedX; points[i + 1] = transformedY; } - // TODO: optimize for axis aligned or scale/translate type transforms. + // TODO(ferhat): optimize for axis aligned or scale/translate type transforms. _convexityType = SPathConvexityType.kUnknown; } @@ -1352,7 +1370,7 @@ class SurfacePath implements ui.Path { pointIndex++; } --pointIndex; - int convexity = + final int convexity = Convexicator.bySign(pathRef, pointIndex, pointCount - pointIndex); if (SPathConvexityType.kConcave == convexity) { setConvexityType(SPathConvexityType.kConcave); @@ -1367,7 +1385,7 @@ class SurfacePath implements ui.Path { // Path passed quick concave check, now compute actual convexity. int contourCount = 0; int count; - Convexicator state = Convexicator(); + final Convexicator state = Convexicator(); int verb; while ((verb = iter.next(pts)) != SPath.kDoneVerb) { switch (verb) { @@ -1403,7 +1421,8 @@ class SurfacePath implements ui.Path { default: return _setComputedConvexity(SPathConvexityType.kConcave); } - for (int i = 2, len = count * 2; i <= len; i += 2) { + final int len = count * 2; + for (int i = 2; i <= len; i += 2) { if (!state.addPoint(pts[i], pts[i + 1])) { if (!state.isFinite) { return SPathConvexityType.kUnknown; @@ -1413,7 +1432,7 @@ class SurfacePath implements ui.Path { } } - if (this._firstDirection == SPathDirection.kUnknown) { + if (_firstDirection == SPathDirection.kUnknown) { if (state.firstDirection == SPathDirection.kUnknown && !pathRef.getBounds().isEmpty) { return _setComputedConvexity(state.reversals < 3 @@ -1455,9 +1474,9 @@ class SurfacePath implements ui.Path { final PathRefIterator iter = PathRefIterator(pathRef); final Float32List points = pathRef.points; int verb; - _CubicBounds? cubicBounds; - _QuadBounds? quadBounds; - _ConicBounds? conicBounds; + CubicBounds? cubicBounds; + QuadBounds? quadBounds; + ConicBounds? conicBounds; while ((verb = iter.nextIndex()) != SPath.kDoneVerb) { final int pIndex = iter.iterIndex; switch (verb) { @@ -1470,7 +1489,7 @@ class SurfacePath implements ui.Path { minY = maxY = points[pIndex + 3]; break; case SPath.kQuadVerb: - quadBounds ??= _QuadBounds(); + quadBounds ??= QuadBounds(); quadBounds.calculateBounds(points, pIndex); minX = quadBounds.minX; minY = quadBounds.minY; @@ -1478,7 +1497,7 @@ class SurfacePath implements ui.Path { maxY = quadBounds.maxY; break; case SPath.kConicVerb: - conicBounds ??= _ConicBounds(); + conicBounds ??= ConicBounds(); conicBounds.calculateBounds(points, iter.conicWeight, pIndex); minX = conicBounds.minX; minY = conicBounds.minY; @@ -1486,7 +1505,7 @@ class SurfacePath implements ui.Path { maxY = conicBounds.maxY; break; case SPath.kCubicVerb: - cubicBounds ??= _CubicBounds(); + cubicBounds ??= CubicBounds(); cubicBounds.calculateBounds(points, pIndex); minX = cubicBounds.minX; minY = cubicBounds.minY; @@ -1507,7 +1526,7 @@ class SurfacePath implements ui.Path { bottom = math.max(bottom, maxY); } } - ui.Rect newBounds = ltrbInitialized + final ui.Rect newBounds = ltrbInitialized ? ui.Rect.fromLTRB(left, top, right, bottom) : ui.Rect.zero; pathRef.getBounds(); @@ -1521,27 +1540,41 @@ class SurfacePath implements ui.Path { /// as if they had been closed, even if they were not explicitly closed. @override SurfacePathMetrics computeMetrics({bool forceClosed = false}) { - return SurfacePathMetrics._(this, forceClosed); + return SurfacePathMetrics(pathRef, forceClosed); } - /// Detects if path is rounded rectangle and returns rounded rectangle or - /// null. + /// Detects if path is rounded rectangle. + /// + /// Returns rounded rectangle or null. /// /// Used for web optimization of physical shape represented as /// a persistent div. - ui.RRect? get webOnlyPathAsRoundedRect => pathRef.getRRect(); + ui.RRect? toRoundedRect() => pathRef.getRRect(); - /// Detects if path is simple rectangle and returns rectangle or null. + /// Detects if path is simple rectangle. + /// + /// Returns rectangle or null. + /// + /// Used for web optimization of physical shape represented as + /// a persistent div. !Warning it does not detect if closed, don't use this + /// for optimizing strokes. + ui.Rect? toRect() => pathRef.getRect(); + + /// Detects if path is a vertical or horizontal line. + /// + /// Returns LTRB or null. /// /// Used for web optimization of physical shape represented as /// a persistent div. - ui.Rect? get webOnlyPathAsRect => pathRef.getRect(); + ui.Rect? toStraightLine() => pathRef.getStraightLine(); - /// Detects if path is simple oval and returns bounding rectangle or null. + /// Detects if path is simple oval. + /// + /// Returns bounding rectangle or null. /// /// Used for web optimization of physical shape represented as /// a persistent div. - ui.Rect? get webOnlyPathAsCircle => + ui.Rect? toCircle() => pathRef.isOval == -1 ? null : pathRef.getBounds(); /// Returns if Path is empty. @@ -1637,11 +1670,3 @@ bool _isSimple2dTransform(Float32List m) => m[2] == 0.0; // m[1] - 2D rotation is simple // m[0] - scale x is simple - -double _lengthSquaredOffset(ui.Offset offset) { - final double dx = offset.dx; - final double dy = offset.dy; - return dx * dx + dy * dy; -} - -double _lengthSquared(double dx, double dy) => dx * dx + dy * dy; diff --git a/lib/web_ui/lib/src/engine/html/path/path_iterator.dart b/lib/web_ui/lib/src/engine/html/path/path_iterator.dart new file mode 100644 index 0000000000000..90793a95004f0 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/path/path_iterator.dart @@ -0,0 +1,220 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'path_ref.dart'; +import 'path_utils.dart'; + +// Iterates through path including generating closing segments. +class PathIterator { + PathIterator(this.pathRef, bool forceClose) + : _forceClose = forceClose, + _verbCount = pathRef.countVerbs() { + _pointIndex = 0; + if (!pathRef.isFinite) { + // Don't allow iteration through non-finite points, prepare to return + // done verb. + _verbIndex = pathRef.countVerbs(); + } + } + + final PathRef pathRef; + final bool _forceClose; + final int _verbCount; + + bool _needClose = false; + int _segmentState = SPathSegmentState.kEmptyContour; + int _conicWeightIndex = -1; + double _lastPointX = 0; + double _lastPointY = 0; + double _moveToX = 0; + double _moveToY = 0; + int _verbIndex = 0; + int _pointIndex = 0; + + int get pathVerbIndex => _verbIndex; + int get conicWeightIndex => _conicWeightIndex; + + /// Maximum buffer size required for points in [next] calls. + static const int kMaxBufferSize = 8; + + /// Returns true if first contour on path is closed. + bool isClosedContour() { + if (_verbCount == 0 || _verbIndex == _verbCount) { + return false; + } + if (_forceClose) { + return true; + } + int verbIndex = 0; + // Skip starting moveTo. + if (pathRef.atVerb(verbIndex) == SPath.kMoveVerb) { + ++verbIndex; + } + while (verbIndex < _verbCount) { + final int verb = pathRef.atVerb(verbIndex++); + if (SPath.kMoveVerb == verb) { + break; + } + if (SPath.kCloseVerb == verb) { + return true; + } + } + return false; + } + + int _autoClose(Float32List outPts) { + if (_lastPointX != _moveToX || _lastPointY != _moveToY) { + // Handle special case where comparison above will return true for + // NaN != NaN although it should be false. + if (_lastPointX.isNaN || + _lastPointY.isNaN || + _moveToX.isNaN || + _moveToY.isNaN) { + return SPath.kCloseVerb; + } + outPts[0] = _lastPointX; + outPts[1] = _lastPointY; + outPts[2] = _moveToX; + outPts[3] = _moveToY; + _lastPointX = _moveToX; + _lastPointY = _moveToY; + return SPath.kLineVerb; + } else { + outPts[0] = _moveToX; + outPts[1] = _moveToY; + return SPath.kCloseVerb; + } + } + + // Returns true if caller should use moveTo, false if last point of + // previous primitive. + ui.Offset _constructMoveTo() { + if (_segmentState == SPathSegmentState.kAfterMove) { + // Set the first return point to move point. + _segmentState = SPathSegmentState.kAfterPrimitive; + return ui.Offset(_moveToX, _moveToY); + } + return ui.Offset( + pathRef.points[_pointIndex - 2], pathRef.points[_pointIndex - 1]); + } + + int peek() { + if (_verbIndex < pathRef.countVerbs()) { + return pathRef.atVerb(_verbIndex); + } + if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { + return (_lastPointX != _moveToX || _lastPointY != _moveToY) + ? SPath.kLineVerb + : SPath.kCloseVerb; + } + return SPath.kDoneVerb; + } + + // Returns next verb and reads associated points into [outPts]. + int next(Float32List outPts) { + if (_verbIndex == pathRef.countVerbs()) { + // Close the curve if requested and if there is some curve to close + if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { + if (SPath.kLineVerb == _autoClose(outPts)) { + return SPath.kLineVerb; + } + _needClose = false; + return SPath.kCloseVerb; + } + return SPath.kDoneVerb; + } + int verb = pathRef.atVerb(_verbIndex++); + switch (verb) { + case SPath.kMoveVerb: + if (_needClose) { + // Move back one verb. + _verbIndex--; + final int autoVerb = _autoClose(outPts); + if (autoVerb == SPath.kCloseVerb) { + _needClose = false; + } + return autoVerb; + } + if (_verbIndex == _verbCount) { + return SPath.kDoneVerb; + } + final double offsetX = pathRef.points[_pointIndex++]; + final double offsetY = pathRef.points[_pointIndex++]; + _moveToX = offsetX; + _moveToY = offsetY; + outPts[0] = offsetX; + outPts[1] = offsetY; + _segmentState = SPathSegmentState.kAfterMove; + _lastPointX = _moveToX; + _lastPointY = _moveToY; + _needClose = _forceClose; + break; + case SPath.kLineVerb: + final ui.Offset start = _constructMoveTo(); + final double offsetX = pathRef.points[_pointIndex++]; + final double offsetY = pathRef.points[_pointIndex++]; + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = offsetX; + outPts[3] = offsetY; + _lastPointX = offsetX; + _lastPointY = offsetY; + break; + case SPath.kConicVerb: + _conicWeightIndex++; + final ui.Offset start = _constructMoveTo(); + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = pathRef.points[_pointIndex++]; + outPts[3] = pathRef.points[_pointIndex++]; + _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; + _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; + break; + case SPath.kQuadVerb: + final ui.Offset start = _constructMoveTo(); + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = pathRef.points[_pointIndex++]; + outPts[3] = pathRef.points[_pointIndex++]; + _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; + _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; + break; + case SPath.kCubicVerb: + final ui.Offset start = _constructMoveTo(); + outPts[0] = start.dx; + outPts[1] = start.dy; + outPts[2] = pathRef.points[_pointIndex++]; + outPts[3] = pathRef.points[_pointIndex++]; + outPts[4] = pathRef.points[_pointIndex++]; + outPts[5] = pathRef.points[_pointIndex++]; + _lastPointX = outPts[6] = pathRef.points[_pointIndex++]; + _lastPointY = outPts[7] = pathRef.points[_pointIndex++]; + break; + case SPath.kCloseVerb: + verb = _autoClose(outPts); + if (verb == SPath.kLineVerb) { + // Move back one verb since we constructed line for this close verb. + _verbIndex--; + } else { + _needClose = false; + _segmentState = SPathSegmentState.kEmptyContour; + } + _lastPointX = _moveToX; + _lastPointY = _moveToY; + break; + case SPath.kDoneVerb: + assert(_verbIndex == pathRef.countVerbs()); + break; + default: + throw FormatException('Unsupport Path verb $verb'); + } + return verb; + } + + double get conicWeight => pathRef.atWeight(_conicWeightIndex); +} diff --git a/lib/web_ui/lib/src/engine/html/path/path_metrics.dart b/lib/web_ui/lib/src/engine/html/path/path_metrics.dart index fa640e415c85c..7109391c98447 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_metrics.dart @@ -2,8 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:collection' show IterableBase; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'conic.dart'; +import 'cubic.dart'; +import 'path_iterator.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; const double kEpsilon = 0.000000001; @@ -23,7 +32,7 @@ const double kEpsilon = 0.000000001; /// are only valid until the next one is obtained. class SurfacePathMetrics extends IterableBase implements ui.PathMetrics { - SurfacePathMetrics._(SurfacePath path, bool forceClosed) + SurfacePathMetrics(PathRef path, bool forceClosed) : _iterator = SurfacePathMetricIterator._(_SurfacePathMeasure(path, forceClosed)); @@ -42,12 +51,12 @@ class _SurfacePathMeasure { : // nextContour will increment this to the zero based index. _currentContourIndex = -1, - _pathIterator = PathIterator(_path.pathRef, forceClosed); + _pathIterator = PathIterator(_path, forceClosed); final double resScale; - final SurfacePath _path; + final PathRef _path; PathIterator _pathIterator; - final List<_PathContourMeasure> _contours = []; + final List<_PathContourMeasure> _contours = <_PathContourMeasure>[]; // If the contour ends with a call to [Path.close] (which may // have been implied when using [Path.addRect]) @@ -104,17 +113,17 @@ class _SurfacePathMeasure { // calling `_moveNext` - `_moveNext` should be called after the first // iteration is done instead of before. bool _nativeNextContour() { - if (_verbIterIndex == _path.pathRef.countVerbs()) { + if (_verbIterIndex == _path.countVerbs()) { return false; } - _PathContourMeasure measure = - _PathContourMeasure(_path.pathRef, _pathIterator, forceClosed); + final _PathContourMeasure measure = + _PathContourMeasure(_path, _pathIterator, forceClosed); _verbIterIndex = measure.verbEndIndex; _contours.add(measure); return true; } - ui.Path? extractPath(int contourIndex, double start, double end, + ui.Path extractPath(int contourIndex, double start, double end, {bool startWithMoveTo = true}) { return _contours[contourIndex].extractPath(start, end, startWithMoveTo); } @@ -129,7 +138,7 @@ class _PathContourMeasure { final PathRef pathRef; int _verbEndIndex = 0; - final List<_PathSegment> _segments = []; + final List<_PathSegment> _segments = <_PathSegment>[]; // Allocate buffer large enough for returning cubic curve chop result. // 2 floats for each coordinate x (start, end & control point 1 & 2). static final Float32List _buffer = Float32List(8); @@ -143,7 +152,7 @@ class _PathContourMeasure { bool _isClosed = false; ui.Tangent? getTangentForOffset(double distance) { - final segmentIndex = _segmentIndexAtDistance(distance); + final int segmentIndex = _segmentIndexAtDistance(distance); if (segmentIndex == -1) { return null; } @@ -169,7 +178,7 @@ class _PathContourMeasure { int lo = 0; int hi = _segments.length - 1; while (lo < hi) { - int mid = (lo + hi) >> 1; + final int mid = (lo + hi) >> 1; if (_segments[mid].distance < distance) { lo = mid + 1; } else { @@ -183,7 +192,7 @@ class _PathContourMeasure { } _SurfaceTangent _getPosTan(int segmentIndex, double distance) { - _PathSegment segment = _segments[segmentIndex]; + final _PathSegment segment = _segments[segmentIndex]; // Compute distance to segment. Since distance is cumulative to find // t = 0..1 on the segment, we need to calculate start distance using prior // segment. @@ -196,7 +205,7 @@ class _PathContourMeasure { return segment.computeTangent(t); } - ui.Path? extractPath( + ui.Path extractPath( double startDistance, double stopDistance, bool startWithMoveTo) { if (startDistance < 0) { startDistance = 0; @@ -204,14 +213,14 @@ class _PathContourMeasure { if (stopDistance > _contourLength) { stopDistance = _contourLength; } + final ui.Path path = ui.Path(); if (startDistance > stopDistance || _segments.isEmpty) { - return null; + return path; } - final ui.Path path = ui.Path(); - int startSegmentIndex = _segmentIndexAtDistance(startDistance); - int stopSegmentIndex = _segmentIndexAtDistance(stopDistance); + final int startSegmentIndex = _segmentIndexAtDistance(startDistance); + final int stopSegmentIndex = _segmentIndexAtDistance(stopDistance); if (startSegmentIndex == -1 || stopSegmentIndex == -1) { - return null; + return path; } int currentSegmentIndex = startSegmentIndex; _PathSegment seg = _segments[currentSegmentIndex]; @@ -254,7 +263,7 @@ class _PathContourMeasure { path.lineTo(toX, toY); break; case SPath.kCubicVerb: - _chopCubicBetweenT(points, startT, stopT, _buffer); + chopCubicBetweenT(points, startT, stopT, _buffer); path.cubicTo(_buffer[2], _buffer[3], _buffer[4], _buffer[5], _buffer[6], _buffer[7]); break; @@ -278,8 +287,7 @@ class _PathContourMeasure { double distance = 0.0; bool haveSeenMoveTo = false; - final Function lineToHandler = - (double fromX, double fromY, double x, double y) { + void lineToHandler(double fromX, double fromY, double x, double y) { final double dx = fromX - x; final double dy = fromY - y; final double prevDistance = distance; @@ -288,10 +296,12 @@ class _PathContourMeasure { // actually made it larger, since a very small delta might be > 0, but // still have no effect on distance (if distance >>> delta). if (distance > prevDistance) { - _segments - .add(_PathSegment(SPath.kLineVerb, distance, [fromX, fromY, x, y])); + _segments.add( + _PathSegment(SPath.kLineVerb, distance, [fromX, fromY, x, y]), + ); } - }; + } + int verb = 0; final Float32List points = Float32List(PathRefIterator.kMaxBufferSize); do { @@ -327,9 +337,9 @@ class _PathContourMeasure { case SPath.kConicVerb: assert(haveSeenMoveTo); final double w = iter.conicWeight; - Conic conic = Conic(points[0], points[1], points[2], points[3], + final Conic conic = Conic(points[0], points[1], points[2], points[3], points[4], points[5], w); - List conicPoints = conic.toQuads(); + final List conicPoints = conic.toQuads(); final int len = conicPoints.length; double startX = conicPoints[0].dx; double startY = conicPoints[0].dy; @@ -352,13 +362,13 @@ class _PathContourMeasure { break; case SPath.kCloseVerb: _contourLength = distance; - return iter._verbIndex; + return iter.pathVerbIndex; default: break; } } while (verb != SPath.kDoneVerb); _contourLength = distance; - return iter._verbIndex; + return iter.pathVerbIndex; } static bool _tspanBigEnough(int tSpan) => (tSpan >> 10) != 0; @@ -541,6 +551,7 @@ class SurfacePathMetric implements ui.PathMetric { /// have been implied when using methods like [Path.addRect]) or if /// `forceClosed` was specified as true in the call to [Path.computeMetrics]. /// Returns false otherwise. + @override final bool isClosed; /// The zero-based index of the contour. @@ -555,6 +566,7 @@ class SurfacePathMetric implements ui.PathMetric { /// the contours of the path at the time the path's metrics were computed. If /// additional contours were added or existing contours updated, this metric /// will be invalid for the current state of the path. + @override final int contourIndex; final _SurfacePathMeasure _measure; @@ -574,13 +586,12 @@ class SurfacePathMetric implements ui.PathMetric { return _measure.getTangentForOffset(contourIndex, distance); } - /// Given a start and stop distance, return the intervening segment(s). + /// Given a start and end distance, return the intervening segment(s). /// /// `start` and `end` are pinned to legal values (0..[length]) - /// Returns null if the segment is 0 length or `start` > `stop`. /// Begin the segment with a moveTo if `startWithMoveTo` is true. - ui.Path? extractPath(double start, double end, - {bool startWithMoveTo = true}) { + @override + ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { return _measure.extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo); } @@ -593,7 +604,7 @@ class SurfacePathMetric implements ui.PathMetric { ui.Offset _normalizeSlope(double dx, double dy) { final double length = math.sqrt(dx * dx + dy * dy); return length < kEpsilon - ? ui.Offset(0.0, 0.0) + ? const ui.Offset(0.0, 0.0) : ui.Offset(dx / length, dy / length); } @@ -634,8 +645,8 @@ class _PathSegment { _SurfaceTangent tangentForQuadAt(double t, double x0, double y0, double x1, double y1, double x2, double y2) { assert(t >= 0 && t <= 1); - final _SkQuadCoefficients _quadEval = - _SkQuadCoefficients(x0, y0, x1, y1, x2, y2); + final SkQuadCoefficients _quadEval = + SkQuadCoefficients(x0, y0, x1, y1, x2, y2); final ui.Offset pos = ui.Offset(_quadEval.evalX(t), _quadEval.evalY(t)); // Derivative of quad curve is 2(b - a + (a - 2b + c)t). // If control point is at start or end point, this yields 0 for t = 0 and @@ -684,23 +695,6 @@ class _PathSegment { } } -/// Evaluates A * t^2 + B * t + C = 0 for quadratic curve. -class _SkQuadCoefficients { - _SkQuadCoefficients( - double x0, double y0, double x1, double y1, double x2, double y2) - : cx = x0, - cy = y0, - bx = 2 * (x1 - x0), - by = 2 * (y1 - y0), - ax = x2 - (2 * x1) + x0, - ay = y2 - (2 * y1) + y0; - final double ax, ay, bx, by, cx, cy; - - double evalX(double t) => (ax * t + bx) * t + cx; - - double evalY(double t) => (ay * t + by) * t + cy; -} - // Evaluates A * t^3 + B * t^2 + Ct + D = 0 for cubic curve. class _SkCubicCoefficients { final double ax, ay, bx, by, cx, cy, dx, dy; @@ -735,12 +729,12 @@ void _chopQuadBetweenT( final bool chopStart = startT != 0; final double t = chopStart ? startT : stopT; - final double ab1x = _interpolate(p0x, p1x, t); - final double ab1y = _interpolate(p0y, p1y, t); - final double bc1x = _interpolate(p1x, p2x, t); - final double bc1y = _interpolate(p1y, p2y, t); - final double abc1x = _interpolate(ab1x, bc1x, t); - final double abc1y = _interpolate(ab1y, bc1y, t); + final double ab1x = interpolate(p0x, p1x, t); + final double ab1y = interpolate(p0y, p1y, t); + final double bc1x = interpolate(p1x, p2x, t); + final double bc1y = interpolate(p1y, p2y, t); + final double abc1x = interpolate(ab1x, bc1x, t); + final double abc1y = interpolate(ab1y, bc1y, t); if (!chopStart) { // Return left side of curve. buffer[0] = p0x; @@ -764,12 +758,12 @@ void _chopQuadBetweenT( // We chopped at startT, now the right hand side of curve is at // abc1x, abc1y, bc1x, bc1y, p2x, p2y final double endT = (stopT - startT) / (1 - startT); - final double ab2x = _interpolate(abc1x, bc1x, endT); - final double ab2y = _interpolate(abc1y, bc1y, endT); - final double bc2x = _interpolate(bc1x, p2x, endT); - final double bc2y = _interpolate(bc1y, p2y, endT); - final double abc2x = _interpolate(ab2x, bc2x, endT); - final double abc2y = _interpolate(ab2y, bc2y, endT); + final double ab2x = interpolate(abc1x, bc1x, endT); + final double ab2y = interpolate(abc1y, bc1y, endT); + final double bc2x = interpolate(bc1x, p2x, endT); + final double bc2y = interpolate(bc1y, p2y, endT); + final double abc2x = interpolate(ab2x, bc2x, endT); + final double abc2y = interpolate(ab2y, bc2y, endT); buffer[0] = abc1x; buffer[1] = abc1y; diff --git a/lib/web_ui/lib/src/engine/html/path/path_ref.dart b/lib/web_ui/lib/src/engine/html/path/path_ref.dart index 2b9c9f094ca09..14f14a3756a72 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_ref.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_ref.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:js_util' as js_util; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../util.dart'; +import 'path_utils.dart'; /// Stores the path verbs, points and conic weights. /// @@ -15,7 +21,7 @@ part of engine; /// to update caches due to content changes. class PathRef { PathRef() - : _fPoints = Float32List(kInitialPointsCapacity * 2), + : fPoints = Float32List(kInitialPointsCapacity * 2), _fVerbs = Uint8List(kInitialVerbsCapacity) { _fPointsCapacity = kInitialPointsCapacity; _fVerbsCapacity = kInitialVerbsCapacity; @@ -42,12 +48,14 @@ class PathRef { static const int kInitialPointsCapacity = 8; static const int kInitialVerbsCapacity = 8; + /// Bounds of points that define path. ui.Rect? fBounds; + /// Computed tight bounds of path (may exclude curve control points). ui.Rect? cachedBounds; int _fPointsCapacity = 0; int _fPointsLength = 0; int _fVerbsCapacity = 0; - Float32List _fPoints; + Float32List fPoints; Uint8List _fVerbs; int _fVerbsLength = 0; int _conicWeightsCapacity = 0; @@ -73,17 +81,17 @@ class PathRef { /// Given a point index stores [x],[y]. void setPoint(int pointIndex, double x, double y) { assert(pointIndex < _fPointsLength); - int index = pointIndex * 2; - _fPoints[index] = x; - _fPoints[index + 1] = y; + final int index = pointIndex * 2; + fPoints[index] = x; + fPoints[index + 1] = y; } /// Creates a copy of the path by pointing new path to a current /// points,verbs and weights arrays. If original path is mutated by adding /// more verbs, this copy only returns path at the time of copy and shares /// typed arrays of original path. - PathRef._shallowCopy(PathRef ref) - : _fPoints = ref._fPoints, + PathRef.shallowCopy(PathRef ref) + : fPoints = ref.fPoints, _fVerbs = ref._fVerbs { _fVerbsCapacity = ref._fVerbsCapacity; _fVerbsLength = ref._fVerbsLength; @@ -109,7 +117,7 @@ class PathRef { debugValidate(); } - Float32List get points => _fPoints; + Float32List get points => fPoints; Float32List? get conicWeights => _conicWeights; int countPoints() => _fPointsLength; @@ -122,9 +130,13 @@ class PathRef { } ui.Offset atPoint(int index) { - return ui.Offset(_fPoints[index * 2], _fPoints[index * 2 + 1]); + return ui.Offset(fPoints[index * 2], fPoints[index * 2 + 1]); } + double pointXAt(int index) => fPoints[index * 2]; + + double pointYAt(int index) => fPoints[index * 2 + 1]; + double atWeight(int index) { return _conicWeights![index]; } @@ -154,7 +166,16 @@ class PathRef { int get isRRect => fIsRRect ? fRRectOrOvalStartIdx : -1; int get isRect => fIsRect ? fRRectOrOvalStartIdx : -1; ui.RRect? getRRect() => fIsRRect ? _getRRect() : null; - ui.Rect? getRect() => fIsRect ? _getRect() : null; + ui.Rect? getRect() { + /// Use _detectRect() for detection if explicitly addRect was used (fIsRect) or + /// it is a potential due to moveTo + 3 lineTo verbs. + if (fIsRect) { + return ui.Rect.fromLTRB( + atPoint(0).dx, atPoint(0).dy, atPoint(1).dx, atPoint(2).dy); + } else { + return _fVerbsLength == 4 ? _detectRect() : null; + } + } bool get isRectCCW => fRRectOrOvalIsCCW; bool get hasComputedBounds => !fBoundsIsDirty; @@ -171,9 +192,49 @@ class PathRef { } /// Reconstructs Rect from path commands. - ui.Rect _getRect() { - return ui.Rect.fromLTRB( - atPoint(0).dx, atPoint(0).dy, atPoint(1).dx, atPoint(2).dy); + /// + /// Detects clockwise starting with horizontal line. + ui.Rect? _detectRect() { + assert(_fVerbs[0] == SPath.kMoveVerb); + final double x0 = atPoint(0).dx; + final double y0 = atPoint(0).dy; + final double x1 = atPoint(1).dx; + final double y1 = atPoint(1).dy; + if (_fVerbs[1] != SPath.kLineVerb || y1 != y0) { + return null; + } + final double width = x1 - x0; + final double x2 = atPoint(2).dx; + final double y2 = atPoint(2).dy; + if (_fVerbs[2] != SPath.kLineVerb || x2 != x1) { + return null; + } + final double height = y2 - y1; + final double x3 = atPoint(3).dx; + final double y3 = atPoint(3).dy; + if (_fVerbs[3] != SPath.kLineVerb || y3 != y2) { + return null; + } + if ((x2 - x3) != width || (y3 - y0) != height) { + return null; + } + return ui.Rect.fromLTWH(x0, y0, width, height); + } + + /// Returns horizontal/vertical line bounds or null if not a line. + ui.Rect? getStraightLine() { + if (_fVerbsLength != 2 || _fVerbs[0] != SPath.kMoveVerb || + _fVerbs[1] != SPath.kLineVerb) { + return null; + } + final double x0 = fPoints[0]; + final double y0 = fPoints[1]; + final double x1 = fPoints[2]; + final double y1 = fPoints[3]; + if (y0 == y1 || x0 == x1) { + return ui.Rect.fromLTRB(x0, y0, x1, y1); + } + return null; } /// Reconstructs RRect from path commands. @@ -181,7 +242,7 @@ class PathRef { /// Expect 4 Conics and lines between. /// Use conic points to calculate corner radius. ui.RRect _getRRect() { - ui.Rect bounds = getBounds(); + final ui.Rect bounds = getBounds(); // Radii x,y of 4 corners final List radii = []; final PathRefIterator iter = PathRefIterator(this); @@ -193,10 +254,10 @@ class PathRef { if (SPath.kConicVerb == verb) { final double controlPx = pts[2]; final double controlPy = pts[3]; - double vector1_0x = controlPx - pts[0]; - double vector1_0y = controlPy - pts[1]; - double vector2_1x = pts[4] - pts[2]; - double vector2_1y = pts[5] - pts[3]; + final double vector1_0x = controlPx - pts[0]; + final double vector1_0y = controlPy - pts[1]; + final double vector2_1x = pts[4] - pts[2]; + final double vector2_1y = pts[5] - pts[3]; double dx, dy; // Depending on the corner we have control point at same // horizontal position as startpoint or same vertical position. @@ -216,11 +277,11 @@ class PathRef { dy = vector1_0y.abs(); } if (assertionsEnabled) { - final int checkCornerIndex = _nearlyEqual(controlPx, bounds.left) - ? (_nearlyEqual(controlPy, bounds.top) + final int checkCornerIndex = SPath.nearlyEqual(controlPx, bounds.left) + ? (SPath.nearlyEqual(controlPy, bounds.top) ? _Corner.kUpperLeft : _Corner.kLowerLeft) - : (_nearlyEqual(controlPy, bounds.top) + : (SPath.nearlyEqual(controlPy, bounds.top) ? _Corner.kUpperRight : _Corner.kLowerRight); assert(checkCornerIndex == cornerIndex); @@ -240,6 +301,7 @@ class PathRef { bottomLeft: radii[_Corner.kLowerLeft]); } + @override bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -247,9 +309,13 @@ class PathRef { if (other.runtimeType != runtimeType) { return false; } - return equals(other as PathRef); + return other is PathRef && equals(other); } + @override + int get hashCode => ui.hashValues(fSegmentMask, + fPoints, _conicWeights, _fVerbs); + bool equals(PathRef ref) { // We explicitly check fSegmentMask as a quick-reject. We could skip it, // since it is only a cache of info in the fVerbs, but its a fast way to @@ -262,8 +328,10 @@ class PathRef { if (pointCount != ref.countPoints()) { return false; } - for (int i = 0, len = pointCount * 2; i < len; i++) { - if (_fPoints[i] != ref._fPoints[i]) { + + final int len = pointCount * 2; + for (int i = 0; i < len; i++) { + if (fPoints[i] != ref.fPoints[i]) { return false; } } @@ -307,7 +375,8 @@ class PathRef { final int sourceCapacity = source._fPointsCapacity; final Float32List dest = Float32List(sourceCapacity * 2); final Float32List sourcePoints = source.points; - for (int i = 0, len = sourceLength * 2; i < len; i += 2) { + final int len = sourceLength * 2; + for (int i = 0; i < len; i += 2) { dest[i] = sourcePoints[i] + offsetX; dest[i + 1] = sourcePoints[i + 1] + offsetY; } @@ -315,14 +384,14 @@ class PathRef { } static Uint8List _fVerbsFromSource(PathRef source) { - Uint8List verbs = Uint8List(source._fVerbsCapacity); + final Uint8List verbs = Uint8List(source._fVerbsCapacity); verbs.setAll(0, source._fVerbs); return verbs; } /// Returns a new path by translating [source] by [offsetX], [offsetY]. PathRef.shiftedFrom(PathRef source, double offsetX, double offsetY) - : _fPoints = _fPointsFromSource(source, offsetX, offsetY), + : fPoints = _fPointsFromSource(source, offsetX, offsetY), _fVerbs = _fVerbsFromSource(source) { _conicWeightsCapacity = source._conicWeightsCapacity; _conicWeightsLength = source._conicWeightsLength; @@ -360,8 +429,8 @@ class PathRef { resetToSize(verbCount, pointCount, weightCount, additionalReserveVerbs, additionalReservePoints); - js_util.callMethod(_fVerbs, 'set', [ref._fVerbs]); - js_util.callMethod(_fPoints, 'set', [ref._fPoints]); + js_util.callMethod(_fVerbs, 'set', [ref._fVerbs]); + js_util.callMethod(fPoints, 'set', [ref.fPoints]); if (ref._conicWeights == null) { _conicWeights = null; } else { @@ -386,9 +455,9 @@ class PathRef { void _resizePoints(int newLength) { if (newLength > _fPointsCapacity) { _fPointsCapacity = newLength + 10; - Float32List newPoints = Float32List(_fPointsCapacity * 2); - js_util.callMethod(newPoints, 'set', [_fPoints]); - _fPoints = newPoints; + final Float32List newPoints = Float32List(_fPointsCapacity * 2); + js_util.callMethod(newPoints, 'set', [fPoints]); + fPoints = newPoints; } _fPointsLength = newLength; } @@ -396,7 +465,7 @@ class PathRef { void _resizeVerbs(int newLength) { if (newLength > _fVerbsCapacity) { _fVerbsCapacity = newLength + 8; - Uint8List newVerbs = Uint8List(_fVerbsCapacity); + final Uint8List newVerbs = Uint8List(_fVerbsCapacity); js_util.callMethod(newVerbs, 'set', [_fVerbs]); _fVerbs = newVerbs; } @@ -406,7 +475,7 @@ class PathRef { void _resizeConicWeights(int newLength) { if (newLength > _conicWeightsCapacity) { _conicWeightsCapacity = newLength + 4; - Float32List newWeights = Float32List(_conicWeightsCapacity); + final Float32List newWeights = Float32List(_conicWeightsCapacity); if (_conicWeights != null) { js_util.callMethod(newWeights, 'set', [_conicWeights]); } @@ -415,7 +484,7 @@ class PathRef { _conicWeightsLength = newLength; } - void _append(PathRef source) { + void append(PathRef source) { final int pointCount = source.countPoints(); final int curLength = _fPointsLength; final int newPointCount = curLength + pointCount; @@ -425,7 +494,7 @@ class PathRef { for (int source = pointCount * 2 - 1, dst = newPointCount * 2 - 1; source >= 0; source--, dst--) { - _fPoints[dst] = sourcePoints[source]; + fPoints[dst] = sourcePoints[source]; } final int verbCount = countVerbs(); final int newVerbCount = source.countVerbs(); @@ -437,8 +506,8 @@ class PathRef { final int weightCount = countWeights(); final int newWeightCount = source.countWeights(); _resizeConicWeights(weightCount + newWeightCount); - Float32List sourceWeights = source._conicWeights!; - Float32List dest = _conicWeights!; + final Float32List sourceWeights = source._conicWeights!; + final Float32List dest = _conicWeights!; for (int i = 0; i < newWeightCount; i++) { dest[weightCount + i] = sourceWeights[i]; } @@ -448,9 +517,9 @@ class PathRef { /// Doesn't read fSegmentMask, but (re)computes it from the verbs array int computeSegmentMask() { - Uint8List verbs = _fVerbs; + final Uint8List verbs = _fVerbs; int mask = 0; - int verbCount = countVerbs(); + final int verbCount = countVerbs(); for (int i = 0; i < verbCount; ++i) { switch (verbs[i]) { case SPath.kLineVerb: @@ -494,7 +563,7 @@ class PathRef { void _computeBounds() { debugValidate(); assert(fBoundsIsDirty); - int pointCount = countPoints(); + final int pointCount = countPoints(); fBoundsIsDirty = false; cachedBounds = null; double accum = 0; @@ -503,21 +572,22 @@ class PathRef { fIsFinite = true; } else { double minX, maxX, minY, maxY; - minX = maxX = _fPoints[0]; + minX = maxX = fPoints[0]; accum *= minX; - minY = maxY = _fPoints[1]; + minY = maxY = fPoints[1]; accum *= minY; - for (int i = 2, len = 2 * pointCount; i < len; i += 2) { - final double x = _fPoints[i]; + final int len = 2 * pointCount; + for (int i = 2; i < len; i += 2) { + final double x = fPoints[i]; accum *= x; - final double y = _fPoints[i + 1]; + final double y = fPoints[i + 1]; accum *= y; minX = math.min(minX, x); minY = math.min(minY, y); maxX = math.max(maxX, x); maxY = math.max(maxY, y); } - bool allFinite = (accum * 0 == 0); + final bool allFinite = accum * 0 == 0; if (allFinite) { fBounds = ui.Rect.fromLTRB(minX, minY, maxX, maxY); fIsFinite = true; @@ -585,13 +655,13 @@ class PathRef { break; case SPath.kDoneVerb: if (assertionsEnabled) { - throw Exception("growForVerb called for kDone"); + throw Exception('growForVerb called for kDone'); } pCnt = 0; break; default: if (assertionsEnabled) { - throw Exception("default is not reached"); + throw Exception('default is not reached'); } pCnt = 0; break; @@ -601,7 +671,7 @@ class PathRef { fBoundsIsDirty = true; // this also invalidates fIsFinite startEdit(); - int verbCount = countVerbs(); + final int verbCount = countVerbs(); _resizeVerbs(verbCount + 1); _fVerbs[verbCount] = verb; @@ -652,13 +722,13 @@ class PathRef { break; case SPath.kDoneVerb: if (assertionsEnabled) { - throw Exception("growForVerb called for kDone"); + throw Exception('growForVerb called for kDone'); } pCnt = 0; break; default: if (assertionsEnabled) { - throw Exception("default is not reached"); + throw Exception('default is not reached'); } pCnt = 0; break; @@ -671,7 +741,7 @@ class PathRef { if (SPath.kConicVerb == verb) { _resizeConicWeights(countWeights() + numVbs); } - int verbCount = countVerbs(); + final int verbCount = countVerbs(); _resizeVerbs(verbCount + numVbs); for (int i = 0; i < numVbs; i++) { _fVerbs[verbCount + i] = verb; @@ -693,23 +763,23 @@ class PathRef { fSegmentMask |= path.fSegmentMask; fBoundsIsDirty = true; // this also invalidates fIsFinite - int numVerbs = path.countVerbs(); + final int numVerbs = path.countVerbs(); if (numVerbs != 0) { - int curLength = countVerbs(); + final int curLength = countVerbs(); _resizePoints(curLength + numVerbs); _fVerbs.setAll(curLength, path._fVerbs); } final int numPts = path.countPoints(); if (numPts != 0) { - int curLength = countPoints(); + final int curLength = countPoints(); _resizePoints(curLength + numPts); - _fPoints.setAll(curLength * 2, path._fPoints); + fPoints.setAll(curLength * 2, path.fPoints); } final int numConics = path.countWeights(); if (numConics != 0) { - int curLength = countWeights(); + final int curLength = countWeights(); _resizeConicWeights(curLength + numConics); final Float32List sourceWeights = path._conicWeights!; final Float32List destWeights = _conicWeights!; @@ -730,6 +800,7 @@ class PathRef { fIsRRect = false; fIsRect = false; cachedBounds = null; + fBoundsIsDirty = true; } void setIsOval(bool isOval, bool isCCW, int start) { @@ -752,7 +823,7 @@ class PathRef { Float32List getPoints() { debugValidate(); - return _fPoints; + return fPoints; } static const int kMinSize = 256; @@ -796,15 +867,16 @@ class PathRef { if (!fBoundsIsDirty && !fBounds!.isEmpty) { bool isFinite = true; - ui.Rect bounds = fBounds!; + final ui.Rect bounds = fBounds!; final double boundsLeft = bounds.left; final double boundsTop = bounds.top; final double boundsRight = bounds.right; final double boundsBottom = bounds.bottom; - for (int i = 0, len = _fPointsLength * 2; i < len; i += 2) { - final double pointX = _fPoints[i]; - final double pointY = _fPoints[i + 1]; - double tolerance = 0.0001; + final int len = _fPointsLength * 2; + for (int i = 0; i < len; i += 2) { + final double pointX = fPoints[i]; + final double pointY = fPoints[i + 1]; + const double tolerance = 0.0001; final bool pointIsFinite = pointX.isFinite && pointY.isFinite; if (pointIsFinite && (pointX + tolerance < boundsLeft || @@ -835,10 +907,10 @@ class PathRef { int findMaxY(int pointIndex, int count) { assert(count > 0); // move to y component. - double max = _fPoints[pointIndex * 2 + 1]; + double max = fPoints[pointIndex * 2 + 1]; int firstIndex = pointIndex; for (int i = 1; i < count; i++) { - double y = _fPoints[(pointIndex + i) * 2]; + final double y = fPoints[(pointIndex + i) * 2]; if (y > max) { max = y; firstIndex = pointIndex + i; @@ -859,8 +931,8 @@ class PathRef { // we wrapped around, so abort break; } - if (_fPoints[index * 2] != _fPoints[i * 2] || - _fPoints[index * 2 + 1] != _fPoints[i * 2 + 1]) { + if (fPoints[index * 2] != fPoints[i * 2] || + fPoints[index * 2 + 1] != fPoints[i * 2 + 1]) { // found a different point, success! break; } @@ -914,7 +986,7 @@ class PathRefIterator { if (_verbIndex == pathRef.countVerbs()) { return SPath.kDoneVerb; } - int verb = pathRef._fVerbs[_verbIndex++]; + final int verb = pathRef._fVerbs[_verbIndex++]; switch (verb) { case SPath.kMoveVerb: iterIndex = _pointIndex; @@ -953,7 +1025,7 @@ class PathRefIterator { if (_verbIndex == pathRef.countVerbs()) { return SPath.kDoneVerb; } - int verb = pathRef._fVerbs[_verbIndex++]; + final int verb = pathRef._fVerbs[_verbIndex++]; final Float32List points = pathRef.points; int pointIndex = _pointIndex; switch (verb) { diff --git a/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart b/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart index 85b5ab4cfb3c7..597c9d871667d 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart @@ -2,14 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'conic.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; /// Converts [path] to SVG path syntax to be used as "d" attribute in path /// element. -void pathToSvg(SurfacePath path, StringBuffer sb, +void pathToSvg(PathRef pathRef, StringBuffer sb, {double offsetX = 0, double offsetY = 0}) { - final PathRefIterator iter = PathRefIterator(path.pathRef); + final PathRefIterator iter = PathRefIterator(pathRef); int verb = 0; final Float32List outPts = Float32List(PathRefIterator.kMaxBufferSize); while ((verb = iter.next(outPts)) != SPath.kDoneVerb) { @@ -30,9 +35,9 @@ void pathToSvg(SurfacePath path, StringBuffer sb, break; case SPath.kConicVerb: final double w = iter.conicWeight; - Conic conic = Conic(outPts[0], outPts[1], outPts[2], outPts[3], + final Conic conic = Conic(outPts[0], outPts[1], outPts[2], outPts[3], outPts[4], outPts[5], w); - List points = conic.toQuads(); + final List points = conic.toQuads(); final int len = points.length; for (int i = 1; i < len; i += 2) { final double p1x = points[i].dx; diff --git a/lib/web_ui/lib/src/engine/html/path/path_utils.dart b/lib/web_ui/lib/src/engine/html/path/path_utils.dart index 9908cf52370c8..fde50c5e4156f 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_utils.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_utils.dart @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; + +import 'path_ref.dart'; /// Mask used to keep track of types of verbs used in a path segment. class SPathSegmentMask { @@ -23,7 +26,11 @@ class SPathVerb { static const int kClose = 5; // 0 points } -class SPath { +abstract class SPath { + // This class is not meant to be instantiated or extended; this constructor + // prevents instantiation and extension. + SPath._(); + static const int kMoveVerb = SPathVerb.kMove; static const int kLineVerb = SPathVerb.kLine; static const int kQuadVerb = SPathVerb.kQuad; @@ -56,6 +63,14 @@ class SPath { static int scalarSignedAsInt(double x) { return x < 0 ? -1 : ((x > 0) ? 1 : 0); } + + static bool nearlyEqual(double value1, double value2) => + (value1 - value2).abs() < SPath.scalarNearlyZero; + + // Snaps a value to zero if almost zero (within tolerance). + static double snapToZero(double value) => SPath.nearlyEqual(value, 0.0) ? 0.0 : value; + + static bool isInteger(double value) => value.floor() == value; } class SPathAddPathMode { @@ -99,21 +114,21 @@ class SPathSegmentState { /// Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) /// x1 = Q / A /// x2 = C / Q -class _QuadRoots { +class QuadRoots { double? root0; double? root1; - _QuadRoots(); + QuadRoots(); /// Returns roots as list. List get roots => (root0 == null) - ? [] + ? [] : (root1 == null ? [root0!] : [root0!, root1!]); int findRoots(double a, double b, double c) { int rootCount = 0; if (a == 0) { - root0 = _validUnitDivide(-c, b); + root0 = validUnitDivide(-c, b); return root0 == null ? 0 : 1; } @@ -126,13 +141,13 @@ class _QuadRoots { return 0; } - double q = (b < 0) ? -(b - dr) / 2 : -(b + dr) / 2; - double? res = _validUnitDivide(q, a); + final double q = (b < 0) ? -(b - dr) / 2 : -(b + dr) / 2; + double? res = validUnitDivide(q, a); if (res != null) { root0 = res; ++rootCount; } - res = _validUnitDivide(c, q); + res = validUnitDivide(c, q); if (res != null) { if (rootCount == 0) { root0 = res; @@ -155,7 +170,7 @@ class _QuadRoots { } } -double? _validUnitDivide(double numer, double denom) { +double? validUnitDivide(double numer, double denom) { if (numer < 0) { numer = -numer; denom = -denom; @@ -174,15 +189,7 @@ double? _validUnitDivide(double numer, double denom) { return r; } -// Snaps a value to zero if almost zero (within tolerance). -double _snapToZero(double value) => _nearlyEqual(value, 0.0) ? 0.0 : value; - -bool _nearlyEqual(double value1, double value2) => - (value1 - value2).abs() < SPath.scalarNearlyZero; - -bool _isInteger(double value) => value.floor() == value; - -bool _isRRectOval(ui.RRect rrect) { +bool isRRectOval(ui.RRect rrect) { if ((rrect.tlRadiusX + rrect.trRadiusX) != rrect.width) { return false; } @@ -207,10 +214,10 @@ double polyEval4(double A, double B, double C, double D, double t) => // Interpolate between two doubles (Not using lerpDouble here since it null // checks and treats values as 0). -double _interpolate(double startValue, double endValue, double t) => +double interpolate(double startValue, double endValue, double t) => (startValue * (1 - t)) + endValue * t; -double _dotProduct(double x0, double y0, double x1, double y1) { +double dotProduct(double x0, double y0, double x1, double y1) { return x0 * x1 + y0 * y1; } @@ -292,9 +299,9 @@ class Convexicator { // Cross product = ||lastVec|| * ||curVec|| * sin(theta) * N // sin(theta) angle between two vectors is positive for angles 0..180 and // negative for greater, providing left or right direction. - double lastX = lastVecX!; - double lastY = lastVecY!; - double cross = lastX * curVecY - lastY * curVecX; + final double lastX = lastVecX!; + final double lastY = lastVecY!; + final double cross = lastX * curVecY - lastY * curVecX; if (!cross.isFinite) { return DirChange.kUnknown; } @@ -306,18 +313,18 @@ class Convexicator { final double largest = math.max( math.max(curVecX, math.max(curVecY, math.max(lastX, lastY))), -smallest); - if (_nearlyEqual(largest, largest + cross)) { - final double nearlyZeroSquared = + if (SPath.nearlyEqual(largest, largest + cross)) { + const double nearlyZeroSquared = SPath.scalarNearlyZero * SPath.scalarNearlyZero; - if (_nearlyEqual(_lengthSquared(lastX, lastY), nearlyZeroSquared) || - _nearlyEqual(_lengthSquared(curVecX, curVecY), nearlyZeroSquared)) { + if (SPath.nearlyEqual(lengthSquared(lastX, lastY), nearlyZeroSquared) || + SPath.nearlyEqual(lengthSquared(curVecX, curVecY), nearlyZeroSquared)) { // Length of either vector is smaller than tolerance to be able // to compute direction. return DirChange.kUnknown; } // The vectors are parallel, sign of dot product gives us direction. // cosine is positive for straight -90 < Theta < 90 - return _dotProduct(lastX, lastY, curVecX, curVecY) < 0 + return dotProduct(lastX, lastY, curVecX, curVecY) < 0 ? DirChange.kBackwards : DirChange.kStraight; } @@ -325,7 +332,7 @@ class Convexicator { } bool _addVector(double curVecX, double curVecY) { - DirChange dir = _directionChange(curVecX, curVecY); + final DirChange dir = _directionChange(curVecX, curVecY); final bool isDirectionRight = dir == DirChange.kRight; if (dir == DirChange.kLeft || isDirectionRight) { if (_expectedDirection == DirChange.kInvalid) { @@ -363,27 +370,27 @@ class Convexicator { // Quick test to detect concave by looking at number of changes in direction // of vectors formed by path points (excluding control points). static int bySign(PathRef pathRef, int pointIndex, int numPoints) { - int lastPointIndex = pointIndex + numPoints; + final int lastPointIndex = pointIndex + numPoints; int currentPoint = pointIndex++; - int firstPointIndex = currentPoint; + final int firstPointIndex = currentPoint; int signChangeCountX = 0; int signChangeCountY = 0; int lastSx = kValueNeverReturnedBySign; int lastSy = kValueNeverReturnedBySign; for (int outerLoop = 0; outerLoop < 2; ++outerLoop) { while (pointIndex != lastPointIndex) { - double vecX = pathRef._fPoints[pointIndex * 2] - - pathRef._fPoints[currentPoint * 2]; - double vecY = pathRef._fPoints[pointIndex * 2 + 1] - - pathRef._fPoints[currentPoint * 2 + 1]; + final double vecX = pathRef.pointXAt(pointIndex) - + pathRef.pointXAt(currentPoint); + final double vecY = pathRef.pointYAt(pointIndex) - + pathRef.pointYAt(currentPoint); if (!(vecX == 0 && vecY == 0)) { // Give up if vector construction failed. // give up if vector construction failed if (!(vecX.isFinite && vecY.isFinite)) { return SPathConvexityType.kUnknown; } - int sx = vecX < 0 ? 1 : 0; - int sy = vecY < 0 ? 1 : 0; + final int sx = vecX < 0 ? 1 : 0; + final int sy = vecY < 0 ? 1 : 0; signChangeCountX += (sx != lastSx) ? 1 : 0; signChangeCountY += (sy != lastSy) ? 1 : 0; if (signChangeCountX > 3 || signChangeCountY > 3) { @@ -411,3 +418,28 @@ enum DirChange { kBackwards, // if double back, allow simple lines to be convex kInvalid } + +double lengthSquaredOffset(ui.Offset offset) { + final double dx = offset.dx; + final double dy = offset.dy; + return dx * dx + dy * dy; +} + +double lengthSquared(double dx, double dy) => dx * dx + dy * dy; + +/// Evaluates A * t^2 + B * t + C = 0 for quadratic curve. +class SkQuadCoefficients { + SkQuadCoefficients( + double x0, double y0, double x1, double y1, double x2, double y2) + : cx = x0, + cy = y0, + bx = 2 * (x1 - x0), + by = 2 * (y1 - y0), + ax = x2 - (2 * x1) + x0, + ay = y2 - (2 * y1) + y0; + final double ax, ay, bx, by, cx, cy; + + double evalX(double t) => (ax * t + bx) * t + cx; + + double evalY(double t) => (ay * t + by) * t + cy; +} diff --git a/lib/web_ui/lib/src/engine/html/path/path_windings.dart b/lib/web_ui/lib/src/engine/html/path/path_windings.dart index e922e4a53360a..ac877a264d985 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_windings.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_windings.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'conic.dart'; +import 'cubic.dart'; +import 'path_iterator.dart'; +import 'path_ref.dart'; +import 'path_utils.dart'; /// Computes winding number and onCurveCount for a path and point. class PathWinding { @@ -40,7 +46,7 @@ class PathWinding { _computeQuadWinding(); break; case SPath.kConicVerb: - _computeConicWinding(pathRef._conicWeights![iter._conicWeightIndex]); + _computeConicWinding(pathRef.conicWeights![iter.conicWeightIndex]); break; case SPath.kCubicVerb: _computeCubicWinding(); @@ -60,7 +66,7 @@ class PathWinding { int dir = 1; // Swap so that y0 <= y1 holds. if (y0 > y1) { - double temp = y0; + final double temp = y0; y0 = y1; y1 = temp; dir = -1; @@ -144,7 +150,7 @@ class PathWinding { return 0; } - _QuadRoots quadRoots = _QuadRoots(); + final QuadRoots quadRoots = QuadRoots(); final int n = quadRoots.findRoots( startY - 2 * y1 + endY, 2 * (y1 - startY), startY - y); assert(n <= 1); @@ -159,7 +165,7 @@ class PathWinding { final double B = 2 * (x1 - C); xt = polyEval(A, B, C, t); } - if (_nearlyEqual(xt, x)) { + if (SPath.nearlyEqual(xt, x)) { if (x != x2 || y != endY) { // don't test end points; they're start points _onCurveCount += 1; @@ -178,15 +184,15 @@ class PathWinding { final double y1 = buffer[3]; final double x2 = buffer[4]; final double y2 = buffer[5]; - double? tValueAtExtrema = _validUnitDivide(y0 - y1, y0 - y1 - y1 + y2); + final double? tValueAtExtrema = validUnitDivide(y0 - y1, y0 - y1 - y1 + y2); if (tValueAtExtrema != null) { // Chop quad at t value by interpolating along p0-p1 and p1-p2. - double p01x = x0 + (tValueAtExtrema * (x1 - x0)); - double p01y = y0 + (tValueAtExtrema * (y1 - y0)); - double p12x = x1 + (tValueAtExtrema * (x2 - x1)); - double p12y = y1 + (tValueAtExtrema * (y2 - y1)); - double cx = p01x + (tValueAtExtrema * (p12x - p01x)); - double cy = p01y + (tValueAtExtrema * (p12y - p01y)); + final double p01x = x0 + (tValueAtExtrema * (x1 - x0)); + final double p01y = y0 + (tValueAtExtrema * (y1 - y0)); + final double p12x = x1 + (tValueAtExtrema * (x2 - x1)); + final double p12y = y1 + (tValueAtExtrema * (y2 - y1)); + final double cx = p01x + (tValueAtExtrema * (p12x - p01x)); + final double cy = p01y + (tValueAtExtrema * (p12y - p01y)); buffer[2] = p01x; buffer[3] = p01y; buffer[4] = cx; @@ -218,12 +224,12 @@ class PathWinding { } void _computeConicWinding(double weight) { - Conic conic = Conic(_buffer[0], _buffer[1], _buffer[2], _buffer[3], + final Conic conic = Conic(_buffer[0], _buffer[1], _buffer[2], _buffer[3], _buffer[4], _buffer[5], weight); // If the data points are very large, the conic may not be monotonic but may also // fail to chop. Then, the chopper does not split the original conic in two. - bool isMono = _isQuadMonotonic(_buffer); - List conics = []; + final bool isMono = _isQuadMonotonic(_buffer); + final List conics = []; conic.chopAtYExtrema(conics); _computeMonoConicWinding(conics[0]); if (!isMono && conics.length == 2) { @@ -260,8 +266,8 @@ class PathWinding { // B = b*w - w * yCept + yCept - a B -= C; C -= y; - final _QuadRoots quadRoots = _QuadRoots(); - int n = quadRoots.findRoots(A, 2 * B, C); + final QuadRoots quadRoots = QuadRoots(); + final int n = quadRoots.findRoots(A, 2 * B, C); assert(n <= 1); double xt; if (0 == n) { @@ -272,10 +278,10 @@ class PathWinding { } else { final double root = quadRoots.root0!; xt = - _conicEvalNumerator(conic.p0x, conic.p1x, conic.p2x, conic.fW, root) / - _conicEvalDenominator(conic.fW, root); + Conic.evalNumerator(conic.p0x, conic.p1x, conic.p2x, conic.fW, root) / + Conic.evalDenominator(conic.fW, root); } - if (_nearlyEqual(xt, x)) { + if (SPath.nearlyEqual(xt, x)) { if (x != conic.p2x || y != conic.p2y) { // don't test end points; they're start points _onCurveCount += 1; @@ -286,7 +292,7 @@ class PathWinding { } void _computeCubicWinding() { - int n = _chopCubicAtYExtrema(_buffer, _buffer); + final int n = chopCubicAtYExtrema(_buffer, _buffer); for (int i = 0; i <= n; ++i) { _windingMonoCubic(i * 3 * 2); } @@ -335,12 +341,12 @@ class PathWinding { return; } // Compute the actual x(t) value. - double? t = _chopMonoAtY(_buffer, bufferStartPos, y); + final double? t = chopMonoAtY(_buffer, bufferStartPos, y); if (t == null) { return; } - double xt = _evalCubicPts(px0, px1, px2, px3, t); - if (_nearlyEqual(xt, x)) { + final double xt = evalCubicPts(px0, px1, px2, px3, t); + if (SPath.nearlyEqual(xt, x)) { if (x != px3 || y != py3) { // don't test end points; they're start points _onCurveCount += 1; @@ -350,210 +356,3 @@ class PathWinding { _w += xt < x ? dir : 0; } } - -// Iterates through path including generating closing segments. -class PathIterator { - PathIterator(this.pathRef, bool forceClose) - : _forceClose = forceClose, - _verbCount = pathRef.countVerbs() { - _pointIndex = 0; - if (!pathRef.isFinite) { - // Don't allow iteration through non-finite points, prepare to return - // done verb. - _verbIndex = pathRef.countVerbs(); - } - } - - final PathRef pathRef; - final bool _forceClose; - final int _verbCount; - - bool _needClose = false; - int _segmentState = SPathSegmentState.kEmptyContour; - int _conicWeightIndex = -1; - double _lastPointX = 0; - double _lastPointY = 0; - double _moveToX = 0; - double _moveToY = 0; - int _verbIndex = 0; - int _pointIndex = 0; - - /// Maximum buffer size required for points in [next] calls. - static const int kMaxBufferSize = 8; - - /// Returns true if first contour on path is closed. - bool isClosedContour() { - if (_verbCount == 0 || _verbIndex == _verbCount) { - return false; - } - if (_forceClose) { - return true; - } - int verbIndex = 0; - // Skip starting moveTo. - if (pathRef.atVerb(verbIndex) == SPath.kMoveVerb) { - ++verbIndex; - } - while (verbIndex < _verbCount) { - int verb = pathRef.atVerb(verbIndex++); - if (SPath.kMoveVerb == verb) { - break; - } - if (SPath.kCloseVerb == verb) { - return true; - } - } - return false; - } - - int _autoClose(Float32List outPts) { - if (_lastPointX != _moveToX || _lastPointY != _moveToY) { - // Handle special case where comparison above will return true for - // NaN != NaN although it should be false. - if (_lastPointX.isNaN || - _lastPointY.isNaN || - _moveToX.isNaN || - _moveToY.isNaN) { - return SPath.kCloseVerb; - } - outPts[0] = _lastPointX; - outPts[1] = _lastPointY; - outPts[2] = _moveToX; - outPts[3] = _moveToY; - _lastPointX = _moveToX; - _lastPointY = _moveToY; - return SPath.kLineVerb; - } else { - outPts[0] = _moveToX; - outPts[1] = _moveToY; - return SPath.kCloseVerb; - } - } - - // Returns true if caller should use moveTo, false if last point of - // previous primitive. - ui.Offset _constructMoveTo() { - if (_segmentState == SPathSegmentState.kAfterMove) { - // Set the first return point to move point. - _segmentState = SPathSegmentState.kAfterPrimitive; - return ui.Offset(_moveToX, _moveToY); - } - return ui.Offset( - pathRef.points[_pointIndex - 2], pathRef.points[_pointIndex - 1]); - } - - int peek() { - if (_verbIndex < pathRef.countVerbs()) { - return pathRef._fVerbs[_verbIndex]; - } - if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { - return (_lastPointX != _moveToX || _lastPointY != _moveToY) - ? SPath.kLineVerb - : SPath.kCloseVerb; - } - return SPath.kDoneVerb; - } - - // Returns next verb and reads associated points into [outPts]. - int next(Float32List outPts) { - if (_verbIndex == pathRef.countVerbs()) { - // Close the curve if requested and if there is some curve to close - if (_needClose && _segmentState == SPathSegmentState.kAfterPrimitive) { - if (SPath.kLineVerb == _autoClose(outPts)) { - return SPath.kLineVerb; - } - _needClose = false; - return SPath.kCloseVerb; - } - return SPath.kDoneVerb; - } - int verb = pathRef._fVerbs[_verbIndex++]; - switch (verb) { - case SPath.kMoveVerb: - if (_needClose) { - // Move back one verb. - _verbIndex--; - final int autoVerb = _autoClose(outPts); - if (autoVerb == SPath.kCloseVerb) { - _needClose = false; - } - return autoVerb; - } - if (_verbIndex == _verbCount) { - return SPath.kDoneVerb; - } - double offsetX = pathRef.points[_pointIndex++]; - double offsetY = pathRef.points[_pointIndex++]; - _moveToX = offsetX; - _moveToY = offsetY; - outPts[0] = offsetX; - outPts[1] = offsetY; - _segmentState = SPathSegmentState.kAfterMove; - _lastPointX = _moveToX; - _lastPointY = _moveToY; - _needClose = _forceClose; - break; - case SPath.kLineVerb: - final ui.Offset start = _constructMoveTo(); - double offsetX = pathRef.points[_pointIndex++]; - double offsetY = pathRef.points[_pointIndex++]; - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = offsetX; - outPts[3] = offsetY; - _lastPointX = offsetX; - _lastPointY = offsetY; - break; - case SPath.kConicVerb: - _conicWeightIndex++; - final ui.Offset start = _constructMoveTo(); - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = pathRef.points[_pointIndex++]; - outPts[3] = pathRef.points[_pointIndex++]; - _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; - _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; - break; - case SPath.kQuadVerb: - final ui.Offset start = _constructMoveTo(); - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = pathRef.points[_pointIndex++]; - outPts[3] = pathRef.points[_pointIndex++]; - _lastPointX = outPts[4] = pathRef.points[_pointIndex++]; - _lastPointY = outPts[5] = pathRef.points[_pointIndex++]; - break; - case SPath.kCubicVerb: - final ui.Offset start = _constructMoveTo(); - outPts[0] = start.dx; - outPts[1] = start.dy; - outPts[2] = pathRef.points[_pointIndex++]; - outPts[3] = pathRef.points[_pointIndex++]; - outPts[4] = pathRef.points[_pointIndex++]; - outPts[5] = pathRef.points[_pointIndex++]; - _lastPointX = outPts[6] = pathRef.points[_pointIndex++]; - _lastPointY = outPts[7] = pathRef.points[_pointIndex++]; - break; - case SPath.kCloseVerb: - verb = _autoClose(outPts); - if (verb == SPath.kLineVerb) { - // Move back one verb since we constructed line for this close verb. - _verbIndex--; - } else { - _needClose = false; - _segmentState = SPathSegmentState.kEmptyContour; - } - _lastPointX = _moveToX; - _lastPointY = _moveToY; - break; - case SPath.kDoneVerb: - assert(_verbIndex == pathRef.countVerbs()); - break; - default: - throw FormatException('Unsupport Path verb $verb'); - } - return verb; - } - - double get conicWeight => pathRef.atWeight(_conicWeightIndex); -} diff --git a/lib/web_ui/lib/src/engine/html/path/tangent.dart b/lib/web_ui/lib/src/engine/html/path/tangent.dart index ce3a2e4336647..d65365b034596 100644 --- a/lib/web_ui/lib/src/engine/html/path/tangent.dart +++ b/lib/web_ui/lib/src/engine/html/path/tangent.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'conic.dart'; +import 'cubic.dart'; +import 'path_utils.dart'; /// Computes tangent at point x,y on a line. void tangentLine( @@ -20,7 +25,7 @@ void tangentLine( } final double dx = x1 - x0; final double dy = y1 - y0; - if (!_nearlyEqual((x - x0) * dy, dx * (y - y0))) { + if (!SPath.nearlyEqual((x - x0) * dy, dx * (y - y0))) { return; } tangents.add(ui.Offset(dx, dy)); @@ -41,15 +46,15 @@ void tangentQuad( if (!SPath.between(x0, x, x1) && !SPath.between(x1, x, x2)) { return; } - final _QuadRoots roots = _QuadRoots(); - int n = roots.findRoots(y0 - 2 * y1 + y2, 2 * (y1 - y0), y0 - y); + final QuadRoots roots = QuadRoots(); + final int n = roots.findRoots(y0 - 2 * y1 + y2, 2 * (y1 - y0), y0 - y); for (int index = 0; index < n; ++index) { - double t = index == 0 ? roots.root0! : roots.root1!; - double C = x0; - double A = x2 - 2 * x1 + C; - double B = 2 * (x1 - C); - double xt = polyEval(A, B, C, t); - if (!_nearlyEqual(x, xt)) { + final double t = index == 0 ? roots.root0! : roots.root1!; + final double C = x0; + final double A = x2 - 2 * x1 + C; + final double B = 2 * (x1 - C); + final double xt = polyEval(A, B, C, t); + if (!SPath.nearlyEqual(x, xt)) { continue; } tangents.add(_evalQuadTangentAt(x0, y0, x1, y1, x2, y2, t)); @@ -68,12 +73,12 @@ ui.Offset _evalQuadTangentAt(double x0, double y0, double x1, double y1, } assert(t >= 0 && t <= 1.0); - double bx = x1 - x0; - double by = y1 - y0; - double ax = x2 - x1 - bx; - double ay = y2 - y1 - by; - double tx = ax * t + bx; - double ty = ay * t + by; + final double bx = x1 - x0; + final double by = y1 - y0; + final double ax = x2 - x1 - bx; + final double ay = y2 - y1 - by; + final double tx = ax * t + bx; + final double ty = ay * t + by; return ui.Offset(tx * 2, ty * 2); } @@ -101,16 +106,16 @@ void tangentConic(Float32List pts, double x, double y, double weight, // B = b*w - w * yCept + yCept - a B -= C; C -= y; - final _QuadRoots quadRoots = _QuadRoots(); - int n = quadRoots.findRoots(A, 2 * B, C); + final QuadRoots quadRoots = QuadRoots(); + final int n = quadRoots.findRoots(A, 2 * B, C); for (int index = 0; index < n; ++index) { - double t = index == 0 ? quadRoots.root0! : quadRoots.root1!; - double xt = _conicEvalNumerator(x0, x1, x2, weight, t) / - _conicEvalDenominator(weight, t); - if (!_nearlyEqual(x, xt)) { + final double t = index == 0 ? quadRoots.root0! : quadRoots.root1!; + final double xt = Conic.evalNumerator(x0, x1, x2, weight, t) / + Conic.evalDenominator(weight, t); + if (!SPath.nearlyEqual(x, xt)) { continue; } - Conic conic = Conic(x0, y0, x1, y1, x2, y2, weight); + final Conic conic = Conic(x0, y0, x1, y1, x2, y2, weight); tangents.add(conic.evalTangentAt(t)); } } @@ -137,16 +142,16 @@ void tangentCubic( return; } final Float32List dst = Float32List(20); - int n = _chopCubicAtYExtrema(pts, dst); + final int n = chopCubicAtYExtrema(pts, dst); for (int i = 0; i <= n; ++i) { - int bufferPos = i * 6; - double? t = _chopMonoAtY(dst, i * 6, y); + final int bufferPos = i * 6; + final double? t = chopMonoAtY(dst, i * 6, y); if (t == null) { continue; } - double xt = _evalCubicPts(dst[bufferPos], dst[bufferPos + 2], + final double xt = evalCubicPts(dst[bufferPos], dst[bufferPos + 2], dst[bufferPos + 4], dst[bufferPos + 6], t); - if (!_nearlyEqual(x, xt)) { + if (!SPath.nearlyEqual(x, xt)) { continue; } tangents.add(_evalCubicTangentAt(dst, bufferPos, t)); @@ -187,7 +192,7 @@ ui.Offset _evalCubicTangentAt(Float32List points, int bufferPos, double t) { ui.Offset _evalCubicDerivative(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double t) { - final _SkQuadCoefficients coeff = _SkQuadCoefficients( + final SkQuadCoefficients coeff = SkQuadCoefficients( x3 + 3 * (x1 - x2) - x0, y3 + 3 * (y1 - y2) - y0, 2 * (x2 - (2 * x1) + x0), diff --git a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart new file mode 100644 index 0000000000000..607b027a6c44b --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import 'path/path.dart'; +import 'path/path_to_svg.dart'; + +/// Counter used for generating clip path id inside an svg tag. +int _clipIdCounter = 0; + +/// Used for clipping and filter svg resources. +/// +/// Position needs to be absolute since these svgs are sandwiched between +/// canvas elements and can cause layout shifts otherwise. +const String kSvgResourceHeader = ''; + +/// Converts Path to svg element that contains a clip-path definition. +/// +/// Calling this method updates [_clipIdCounter]. The HTML id of the generated +/// clip is set to "svgClip${_clipIdCounter}", e.g. "svgClip123". +String pathToSvgClipPath(ui.Path path, + {double offsetX = 0, + double offsetY = 0, + double scaleX = 1.0, + double scaleY = 1.0}) { + _clipIdCounter += 1; + final StringBuffer sb = StringBuffer(); + sb.write(kSvgResourceHeader); + sb.write(''); + + final String clipId = 'svgClip$_clipIdCounter'; + + if (browserEngine == BrowserEngine.firefox) { + // Firefox objectBoundingBox fails to scale to 1x1 units, instead use + // no clipPathUnits but write the path in target units. + sb.write(''); + sb.write(''); + sb.write(' 'url(#svgClip$_clipIdCounter)'; + +/// Resets clip ids. Used for testing by [debugForgetFrameScene] API. +void resetSvgClipIds() { + _clipIdCounter = 0; +} diff --git a/lib/web_ui/lib/src/engine/html/picture.dart b/lib/web_ui/lib/src/engine/html/picture.dart index 2c030ea68b186..28db753a76978 100644 --- a/lib/web_ui/lib/src/engine/html/picture.dart +++ b/lib/web_ui/lib/src/engine/html/picture.dart @@ -2,8 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../dom_renderer.dart'; +import '../engine_canvas.dart'; +import '../frame_reference.dart'; +import '../picture.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'bitmap_canvas.dart'; +import 'debug_canvas_reuse_overlay.dart'; +import 'dom_canvas.dart'; +import 'path/path_metrics.dart'; +import 'surface.dart'; +import 'surface_stats.dart'; // TODO(yjbanov): this is currently very naive. We probably want to cache // fewer large canvases than small canvases. We could also @@ -17,10 +33,11 @@ part of engine; const int _kCanvasCacheSize = 30; /// Canvases available for reuse, capped at [_kCanvasCacheSize]. +List get recycledCanvases => _recycledCanvases; final List _recycledCanvases = []; /// Reduces recycled canvas list by 50% to reduce bitmap canvas memory use. -void _reduceCanvasMemoryUsage() { +void reduceCanvasMemoryUsage() { final int canvasCount = _recycledCanvases.length; for (int i = 0; i < canvasCount; i++) { _recycledCanvases[i].dispose(); @@ -34,8 +51,8 @@ void _reduceCanvasMemoryUsage() { /// makes canvas allocation more efficient by letting large pictures claim /// larger recycled canvases. Otherwise, small pictures would claim the large /// canvases forcing us to allocate new large canvases. -class _PaintRequest { - _PaintRequest({ +class PaintRequest { + PaintRequest({ required this.canvasSize, required this.paintCallback, }) : assert(canvasSize != null), // ignore: unnecessary_null_comparison @@ -48,20 +65,26 @@ class _PaintRequest { /// Repaint requests produced by [PersistedPicture]s that actually paint on the /// canvas. Painting is delayed until the layer tree is updated to maximize /// the number of reusable canvases. -List<_PaintRequest> _paintQueue = <_PaintRequest>[]; +List paintQueue = []; void _recycleCanvas(EngineCanvas? canvas) { + // If a canvas is in the paint queue it maybe be recycled. To + // prevent subsequent dispose recycling again check. + if (canvas != null && _recycledCanvases.contains(canvas)) { + return; + } if (canvas is BitmapCanvas) { + canvas.setElementCache(null); if (canvas.isReusable()) { _recycledCanvases.add(canvas); if (_recycledCanvases.length > _kCanvasCacheSize) { final BitmapCanvas removedCanvas = _recycledCanvases.removeAt(0); removedCanvas.dispose(); - if (_debugShowCanvasReuseStats) { + if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.disposedCount++; } } - if (_debugShowCanvasReuseStats) { + if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.inRecycleCount = _recycledCanvases.length; } @@ -80,18 +103,22 @@ class PersistedPicture extends PersistedLeafSurface { EngineCanvas? _canvas; /// Returns the canvas used by this picture layer. - /// - /// Useful for tests. - EngineCanvas? get debugCanvas => _canvas; + EngineCanvas? get canvas => _canvas; final double dx; final double dy; final EnginePicture picture; final ui.Rect? localPaintBounds; final int hints; + double _density = 1.0; + + /// Cull rect changes and density changes due to transforms should + /// call applyPaint for picture when retain() or update() is called after + /// preroll is complete. + bool _requiresRepaint = false; /// Cache for reusing elements such as images across picture updates. - CrossFrameCache _elementCache = + CrossFrameCache? _elementCache = CrossFrameCache(); @override @@ -99,12 +126,31 @@ class PersistedPicture extends PersistedLeafSurface { return defaultCreateElement('flt-picture'); } + @override + void preroll(PrerollSurfaceContext prerollContext) { + if (prerollContext.activeShaderMaskCount != 0 || + prerollContext.activeColorFilterCount != 0) { + picture.recordingCanvas?.renderStrategy.isInsideSvgFilterTree = true; + } + super.preroll(prerollContext); + } + @override void recomputeTransformAndClip() { - _transform = parent!._transform; + transform = parent!.transform; if (dx != 0.0 || dy != 0.0) { - _transform = _transform!.clone(); - _transform!.translate(dx, dy); + transform = transform!.clone(); + transform!.translate(dx, dy); + } + final double paintWidth = localPaintBounds!.width; + final double paintHeight = localPaintBounds!.height; + final double newDensity = + localPaintBounds == null || paintWidth == 0 || paintHeight == 0 + ? 1.0 + : _computePixelDensity(transform, paintWidth, paintHeight); + if (newDensity != _density) { + _density = newDensity; + _requiresRepaint = true; } _computeExactCullRects(); } @@ -137,7 +183,7 @@ class PersistedPicture extends PersistedLeafSurface { assert(transform != null); assert(localPaintBounds != null); - if (parent!._projectedClip == null) { + if (parent!.projectedClip == null) { // Compute and cache chain of clipping bounds on parent of picture since // parent may include multiple pictures so it can be reused by all // child pictures. @@ -145,7 +191,7 @@ class PersistedPicture extends PersistedLeafSurface { PersistedSurface? parentSurface = parent; final Matrix4 clipTransform = Matrix4.identity(); while (parentSurface != null) { - final ui.Rect? localClipBounds = parentSurface._localClipBounds; + final ui.Rect? localClipBounds = parentSurface.localClipBounds; if (localClipBounds != null) { if (bounds == null) { bounds = transformRect(clipTransform, localClipBounds); @@ -164,15 +210,15 @@ class PersistedPicture extends PersistedLeafSurface { bounds = ui.Rect.zero; } // Cache projected clip on parent. - parent!._projectedClip = bounds; + parent!.projectedClip = bounds; } // Intersect localPaintBounds with parent projected clip to calculate // and cache [_exactLocalCullRect]. - if (parent!._projectedClip == null) { + if (parent!.projectedClip == null) { _exactLocalCullRect = localPaintBounds; } else { _exactLocalCullRect = - localPaintBounds!.intersect(parent!._projectedClip!); + localPaintBounds!.intersect(parent!.projectedClip!); } if (_exactLocalCullRect!.width <= 0 || _exactLocalCullRect!.height <= 0) { _exactLocalCullRect = ui.Rect.zero; @@ -185,13 +231,14 @@ class PersistedPicture extends PersistedLeafSurface { } } - bool _computeOptimalCullRect(PersistedPicture? oldSurface) { + void _computeOptimalCullRect(PersistedPicture? oldSurface) { assert(_exactLocalCullRect != null); if (oldSurface == null || !oldSurface.picture.recordingCanvas!.didDraw) { // First useful paint. _optimalLocalCullRect = _exactLocalCullRect; - return true; + _requiresRepaint = true; + return; } assert(oldSurface._optimalLocalCullRect != null); @@ -205,7 +252,10 @@ class PersistedPicture extends PersistedLeafSurface { // The clip collapsed into a zero-sized rectangle. If it was already zero, // no need to signal cull rect change. _optimalLocalCullRect = ui.Rect.zero; - return oldOptimalLocalCullRect != ui.Rect.zero; + if (oldOptimalLocalCullRect != ui.Rect.zero) { + _requiresRepaint = true; + } + return; } if (rectContainsOther(oldOptimalLocalCullRect!, _exactLocalCullRect!)) { @@ -214,7 +264,7 @@ class PersistedPicture extends PersistedLeafSurface { // a clip when it is scrolled out of the screen. In this case we do not // repaint the picture. We just let it be shrunk by the outer clip. _optimalLocalCullRect = oldOptimalLocalCullRect; - return false; + return; } // The new cull rect contains area not covered by a previous rect. Perhaps @@ -251,9 +301,8 @@ class PersistedPicture extends PersistedLeafSurface { _predictTrend(bottomwardDelta, _exactLocalCullRect!.height), ).intersect(localPaintBounds!); - final bool localCullRectChanged = _optimalLocalCullRect != newLocalCullRect; + _requiresRepaint = _optimalLocalCullRect != newLocalCullRect; _optimalLocalCullRect = newLocalCullRect; - return localCullRectChanged; } /// Predicts the delta a particular side of a clip rect will move given the @@ -284,24 +333,34 @@ class PersistedPicture extends PersistedLeafSurface { return 0; } - final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas; + final BitmapCanvas bitmapCanvas = _canvas! as BitmapCanvas; return bitmapCanvas.bitmapPixelCount; } void _applyPaint(PersistedPicture? oldSurface) { final EngineCanvas? oldCanvas = oldSurface?._canvas; + _requiresRepaint = false; if (!picture.recordingCanvas!.didDraw || _optimalLocalCullRect!.isEmpty) { // The picture is empty, or it has been completely clipped out. Skip // painting. This removes all the setup work and scaffolding objects // that won't be useful for anything anyway. _recycleCanvas(oldCanvas); - domRenderer.clearDom(rootElement!); + if (oldSurface != null) { + // Make sure it doesn't get reused/recycled again. + oldSurface._canvas = null; + } + if (rootElement != null) { + domRenderer.clearDom(rootElement!); + } + if (_canvas != null && _canvas != oldCanvas) { + _recycleCanvas(_canvas); + } _canvas = null; return; } - if (_debugExplainSurfaceStats) { - _surfaceStatsFor(this).paintCount++; + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).paintCount++; } assert(_optimalLocalCullRect != null); @@ -321,9 +380,10 @@ class PersistedPicture extends PersistedLeafSurface { return 1.0; } - final bool didRequireBitmap = - existingSurface.picture.recordingCanvas!.hasArbitraryPaint; - final bool requiresBitmap = picture.recordingCanvas!.hasArbitraryPaint; + final bool didRequireBitmap = existingSurface + .picture.recordingCanvas!.renderStrategy.hasArbitraryPaint; + final bool requiresBitmap = + picture.recordingCanvas!.renderStrategy.hasArbitraryPaint; if (didRequireBitmap != requiresBitmap) { // Switching canvas types is always expensive. return 1.0; @@ -338,15 +398,15 @@ class PersistedPicture extends PersistedLeafSurface { // We did not allocate a canvas last time. This can happen when the // picture is completely clipped out of the view. return 1.0; - } else if (!oldCanvas.doesFitBounds(_exactLocalCullRect!)) { + } else if (!oldCanvas.doesFitBounds(_exactLocalCullRect!, _density)) { // The canvas needs to be resized before painting. return 1.0; } else { final int newPixelCount = - BitmapCanvas._widthToPhysical(_exactLocalCullRect!.width) * - BitmapCanvas._heightToPhysical(_exactLocalCullRect!.height); + BitmapCanvas.widthToPhysical(_exactLocalCullRect!.width) * + BitmapCanvas.heightToPhysical(_exactLocalCullRect!.height); final int oldPixelCount = - oldCanvas._widthInBitmapPixels * oldCanvas._heightInBitmapPixels; + oldCanvas.widthInBitmapPixels * oldCanvas.heightInBitmapPixels; if (oldPixelCount == 0) { return 1.0; @@ -360,11 +420,8 @@ class PersistedPicture extends PersistedLeafSurface { } } - @override - Matrix4? get localTransformInverse => null; - void applyPaint(EngineCanvas? oldCanvas) { - if (picture.recordingCanvas!.hasArbitraryPaint) { + if (picture.recordingCanvas!.renderStrategy.hasArbitraryPaint) { _applyBitmapPaint(oldCanvas); } else { _applyDomPaint(oldCanvas); @@ -372,48 +429,54 @@ class PersistedPicture extends PersistedLeafSurface { } void _applyDomPaint(EngineCanvas? oldCanvas) { - _recycleCanvas(oldCanvas); - _canvas = DomCanvas(); + _recycleCanvas(_canvas); + final DomCanvas domCanvas = DomCanvas(rootElement!); + _canvas = domCanvas; domRenderer.clearDom(rootElement!); - rootElement!.append(_canvas!.rootElement); - picture.recordingCanvas!.apply(_canvas!, _optimalLocalCullRect); + picture.recordingCanvas!.apply(domCanvas, _optimalLocalCullRect!); } void _applyBitmapPaint(EngineCanvas? oldCanvas) { if (oldCanvas is BitmapCanvas && - oldCanvas.doesFitBounds(_optimalLocalCullRect!) && + oldCanvas.doesFitBounds(_optimalLocalCullRect!, _density) && oldCanvas.isReusable()) { - if (_debugShowCanvasReuseStats) { + final BitmapCanvas reusedCanvas = oldCanvas; + if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.keptCount++; } - oldCanvas.bounds = _optimalLocalCullRect!; - _canvas = oldCanvas; - oldCanvas.setElementCache(_elementCache); - _canvas!.clear(); - picture.recordingCanvas!.apply(_canvas!, _optimalLocalCullRect); + // Re-use old bitmap canvas. + reusedCanvas.bounds = _optimalLocalCullRect!; + _canvas = reusedCanvas; + reusedCanvas.setElementCache(_elementCache); + reusedCanvas.clear(); + picture.recordingCanvas!.apply(reusedCanvas, _optimalLocalCullRect!); } else { // We can't use the old canvas because the size has changed, so we put // it in a cache for later reuse. _recycleCanvas(oldCanvas); + if (_canvas is BitmapCanvas) { + (_canvas! as BitmapCanvas).setElementCache(null); + } + _canvas = null; // We cannot paint immediately because not all canvases that we may be // able to reuse have been released yet. So instead we enqueue this // picture to be painted after the update cycle is done syncing the layer // tree then reuse canvases that were freed up. - _paintQueue.add(_PaintRequest( + paintQueue.add(PaintRequest( canvasSize: _optimalLocalCullRect!.size, paintCallback: () { - _canvas = _findOrCreateCanvas(_optimalLocalCullRect!); - assert(_canvas is BitmapCanvas && - (_canvas as BitmapCanvas?)!._elementCache == _elementCache); - if (_debugExplainSurfaceStats) { - final BitmapCanvas bitmapCanvas = _canvas as BitmapCanvas; - _surfaceStatsFor(this).paintPixelCount += + final BitmapCanvas bitmapCanvas = + _findOrCreateCanvas(_optimalLocalCullRect!); + _canvas = bitmapCanvas; + bitmapCanvas.setElementCache(_elementCache); + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).paintPixelCount += bitmapCanvas.bitmapPixelCount; } domRenderer.clearDom(rootElement!); - rootElement!.append(_canvas!.rootElement); - _canvas!.clear(); - picture.recordingCanvas!.apply(_canvas!, _optimalLocalCullRect); + rootElement!.append(bitmapCanvas.rootElement); + bitmapCanvas.clear(); + picture.recordingCanvas!.apply(bitmapCanvas, _optimalLocalCullRect!); }, )); } @@ -444,7 +507,7 @@ class PersistedPicture extends PersistedLeafSurface { final double candidatePixelCount = candidateSize.width * candidateSize.height; - final bool fits = candidate.doesFitBounds(bounds); + final bool fits = candidate.doesFitBounds(bounds, _density); final bool isSmaller = candidatePixelCount < lastPixelCount; if (fits && isSmaller) { // [isTooSmall] is used to make sure that a small picture doesn't @@ -467,15 +530,15 @@ class PersistedPicture extends PersistedLeafSurface { } if (bestRecycledCanvas != null) { - if (_debugExplainSurfaceStats) { - _surfaceStatsFor(this).reuseCanvasCount++; + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).reuseCanvasCount++; } _recycledCanvases.remove(bestRecycledCanvas); - if (_debugShowCanvasReuseStats) { + if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.inRecycleCount = _recycledCanvases.length; } - if (_debugShowCanvasReuseStats) { + if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.reusedCount++; } bestRecycledCanvas.bounds = bounds; @@ -483,16 +546,18 @@ class PersistedPicture extends PersistedLeafSurface { return bestRecycledCanvas; } - if (_debugShowCanvasReuseStats) { + if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.createdCount++; } - final BitmapCanvas canvas = BitmapCanvas(bounds); + final BitmapCanvas canvas = BitmapCanvas( + bounds, picture.recordingCanvas!.renderStrategy, + density: _density); canvas.setElementCache(_elementCache); - if (_debugExplainSurfaceStats) { - _surfaceStatsFor(this) + if (debugExplainSurfaceStats) { + surfaceStatsFor(this) ..allocateBitmapCanvasCount += 1 ..allocatedBitmapSizeInPixels = - canvas._widthInBitmapPixels * canvas._heightInBitmapPixels; + canvas.widthInBitmapPixels * canvas.heightInBitmapPixels; } return canvas; } @@ -510,6 +575,7 @@ class PersistedPicture extends PersistedLeafSurface { @override void build() { _computeOptimalCullRect(null); + _requiresRepaint = true; super.build(); } @@ -518,16 +584,21 @@ class PersistedPicture extends PersistedLeafSurface { super.update(oldSurface); // Transfer element cache over. _elementCache = oldSurface._elementCache; + if (oldSurface != this) { + oldSurface._elementCache = null; + } if (dx != oldSurface.dx || dy != oldSurface.dy) { _applyTranslate(); } - final bool cullRectChangeRequiresRepaint = - _computeOptimalCullRect(oldSurface); + _computeOptimalCullRect(oldSurface); if (identical(picture, oldSurface.picture)) { + final bool densityChanged = _canvas is BitmapCanvas && + _density != (_canvas! as BitmapCanvas).density; + // The picture is the same. Attempt to avoid repaint. - if (cullRectChangeRequiresRepaint) { + if (_requiresRepaint || densityChanged) { // Cull rect changed such that a repaint is still necessary. _applyPaint(oldSurface); } else { @@ -544,8 +615,8 @@ class PersistedPicture extends PersistedLeafSurface { @override void retain() { super.retain(); - final bool cullRectChangeRequiresRepaint = _computeOptimalCullRect(this); - if (cullRectChangeRequiresRepaint) { + _computeOptimalCullRect(this); + if (_requiresRepaint) { _applyPaint(this); } } @@ -553,6 +624,7 @@ class PersistedPicture extends PersistedLeafSurface { @override void discard() { _recycleCanvas(_canvas); + _canvas = null; super.discard(); } @@ -560,9 +632,9 @@ class PersistedPicture extends PersistedLeafSurface { void debugPrintChildren(StringBuffer buffer, int indent) { super.debugPrintChildren(buffer, indent); if (rootElement != null && rootElement!.firstChild != null) { - final html.Element firstChild = rootElement!.firstChild as html.Element; + final html.Element firstChild = rootElement!.firstChild! as html.Element; final String canvasTag = firstChild.tagName.toLowerCase(); - final int canvasHash = rootElement!.firstChild!.hashCode; + final int canvasHash = firstChild.hashCode; buffer.writeln('${' ' * (indent + 1)}<$canvasTag @$canvasHash />'); } else if (rootElement != null) { buffer.writeln( @@ -577,7 +649,7 @@ class PersistedPicture extends PersistedLeafSurface { super.debugValidate(validationErrors); if (picture.recordingCanvas!.didDraw) { - if (!_optimalLocalCullRect!.isEmpty && debugCanvas == null) { + if (!_optimalLocalCullRect!.isEmpty && canvas == null) { validationErrors .add('$runtimeType has non-trivial picture but it has null canvas'); } @@ -593,3 +665,69 @@ class PersistedPicture extends PersistedLeafSurface { } } } + +/// Given size of a rectangle and transform, computes pixel density +/// (scale factor). +double _computePixelDensity(Matrix4? transform, double width, double height) { + if (transform == null || transform.isIdentityOrTranslation()) { + return 1.0; + } + final Float32List m = transform.storage; + // Apply perspective transform to all 4 corners. Can't use left,top, bottom, + // right since for example rotating 45 degrees would yield inaccurate size. + double minX = m[12] * m[15]; + double minY = m[13] * m[15]; + double maxX = minX; + double maxY = minY; + double x = width; + double y = height; + double wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + double xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + double yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + minX = math.min(minX, xp); + maxX = math.max(maxX, xp); + minY = math.min(minY, yp); + maxY = math.max(maxY, yp); + x = 0; + wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + minX = math.min(minX, xp); + maxX = math.max(maxX, xp); + minY = math.min(minY, yp); + maxY = math.max(maxY, yp); + x = width; + y = 0; + wp = 1.0 / ((m[3] * x) + (m[7] * y) + m[15]); + xp = ((m[0] * x) + (m[4] * y) + m[12]) * wp; + yp = ((m[1] * x) + (m[5] * y) + m[13]) * wp; + minX = math.min(minX, xp); + maxX = math.max(maxX, xp); + minY = math.min(minY, yp); + maxY = math.max(maxY, yp); + final double scaleX = (maxX - minX) / width; + final double scaleY = (maxY - minY) / height; + double scale = math.min(scaleX, scaleY); + // kEpsilon guards against divide by zero below. + if (scale < kEpsilon || scale == 1) { + // Handle local paint bounds scaled to 0, typical when using + // transform animations and nothing is drawn. + return 1.0; + } + if (scale > 1) { + // Normalize scale to multiples of 2: 1x, 2x, 4x, 6x, 8x. + // This is to prevent frequent rescaling of canvas during animations. + // + // On a fullscreen high dpi device dpi*density*resolution will demand + // too much memory, so clamp at 4. + scale = math.min(4.0, (scale / 2.0).ceil() * 2.0); + // Guard against webkit absolute limit. + const double kPixelLimit = 1024 * 1024 * 4; + if ((width * height * scale * scale) > kPixelLimit && scale > 2) { + scale = (kPixelLimit * 0.8) / (width * height); + } + } else { + scale = math.max(2.0 / (2.0 / scale).floor(), 0.0001); + } + return scale; +} diff --git a/lib/web_ui/lib/src/engine/html/platform_view.dart b/lib/web_ui/lib/src/engine/html/platform_view.dart index df76f70602ef7..af0f82db4b51e 100644 --- a/lib/web_ui/lib/src/engine/html/platform_view.dart +++ b/lib/web_ui/lib/src/engine/html/platform_view.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import '../platform_views/slots.dart'; +import 'surface.dart'; /// A surface containing a platform view, which is an HTML element. class PersistedPlatformView extends PersistedLeafSurface { @@ -13,66 +15,21 @@ class PersistedPlatformView extends PersistedLeafSurface { final double width; final double height; - late html.ShadowRoot _shadowRoot; - PersistedPlatformView(this.viewId, this.dx, this.dy, this.width, this.height); @override html.Element createElement() { - html.Element element = defaultCreateElement('flt-platform-view'); - - // Allow the platform view host element to receive pointer events. - // - // This is to allow platform view HTML elements to be interactive. - // - // ACCESSIBILITY NOTE: The way we enable accessibility on Flutter for web - // is to have a full-page button which waits for a double tap. Placing this - // full-page button in front of the scene would cause platform views not - // to receive pointer events. The tradeoff is that by placing the scene in - // front of the semantics placeholder will cause platform views to block - // pointer events from reaching the placeholder. This means that in order - // to enable accessibility, you must double tap the app *outside of a - // platform view*. As a consequence, a full-screen platform view will make - // it impossible to enable accessibility. - element.style.pointerEvents = 'auto'; - - // Enforce the effective size of the PlatformView. - element.style.overflow = 'hidden'; - - _shadowRoot = element.attachShadow({'mode': 'open'}); - final html.StyleElement _styleReset = html.StyleElement(); - _styleReset.innerHtml = ''' - :host { - all: initial; - }'''; - _shadowRoot.append(_styleReset); - final html.Element? platformView = - ui.platformViewRegistry.getCreatedView(viewId); - if (platformView != null) { - _shadowRoot.append(platformView); - } else { - html.window.console.warn('No platform view created for id $viewId'); - } - return element; + return createPlatformViewSlot(viewId); } - @override - Matrix4? get localTransformInverse => null; - @override void apply() { + // See `_compositeWithParams` in the HtmlViewEmbedder for the canvaskit equivalent. rootElement!.style ..transform = 'translate(${dx}px, ${dy}px)' ..width = '${width}px' - ..height = '${height}px'; - // Set size of the root element created by the PlatformView. - final html.Element? platformView = - ui.platformViewRegistry.getCreatedView(viewId); - if (platformView != null) { - platformView.style - ..width = '${width}px' - ..height = '${height}px'; - } + ..height = '${height}px' + ..position = 'absolute'; } // Platform Views can only be updated if their viewId matches. @@ -92,7 +49,10 @@ class PersistedPlatformView extends PersistedLeafSurface { @override void update(PersistedPlatformView oldSurface) { - assert(viewId == oldSurface.viewId, 'PersistedPlatformView with different viewId should never be updated. Check the canUpdateAsMatch method.',); + assert( + viewId == oldSurface.viewId, + 'PersistedPlatformView with different viewId should never be updated. Check the canUpdateAsMatch method.', + ); super.update(oldSurface); // Only update if the view has been resized if (dx != oldSurface.dx || diff --git a/lib/web_ui/lib/src/engine/html/recording_canvas.dart b/lib/web_ui/lib/src/engine/html/recording_canvas.dart index cb6a1ed626fed..b581557496082 100644 --- a/lib/web_ui/lib/src/engine/html/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/recording_canvas.dart @@ -2,8 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../engine_canvas.dart'; +import '../picture.dart'; +import '../rrect_renderer.dart'; +import '../shadow.dart'; +import '../text/paragraph.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'bitmap_canvas.dart'; +import 'painting.dart'; +import 'path/path.dart'; +import 'path/path_utils.dart'; +import 'render_vertices.dart'; +import 'shaders/image_shader.dart'; /// Enable this to print every command applied by a canvas. const bool _debugDumpPaintCommands = false; @@ -14,13 +30,14 @@ const bool _debugDumpPaintCommands = false; // negative radii (which Skia assumes to be 0), see: // https://skia.org/user/api/SkRRect_Reference#SkRRect_inset double _measureBorderRadius(double x, double y) { - double clampedX = x < 0 ? 0 : x; - double clampedY = y < 0 ? 0 : y; + final double clampedX = x < 0 ? 0 : x; + final double clampedY = y < 0 ? 0 : y; return clampedX * clampedX + clampedY * clampedY; } class RawRecordingCanvas extends BitmapCanvas implements ui.PictureRecorder { - RawRecordingCanvas(ui.Size size) : super(ui.Offset.zero & size); + RawRecordingCanvas(ui.Size size) + : super(ui.Offset.zero & size, RenderStrategy()); @override void dispose() { @@ -77,17 +94,14 @@ class RecordingCanvas { RecordingCanvas(ui.Rect? bounds) : _paintBounds = _PaintBounds(bounds ?? ui.Rect.largest); - /// Whether this canvas is doing arbitrary paint operations not expressible - /// via DOM elements. - bool get hasArbitraryPaint => _hasArbitraryPaint; - bool _hasArbitraryPaint = false; + final RenderStrategy renderStrategy = RenderStrategy(); /// Forces arbitrary paint even for simple pictures. /// /// This is useful for testing bitmap canvas when otherwise the compositor /// would prefer a DOM canvas. void debugEnforceArbitraryPaint() { - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; } /// Whether this canvas contain drawing operations. @@ -122,20 +136,32 @@ class RecordingCanvas { _recordingEnded = true; } + /// Applies the recorded commands onto an [engineCanvas] and signals to + /// canvas that all painting is completed for garbage collection/reuse. + /// + /// 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) { + applyCommands(engineCanvas, clipRect); + engineCanvas.endOfPaint(); + } + /// Applies the recorded commands onto an [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) { + void applyCommands(EngineCanvas engineCanvas, ui.Rect clipRect) { assert(_recordingEnded); if (_debugDumpPaintCommands) { final StringBuffer debugBuf = StringBuffer(); int skips = 0; debugBuf.writeln( '--- Applying RecordingCanvas to ${engineCanvas.runtimeType} ' - 'with bounds $_paintBounds and clip $clipRect (w = ${clipRect!.width},' + '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]; @@ -157,16 +183,18 @@ class RecordingCanvas { print(debugBuf); } else { try { - if (rectContainsOther(clipRect!, _pictureBounds!)) { + 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++) { + final int len = _commands.length; + for (int i = 0; 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 int len = _commands.length; + for (int i = 0; i < len; i++) { final PaintCommand command = _commands[i]; if (command is DrawCommand) { if (command.isInvisible(clipRect)) { @@ -180,12 +208,11 @@ class RecordingCanvas { } catch (e) { // commands should never fail, but... // https://bugzilla.mozilla.org/show_bug.cgi?id=941146 - if (!_isNsErrorFailureException(e)) { + if (!isNsErrorFailureException(e)) { rethrow; } } } - engineCanvas.endOfPaint(); } /// Prints recorded commands. @@ -210,7 +237,7 @@ class RecordingCanvas { void saveLayerWithoutBounds(SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; // TODO(het): Implement this correctly using another canvas. _commands.add(const PaintSave()); _paintBounds.saveTransformsAndClip(); @@ -219,7 +246,7 @@ class RecordingCanvas { void saveLayer(ui.Rect bounds, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; // TODO(het): Implement this correctly using another canvas. _commands.add(const PaintSave()); _paintBounds.saveTransformsAndClip(); @@ -268,14 +295,14 @@ class RecordingCanvas { void skew(double sx, double sy) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _paintBounds.skew(sx, sy); _commands.add(PaintSkew(sx, sy)); } void clipRect(ui.Rect rect, ui.ClipOp clipOp) { assert(!_recordingEnded); - final PaintClipRect command = PaintClipRect(rect, clipOp); + final DrawCommand command = PaintClipRect(rect, clipOp); switch (clipOp) { case ui.ClipOp.intersect: _paintBounds.clipRect(rect, command); @@ -284,15 +311,15 @@ class RecordingCanvas { // Since this refers to inverse, can't shrink paintBounds. break; } - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _commands.add(command); } - void clipRRect(ui.RRect rrect) { + void clipRRect(ui.RRect roundedRect) { assert(!_recordingEnded); - final PaintClipRRect command = PaintClipRRect(rrect); - _paintBounds.clipRect(rrect.outerRect, command); - _hasArbitraryPaint = true; + final PaintClipRRect command = PaintClipRRect(roundedRect); + _paintBounds.clipRect(roundedRect.outerRect, command); + renderStrategy.hasArbitraryPaint = true; _commands.add(command); } @@ -300,7 +327,7 @@ class RecordingCanvas { assert(!_recordingEnded); final PaintClipPath command = PaintClipPath(path as SurfacePath); _paintBounds.clipRect(path.getBounds(), command); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _commands.add(command); } @@ -313,6 +340,8 @@ class RecordingCanvas { void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaint paint) { assert(!_recordingEnded); + assert(paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet'); 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 @@ -329,14 +358,16 @@ class RecordingCanvas { math.max(p1.dy, p2.dy) + paintSpread, command, ); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; _commands.add(command); } void drawPaint(SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + assert(paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet'); + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final PaintDrawPaint command = PaintDrawPaint(paint.paintData); _paintBounds.grow(_paintBounds.maxPaintBounds, command); @@ -346,7 +377,7 @@ class RecordingCanvas { void drawRect(ui.Rect rect, SurfacePaint paint) { assert(!_recordingEnded); if (paint.shader != null) { - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; } _didDraw = true; final double paintSpread = _getPaintSpread(paint); @@ -362,7 +393,7 @@ class RecordingCanvas { void drawRRect(ui.RRect rrect, SurfacePaint paint) { assert(!_recordingEnded); if (paint.shader != null || !rrect.webOnlyUniformRadii) { - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; } _didDraw = true; final double paintSpread = _getPaintSpread(paint); @@ -379,8 +410,8 @@ class RecordingCanvas { assert(!_recordingEnded); // 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; - ui.Rect outerRect = outer.outerRect; + final ui.Rect innerRect = inner.outerRect; + final ui.Rect outerRect = outer.outerRect; if (outerRect == innerRect || outerRect.intersect(innerRect) != innerRect) { return; // inner is not fully contained within outer } @@ -414,16 +445,20 @@ class RecordingCanvas { return; // Some inner radius is overlapping some outer radius } - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); final PaintDrawDRRect command = PaintDrawDRRect(outer, inner, paint.paintData); + final double left = math.min(outer.left, outer.right); + final double right = math.max(outer.left, outer.right); + final double top = math.min(outer.top, outer.bottom); + final double bottom = math.max(outer.top, outer.bottom); _paintBounds.growLTRB( - outer.left - paintSpread, - outer.top - paintSpread, - outer.right + paintSpread, - outer.bottom + paintSpread, + left - paintSpread, + top - paintSpread, + right + paintSpread, + bottom + paintSpread, command, ); _commands.add(command); @@ -431,7 +466,7 @@ class RecordingCanvas { void drawOval(ui.Rect rect, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); final PaintDrawOval command = PaintDrawOval(rect, paint.paintData); @@ -445,7 +480,7 @@ class RecordingCanvas { void drawCircle(ui.Offset c, double radius, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); final PaintDrawCircle command = PaintDrawCircle(c, radius, paint.paintData); @@ -465,21 +500,21 @@ class RecordingCanvas { if (paint.shader == null) { // For Rect/RoundedRect paths use drawRect/drawRRect code paths for // DomCanvas optimization. - SurfacePath sPath = path as SurfacePath; - final ui.Rect? rect = sPath.webOnlyPathAsRect; + final SurfacePath sPath = path as SurfacePath; + final ui.Rect? rect = sPath.toRect(); if (rect != null) { drawRect(rect, paint); return; } - final ui.RRect? rrect = sPath.webOnlyPathAsRoundedRect; + final ui.RRect? rrect = sPath.toRoundedRect(); if (rrect != null) { drawRRect(rrect, paint); return; } } - SurfacePath sPath = path as SurfacePath; + final SurfacePath sPath = path as SurfacePath; if (!sPath.pathRef.isEmpty) { - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; ui.Rect pathBounds = sPath.getBounds(); final double paintSpread = _getPaintSpread(paint); @@ -487,7 +522,7 @@ class RecordingCanvas { pathBounds = pathBounds.inflate(paintSpread); } // Clone path so it can be reused for subsequent draw calls. - final ui.Path clone = SurfacePath._shallowCopy(path); + final ui.Path clone = SurfacePath.shallowCopy(path); final PaintDrawPath command = PaintDrawPath(clone as SurfacePath, paint.paintData); _paintBounds.grow(pathBounds, command); @@ -498,20 +533,48 @@ class RecordingCanvas { void drawImage(ui.Image image, ui.Offset offset, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + assert(paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet'); + renderStrategy.hasArbitraryPaint = true; + renderStrategy.hasImageElements = true; _didDraw = true; final double left = offset.dx; final double top = offset.dy; - final command = PaintDrawImage(image, offset, paint.paintData); + final PaintDrawImage command = PaintDrawImage(image, offset, paint.paintData); _paintBounds.growLTRB( left, top, left + image.width, top + image.height, command); _commands.add(command); } + void drawPicture(ui.Picture picture) { + assert(!_recordingEnded); + final EnginePicture enginePicture = picture as EnginePicture; + if (enginePicture.recordingCanvas == null) { + // No contents / nothing to draw. + return; + } + final RecordingCanvas pictureRecording = enginePicture.recordingCanvas!; + if (pictureRecording._didDraw == true) { + _didDraw = true; + } + renderStrategy.merge(pictureRecording.renderStrategy); + // Need to save to make sure we don't pick up leftover clips and + // transforms from running commands in picture. + save(); + _commands.addAll(pictureRecording._commands); + restore(); + if (pictureRecording._pictureBounds != null) { + _paintBounds.growBounds(pictureRecording._pictureBounds!); + } + } + void drawImageRect( ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + assert(paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet'); + renderStrategy.hasArbitraryPaint = true; + renderStrategy.hasImageElements = true; _didDraw = true; final PaintDrawImageRect command = PaintDrawImageRect(image, src, dst, paint.paintData); @@ -522,15 +585,16 @@ class RecordingCanvas { void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { assert(!_recordingEnded); final EngineParagraph engineParagraph = paragraph as EngineParagraph; - if (!engineParagraph._isLaidOut) { + if (!engineParagraph.isLaidOut) { // Ignore non-laid out paragraphs. This matches Flutter's behavior. return; } _didDraw = true; - if (engineParagraph._geometricStyle.ellipsis != null) { - _hasArbitraryPaint = true; + if (engineParagraph.hasArbitraryPaint) { + renderStrategy.hasArbitraryPaint = true; } + renderStrategy.hasParagraphs = true; final double left = offset.dx; final double top = offset.dy; final PaintDrawParagraph command = @@ -548,7 +612,7 @@ class RecordingCanvas { void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final ui.Rect shadowRect = computePenumbraBounds(path.getBounds(), elevation); @@ -561,18 +625,18 @@ class RecordingCanvas { void drawVertices( SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final PaintDrawVertices command = PaintDrawVertices(vertices, blendMode, paint.paintData); - _growPaintBoundsByPoints(vertices._positions, 0, paint, command); + _growPaintBoundsByPoints(vertices.positions, 0, paint, command); _commands.add(command); } void drawRawPoints( ui.PointMode pointMode, Float32List points, SurfacePaint paint) { assert(!_recordingEnded); - _hasArbitraryPaint = true; + renderStrategy.hasArbitraryPaint = true; _didDraw = true; final PaintDrawPoints command = PaintDrawPoints(pointMode, points, paint.paintData); @@ -585,7 +649,8 @@ class RecordingCanvas { double minValueX, maxValueX, minValueY, maxValueY; minValueX = maxValueX = points[0]; minValueY = maxValueY = points[1]; - for (int i = 2, len = points.length; i < len; i += 2) { + final int len = points.length; + for (int i = 2; i < len; i += 2) { final double x = points[i]; final double y = points[i + 1]; if (x.isNaN || y.isNaN) { @@ -649,14 +714,14 @@ abstract class DrawCommand extends PaintCommand { double bottomBound = double.infinity; /// Whether this command intersects with the [clipRect]. - bool isInvisible(ui.Rect? 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 || + return bottomBound < clipRect.top || topBound > clipRect.bottom || rightBound < clipRect.left || leftBound > clipRect.right; @@ -810,7 +875,7 @@ class PaintClipRect extends DrawCommand { @override void apply(EngineCanvas canvas) { - canvas.clipRect(rect); + canvas.clipRect(rect, clipOp); } @override @@ -1025,6 +1090,7 @@ class PaintDrawDRRect extends DrawCommand { @override void apply(EngineCanvas canvas) { + paint.style ??= ui.PaintingStyle.fill; canvas.drawPath(path!, paint); } @@ -1185,7 +1251,7 @@ class PaintDrawParagraph extends DrawCommand { @override String toString() { if (assertionsEnabled) { - return 'DrawParagraph(${paragraph._plainText}, $offset)'; + return 'DrawParagraph(${paragraph.toPlainText()}, $offset)'; } else { return super.toString(); } @@ -1324,7 +1390,7 @@ class Ellipse extends PathCommand { anticlockwise ? startAngle - endAngle : endAngle - startAngle, matrix4, bezierPath); - targetPath._addPath(bezierPath, 0, 0, matrix4, SPathAddPathMode.kAppend); + targetPath.addPathWithMode(bezierPath, 0, 0, matrix4, SPathAddPathMode.kAppend); } void _drawArcWithBezier( @@ -1578,8 +1644,8 @@ class RRectCommand extends PathCommand { @override void transform(Float32List matrix4, SurfacePath targetPath) { final ui.Path roundRectPath = ui.Path(); - _RRectToPathRenderer(roundRectPath).render(rrect); - targetPath._addPath(roundRectPath, 0, 0, matrix4, SPathAddPathMode.kAppend); + RRectToPathRenderer(roundRectPath).render(rrect); + targetPath.addPathWithMode(roundRectPath, 0, 0, matrix4, SPathAddPathMode.kAppend); } @override @@ -1638,7 +1704,7 @@ class _PaintBounds { _currentClipRight = 0.0, _currentClipBottom = 0.0; - _PaintBounds(ui.Rect maxPaintBounds) : maxPaintBounds = maxPaintBounds; + _PaintBounds(this.maxPaintBounds); void translate(double dx, double dy) { if (dx != 0.0 || dy != 0.0) { @@ -1738,7 +1804,8 @@ class _PaintBounds { growLTRB(r.left, r.top, r.right, r.bottom, command); } - /// Grow painted area to include given rectangle. + /// Grow painted area to include given rectangle and precompute + /// clipped out state for command. void growLTRB(double left, double top, double right, double bottom, DrawCommand command) { if (left == right || top == bottom) { @@ -1765,19 +1832,19 @@ class _PaintBounds { } if (_clipRectInitialized) { - if (transformedPointLeft > _currentClipRight) { + if (transformedPointLeft >= _currentClipRight) { command.isClippedOut = true; return; } - if (transformedPointRight < _currentClipLeft) { + if (transformedPointRight <= _currentClipLeft) { command.isClippedOut = true; return; } - if (transformedPointTop > _currentClipBottom) { + if (transformedPointTop >= _currentClipBottom) { command.isClippedOut = true; return; } - if (transformedPointBottom < _currentClipTop) { + if (transformedPointBottom <= _currentClipTop) { command.isClippedOut = true; return; } @@ -1818,6 +1885,52 @@ class _PaintBounds { _didPaintInsideClipArea = true; } + /// Grow painted area to include given rectangle. + void growBounds(ui.Rect bounds) { + final double left = bounds.left; + final double top = bounds.top; + final double right = bounds.right; + final double bottom = bounds.bottom; + if (left == right || top == bottom) { + return; + } + + double transformedPointLeft = left; + double transformedPointTop = top; + double transformedPointRight = right; + double transformedPointBottom = bottom; + + if (!_currentMatrixIsIdentity) { + _tempRectData[0] = left; + _tempRectData[1] = top; + _tempRectData[2] = right; + _tempRectData[3] = bottom; + + transformLTRB(_currentMatrix, _tempRectData); + transformedPointLeft = _tempRectData[0]; + transformedPointTop = _tempRectData[1]; + transformedPointRight = _tempRectData[2]; + transformedPointBottom = _tempRectData[3]; + } + + if (_didPaintInsideClipArea) { + _left = math.min( + math.min(_left, transformedPointLeft), transformedPointRight); + _right = math.max( + math.max(_right, transformedPointLeft), transformedPointRight); + _top = + math.min(math.min(_top, transformedPointTop), transformedPointBottom); + _bottom = math.max( + math.max(_bottom, transformedPointTop), transformedPointBottom); + } else { + _left = math.min(transformedPointLeft, transformedPointRight); + _right = math.max(transformedPointLeft, transformedPointRight); + _top = math.min(transformedPointTop, transformedPointBottom); + _bottom = math.max(transformedPointTop, transformedPointBottom); + } + _didPaintInsideClipArea = true; + } + void saveTransformsAndClip() { _transforms.add(_currentMatrix.clone()); _clipStack.add(_clipRectInitialized @@ -1910,3 +2023,35 @@ double _getPaintSpread(SurfacePaint paint) { } return spread; } + +/// Contains metrics collected by recording canvas to provide data for +/// rendering heuristics (canvas use vs DOM). +class RenderStrategy { + /// Whether paint commands contain image elements. + bool hasImageElements = false; + + /// Whether paint commands contain paragraphs. + bool hasParagraphs = false; + + /// Whether paint commands are doing arbitrary operations + /// not expressible via pure DOM elements. + /// + /// This is used to decide whether to use simplified DomCanvas. + bool hasArbitraryPaint = false; + + /// Whether commands are executed within a shadermask or color filter. + /// + /// Webkit doesn't apply filters to canvas elements in its child + /// element tree. When this is set to true, we prevent canvas usage in + /// bitmap canvas and instead render using dom primitives and svg only. + bool isInsideSvgFilterTree = false; + + RenderStrategy(); + + /// Merges render strategy settings from a child recording. + void merge(RenderStrategy childStrategy) { + hasImageElements |= childStrategy.hasImageElements; + hasParagraphs |= childStrategy.hasParagraphs; + hasArbitraryPaint |= childStrategy.hasArbitraryPaint; + } +} diff --git a/lib/web_ui/lib/src/engine/html/render_vertices.dart b/lib/web_ui/lib/src/engine/html/render_vertices.dart index 11d400bfc5f5b..7034c392a901a 100644 --- a/lib/web_ui/lib/src/engine/html/render_vertices.dart +++ b/lib/web_ui/lib/src/engine/html/render_vertices.dart @@ -2,64 +2,81 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:math' as math; +import 'dart:typed_data'; -_GlRenderer? _glRenderer; +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'painting.dart'; +import 'shaders/image_shader.dart'; +import 'shaders/normalized_gradient.dart'; +import 'shaders/shader_builder.dart'; +import 'shaders/vertex_shaders.dart'; +import 'shaders/webgl_context.dart'; + +GlRenderer? glRenderer; class SurfaceVertices implements ui.Vertices { - final ui.VertexMode _mode; - final Float32List _positions; - final Int32List? _colors; - final Uint16List? _indices; // ignore: unused_field + final ui.VertexMode mode; + final Float32List positions; + final Int32List? colors; + final Uint16List? indices; // ignore: unused_field SurfaceVertices( - ui.VertexMode mode, + this.mode, List positions, { List? colors, List? indices, }) : assert(mode != null), // ignore: unnecessary_null_comparison assert(positions != null), // ignore: unnecessary_null_comparison - _mode = mode, - _colors = colors != null ? _int32ListFromColors(colors) : null, - _indices = indices != null ? Uint16List.fromList(indices) : null, - _positions = offsetListToFloat32List(positions) { + // ignore: unnecessary_this + this.colors = colors != null ? _int32ListFromColors(colors) : null, + // ignore: unnecessary_this + this.indices = indices != null ? Uint16List.fromList(indices) : null, + // ignore: unnecessary_this + this.positions = offsetListToFloat32List(positions) { initWebGl(); } SurfaceVertices.raw( - ui.VertexMode mode, - Float32List positions, { - Int32List? colors, - Uint16List? indices, + this.mode, + this.positions, { + this.colors, + this.indices, }) : assert(mode != null), // ignore: unnecessary_null_comparison - assert(positions != null), // ignore: unnecessary_null_comparison - _mode = mode, - _positions = positions, - _colors = colors, - _indices = indices { + assert(positions != null) { // ignore: unnecessary_null_comparison initWebGl(); } static Int32List _int32ListFromColors(List colors) { - Int32List list = Int32List(colors.length); - for (int i = 0, len = colors.length; i < len; i++) { + final Int32List list = Int32List(colors.length); + final int len = colors.length; + for (int i = 0; i < len; i++) { list[i] = colors[i].value; } return list; } } +/// Lazily initializes web gl. +/// +/// Used to treeshake WebGlRenderer when user doesn't create Vertices object +/// to use the API. void initWebGl() { - _glRenderer ??= _WebGlRenderer(); + glRenderer ??= _WebGlRenderer(); } void disposeWebGl() { - _OffscreenCanvas.dispose(); - _glRenderer = null; + GlContextCache.dispose(); + glRenderer = null; } -abstract class _GlRenderer { +abstract class GlRenderer { void drawVertices( html.CanvasRenderingContext2D? context, int canvasWidthInPixels, @@ -69,6 +86,17 @@ abstract class _GlRenderer { ui.BlendMode blendMode, SurfacePaintData paint); + Object? drawRect(ui.Rect targetRect, GlContext gl, GlProgram glProgram, + NormalizedGradient gradient, int widthInPixels, int heightInPixels); + + String drawRectToImageUrl( + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels); + void drawHairline(html.CanvasRenderingContext2D? _ctx, Float32List positions); } @@ -76,52 +104,7 @@ abstract class _GlRenderer { /// /// This class gets instantiated on demand by Vertices constructor. For apps /// that don't use Vertices WebGlRenderer will be removed from release binary. -class _WebGlRenderer implements _GlRenderer { - // Vertex shader transforms pixel space [Vertices.positions] to - // final clipSpace -1..1 coordinates with inverted Y Axis. - static const _vertexShaderTriangle = ''' - #version 300 es - layout (location=0) in vec4 position; - layout (location=1) in vec4 color; - uniform mat4 u_ctransform; - uniform vec4 u_scale; - uniform vec4 u_shift; - out vec4 vColor; - void main() { - gl_Position = ((u_ctransform * position) * u_scale) + u_shift; - vColor = color.zyxw; - }'''; - // This fragment shader enables Int32List of colors to be passed directly - // to gl context buffer for rendering by decoding RGBA8888. - static const _fragmentShaderTriangle = ''' - #version 300 es - precision highp float; - in vec4 vColor; - out vec4 fragColor; - void main() { - fragColor = vColor; - }'''; - - // WebGL 1 version of shaders above for compatibility with Safari. - static const _vertexShaderTriangleEs1 = ''' - attribute vec4 position; - attribute vec4 color; - uniform mat4 u_ctransform; - uniform vec4 u_scale; - uniform vec4 u_shift; - varying vec4 vColor; - void main() { - gl_Position = ((u_ctransform * position) * u_scale) + u_shift; - vColor = color.zyxw; - }'''; - // WebGL 1 version of shaders above for compatibility with Safari. - static const _fragmentShaderTriangleEs1 = ''' - precision highp float; - varying vec4 vColor; - void main() { - gl_FragColor = vColor; - }'''; - +class _WebGlRenderer implements GlRenderer { @override void drawVertices( html.CanvasRenderingContext2D? context, @@ -132,12 +115,12 @@ class _WebGlRenderer implements _GlRenderer { ui.BlendMode blendMode, SurfacePaintData paint) { // Compute bounds of vertices. - final Float32List positions = vertices._positions; - ui.Rect bounds = _computeVerticesBounds(positions, transform); - double minValueX = bounds.left; - double minValueY = bounds.top; - double maxValueX = bounds.right; - double maxValueY = bounds.bottom; + final Float32List positions = vertices.positions; + final ui.Rect bounds = _computeVerticesBounds(positions, transform); + final double minValueX = bounds.left; + final double minValueY = bounds.top; + final double maxValueX = bounds.right; + final double maxValueY = bounds.bottom; double offsetX = 0; double offsetY = 0; int widthInPixels = canvasWidthInPixels; @@ -161,61 +144,299 @@ class _WebGlRenderer implements _GlRenderer { if (widthInPixels == 0 || heightInPixels == 0) { return; } - _GlContext gl = - _OffscreenCanvas.createGlContext(widthInPixels, heightInPixels)!; - _GlProgram glProgram = webGLVersion == 1 - ? gl.useAndCacheProgram( - _vertexShaderTriangleEs1, _fragmentShaderTriangleEs1)! - : gl.useAndCacheProgram( - _vertexShaderTriangle, _fragmentShaderTriangle)!; - - Object? transformUniform = gl.getUniformLocation(glProgram.program, 'u_ctransform'); - Matrix4 transformAtOffset = transform.clone()..translate(-offsetX, -offsetY); - gl.setUniformMatrix4fv(transformUniform, false, transformAtOffset.storage); + + final bool isWebGl2 = webGLVersion == WebGLVersion.webgl2; + + final EngineImageShader? imageShader = + paint.shader == null ? null : paint.shader! as EngineImageShader; + + final String vertexShader = imageShader == null + ? VertexShaders.writeBaseVertexShader() + : VertexShaders.writeTextureVertexShader(); + final String fragmentShader = imageShader == null + ? _writeVerticesFragmentShader() + : FragmentShaders.writeTextureFragmentShader( + isWebGl2, imageShader.tileModeX, imageShader.tileModeY); + + final GlContext gl = + GlContextCache.createGlContext(widthInPixels, heightInPixels)!; + + final GlProgram glProgram = gl.cacheProgram(vertexShader, fragmentShader); + gl.useProgram(glProgram); + + final Object positionAttributeLocation = + gl.getAttributeLocation(glProgram.program, 'position'); + + setupVertexTransforms(gl, glProgram, offsetX, offsetY, + widthInPixels.toDouble(), heightInPixels.toDouble(), transform); + + if (imageShader != null) { + /// To map from vertex position to texture coordinate in 0..1 range, + /// we setup scalar to be used in vertex shader. + setupTextureTransform( + gl, + glProgram, + 0.0, + 0.0, + 1.0 / imageShader.image.width.toDouble(), + 1.0 / imageShader.image.height.toDouble()); + } + + // Setup geometry. + // + // Create buffer for vertex coordinates. + final Object positionsBuffer = gl.createBuffer()!; + assert(positionsBuffer != null); // ignore: unnecessary_null_comparison + + Object? vao; + if (imageShader != null) { + if (isWebGl2) { + // Create a vertex array object. + vao = gl.createVertexArray(); + // Set vertex array object as active one. + gl.bindVertexArray(vao!); + } + } + // Turn on position attribute. + gl.enableVertexAttribArray(positionAttributeLocation); + // Bind buffer as position buffer and transfer data. + gl.bindArrayBuffer(positionsBuffer); + bufferVertexData(gl, positions, 1.0); + + // Setup data format for attribute. + js_util.callMethod(gl.glContext, 'vertexAttribPointer', [ + positionAttributeLocation, + 2, + gl.kFloat, + false, + 0, + 0, + ]); + + final int vertexCount = positions.length ~/ 2; + Object? texture; + + if (imageShader == null) { + // Setup color buffer. + final Object? colorsBuffer = gl.createBuffer(); + gl.bindArrayBuffer(colorsBuffer); + + // Buffer kBGRA_8888. + if (vertices.colors == null) { + final ui.Color color = paint.color ?? const ui.Color(0xFF000000); + final Uint32List vertexColors = Uint32List(vertexCount); + for (int i = 0; i < vertexCount; i++) { + vertexColors[i] = color.value; + } + gl.bufferData(vertexColors, gl.kStaticDraw); + } else { + gl.bufferData(vertices.colors, gl.kStaticDraw); + } + final Object colorLoc = gl.getAttributeLocation(glProgram.program, 'color'); + js_util.callMethod(gl.glContext, 'vertexAttribPointer', + [colorLoc, 4, gl.kUnsignedByte, true, 0, 0]); + gl.enableVertexAttribArray(colorLoc); + } else { + // Copy image it to the texture. + texture = gl.createTexture(); + // Texture units are a global array of references to the textures. + // By setting activeTexture, we associate the bound texture to a unit. + // Every time we call a texture function such as texImage2D with a target + // like TEXTURE_2D, it looks up texture by using the currently active + // unit. + // In our case we have a single texture unit 0. + gl.activeTexture(gl.kTexture0); + gl.bindTexture(gl.kTexture2D, texture); + + gl.texImage2D(gl.kTexture2D, 0, gl.kRGBA, gl.kRGBA, gl.kUnsignedByte, + imageShader.image.imgElement); + + if (isWebGl2) { + // Texture REPEAT and MIRROR is only supported in WebGL 2, for + // WebGL 1.0 we let shader compute correct uv coordinates. + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapS, + tileModeToGlWrapping(gl, imageShader.tileModeX)); + + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapT, + tileModeToGlWrapping(gl, imageShader.tileModeY)); + + // Mipmapping saves your texture in different resolutions + // so the graphics card can choose which resolution is optimal + // without artifacts. + gl.generateMipmap(gl.kTexture2D); + } else { + // For webgl1, if a texture is not mipmap complete, then the return + // value of a texel fetch is (0, 0, 0, 1), so we have to set + // minifying function to filter. + // See https://www.khronos.org/registry/webgl/specs/1.0.0/#5.13.8. + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapS, gl.kClampToEdge); + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapT, gl.kClampToEdge); + gl.texParameteri(gl.kTexture2D, gl.kTextureMinFilter, gl.kLinear); + } + } + + // Finally render triangles. + gl.clear(); + + final Uint16List? indices = vertices.indices; + if (indices == null) { + gl.drawTriangles(vertexCount, vertices.mode); + } else { + /// If indices are specified to use shared vertices to reduce vertex + /// data transfer, use drawElements to map from vertex indices to + /// triangles. + final Object? indexBuffer = gl.createBuffer(); + gl.bindElementArrayBuffer(indexBuffer); + gl.bufferElementData(indices, gl.kStaticDraw); + gl.drawElements(gl.kTriangles, indices.length, gl.kUnsignedShort); + } + + if (vao != null) { + gl.unbindVertexArray(); + } + + context!.save(); + context.resetTransform(); + gl.drawImage(context, offsetX, offsetY); + context.restore(); + } + + /// Renders a rectangle using given program into an image resource. + /// + /// Browsers that support OffscreenCanvas and the transferToImageBitmap api + /// will return ImageBitmap, otherwise will return CanvasElement. + @override + Object? drawRect(ui.Rect targetRect, GlContext gl, GlProgram glProgram, + NormalizedGradient gradient, int widthInPixels, int heightInPixels) { + drawRectToGl( + targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); + final Object? image = gl.readPatternData(); + gl.bindArrayBuffer(null); + gl.bindElementArrayBuffer(null); + return image; + } + + /// Renders a rectangle using given program into an image resource and returns + /// url. + @override + String drawRectToImageUrl( + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels) { + drawRectToGl( + targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); + final String imageUrl = gl.toImageUrl(); + // Cleanup buffers. + gl.bindArrayBuffer(null); + gl.bindElementArrayBuffer(null); + return imageUrl; + } + + /// Renders a rectangle using given program into [GlContext]. + /// + /// Caller has to cleanup gl array and element array buffers. + void drawRectToGl(ui.Rect targetRect, GlContext gl, GlProgram glProgram, + NormalizedGradient gradient, int widthInPixels, int heightInPixels) { + // Setup rectangle coordinates. + final double left = targetRect.left; + final double top = targetRect.top; + final double right = targetRect.right; + final double bottom = targetRect.bottom; + // Form 2 triangles for rectangle. + final Float32List vertices = Float32List(8); + vertices[0] = left; + vertices[1] = top; + vertices[2] = right; + vertices[3] = top; + vertices[4] = right; + vertices[5] = bottom; + vertices[6] = left; + vertices[7] = bottom; + + final Object transformUniform = + gl.getUniformLocation(glProgram.program, 'u_ctransform'); + gl.setUniformMatrix4fv(transformUniform, false, Matrix4.identity().storage); // Set uniform to scale 0..width/height pixels coordinates to -1..1 // clipspace range and flip the Y axis. - Object? resolution = gl.getUniformLocation(glProgram.program, 'u_scale'); + final Object resolution = gl.getUniformLocation(glProgram.program, 'u_scale'); gl.setUniform4f(resolution, 2.0 / widthInPixels.toDouble(), -2.0 / heightInPixels.toDouble(), 1, 1); - Object? shift = gl.getUniformLocation(glProgram.program, 'u_shift'); + final Object shift = gl.getUniformLocation(glProgram.program, 'u_shift'); gl.setUniform4f(shift, -1, 1, 0, 0); // Setup geometry. - Object positionsBuffer = gl.createBuffer()!; + final Object positionsBuffer = gl.createBuffer()!; assert(positionsBuffer != null); // ignore: unnecessary_null_comparison gl.bindArrayBuffer(positionsBuffer); - gl.bufferData(positions, gl.kStaticDraw); - js_util.callMethod( - gl.glContext!, 'vertexAttribPointer', [0, 2, gl.kFloat, false, 0, 0]); + gl.bufferData(vertices, gl.kStaticDraw); + // Point an attribute to the currently bound vertex buffer object. + js_util.callMethod(gl.glContext, 'vertexAttribPointer', + [0, 2, gl.kFloat, false, 0, 0]); gl.enableVertexAttribArray(0); // Setup color buffer. - Object? colorsBuffer = gl.createBuffer(); + final Object? colorsBuffer = gl.createBuffer(); gl.bindArrayBuffer(colorsBuffer); // Buffer kBGRA_8888. - gl.bufferData(vertices._colors, gl.kStaticDraw); - - js_util.callMethod(gl.glContext!, 'vertexAttribPointer', + final Int32List colors = Int32List.fromList([ + 0xFF00FF00, + 0xFF0000FF, + 0xFFFFFF00, + 0xFF00FFFF, + ]); + gl.bufferData(colors, gl.kStaticDraw); + js_util.callMethod(gl.glContext, 'vertexAttribPointer', [1, 4, gl.kUnsignedByte, true, 0, 0]); gl.enableVertexAttribArray(1); - gl.clear(); - final int vertexCount = positions.length ~/ 2; - gl.drawTriangles(vertexCount, vertices._mode); - context!.save(); - context.resetTransform(); - gl.drawImage(context, offsetX, offsetY); - context.restore(); + final Object? indexBuffer = gl.createBuffer(); + gl.bindElementArrayBuffer(indexBuffer); + gl.bufferElementData(VertexShaders.vertexIndicesForRect, gl.kStaticDraw); + + if (gl.containsUniform(glProgram.program, 'u_resolution')) { + final Object uRes = gl.getUniformLocation(glProgram.program, 'u_resolution'); + gl.setUniform2f( + uRes, widthInPixels.toDouble(), heightInPixels.toDouble()); + } + + gl.clear(); + gl.viewport(0, 0, widthInPixels.toDouble(), heightInPixels.toDouble()); + + gl.drawElements( + gl.kTriangles, VertexShaders.vertexIndicesForRect.length, gl.kUnsignedShort); + } + + /// This fragment shader enables Int32List of colors to be passed directly + /// to gl context buffer for rendering by decoding RGBA8888. + /// #version 300 es + /// precision mediump float; + /// in vec4 vColor; + /// out vec4 fragColor; + /// void main() { + /// fragColor = vColor; + /// } + String _writeVerticesFragmentShader() { + final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); + builder.floatPrecision = ShaderPrecision.kMedium; + builder.addIn(ShaderType.kVec4, name: 'v_color'); + final ShaderMethod method = builder.addMethod('main'); + method.addStatement('${builder.fragmentColor.name} = v_color;'); + return builder.build(); } @override - void drawHairline(html.CanvasRenderingContext2D? _ctx, Float32List positions) { + void drawHairline( + html.CanvasRenderingContext2D? _ctx, Float32List positions) { assert(positions != null); // ignore: unnecessary_null_comparison final int pointCount = positions.length ~/ 2; _ctx!.lineWidth = 1.0; _ctx.beginPath(); - for (int i = 0, len = pointCount * 2; i < len;) { + final int len = pointCount * 2; + for (int i = 0; i < len;) { for (int triangleVertexIndex = 0; triangleVertexIndex < 3; triangleVertexIndex++, i += 2) { @@ -242,7 +463,8 @@ ui.Rect _computeVerticesBounds(Float32List positions, Matrix4 transform) { double minValueX, maxValueX, minValueY, maxValueY; minValueX = maxValueX = positions[0]; minValueY = maxValueY = positions[1]; - for (int i = 2, len = positions.length; i < len; i += 2) { + final int len = positions.length; + for (int i = 2; i < len; i += 2) { final double x = positions[i]; final double y = positions[i + 1]; if (x.isNaN || y.isNaN) { @@ -283,15 +505,15 @@ ui.Rect _transformBounds( math.max(y0, math.max(y1, math.max(y2, y3)))); } -// Converts from [VertexMode] triangleFan and triangleStrip to triangles. -Float32List _convertVertexPositions(ui.VertexMode mode, Float32List positions) { +/// Converts from [VertexMode] triangleFan and triangleStrip to triangles. +Float32List convertVertexPositions(ui.VertexMode mode, Float32List positions) { assert(mode != ui.VertexMode.triangles); if (mode == ui.VertexMode.triangleFan) { final int coordinateCount = positions.length ~/ 2; final int triangleCount = coordinateCount - 2; final Float32List triangleList = Float32List(triangleCount * 3 * 2); - double centerX = positions[0]; - double centerY = positions[1]; + final double centerX = positions[0]; + final double centerY = positions[1]; int destIndex = 0; int positionIndex = 2; for (int triangleIndex = 0; @@ -309,7 +531,7 @@ Float32List _convertVertexPositions(ui.VertexMode mode, Float32List positions) { assert(mode == ui.VertexMode.triangleStrip); // Set of connected triangles. Each triangle shares 2 last vertices. final int vertexCount = positions.length ~/ 2; - int triangleCount = vertexCount - 2; + final int triangleCount = vertexCount - 2; double x0 = positions[0]; double y0 = positions[1]; double x1 = positions[2]; @@ -333,348 +555,3 @@ Float32List _convertVertexPositions(ui.VertexMode mode, Float32List positions) { return triangleList; } } - -/// Compiled and cached gl program. -class _GlProgram { - final Object? program; - _GlProgram(this.program); -} - -/// JS Interop helper for webgl apis. -class _GlContext { - final Object? glContext; - final bool isOffscreen; - dynamic _kCompileStatus; - dynamic _kArrayBuffer; - dynamic _kStaticDraw; - dynamic _kFloat; - dynamic _kColorBufferBit; - dynamic _kTriangles; - dynamic _kLinkStatus; - dynamic _kUnsignedByte; - dynamic _kRGBA; - Object? _canvas; - int? _widthInPixels; - int? _heightInPixels; - static late Map _programCache; - - _GlContext.fromOffscreenCanvas(html.OffscreenCanvas canvas) - : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false}), - isOffscreen = true { - _programCache = {}; - _canvas = canvas; - } - - _GlContext.fromCanvas(html.CanvasElement canvas, bool useWebGl1) - : glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', - {'premultipliedAlpha': false}), - isOffscreen = false { - _programCache = {}; - _canvas = canvas; - } - - void setViewportSize(int width, int height) { - _widthInPixels = width; - _heightInPixels = height; - } - - /// Draws Gl context contents to canvas context. - void drawImage(html.CanvasRenderingContext2D context, - double left, double top) { - // Actual size of canvas may be larger than viewport size. Use - // source/destination to draw part of the image data. - js_util.callMethod(context, 'drawImage', - [_canvas, 0, 0, _widthInPixels, _heightInPixels, - left, top, _widthInPixels, _heightInPixels]); - } - - _GlProgram? useAndCacheProgram( - String vertexShaderSource, String fragmentShaderSource) { - String cacheKey = '$vertexShaderSource||$fragmentShaderSource'; - _GlProgram? cachedProgram = _programCache[cacheKey]; - if (cachedProgram == null) { - // Create and compile shaders. - Object vertexShader = compileShader('VERTEX_SHADER', vertexShaderSource); - Object fragmentShader = - compileShader('FRAGMENT_SHADER', fragmentShaderSource); - // Create a gl program and link shaders. - Object? program = createProgram(); - attachShader(program, vertexShader); - attachShader(program, fragmentShader); - linkProgram(program); - cachedProgram = _GlProgram(program); - _programCache[cacheKey] = cachedProgram; - useProgram(program); - } - return cachedProgram; - } - - Object compileShader(String shaderType, String source) { - Object? shader = _createShader(shaderType); - if (shader == null) { - throw Exception(error); - } - js_util.callMethod(glContext!, 'shaderSource', [shader, source]); - js_util.callMethod(glContext!, 'compileShader', [shader]); - bool shaderStatus = js_util - .callMethod(glContext!, 'getShaderParameter', [shader, compileStatus]); - if (!shaderStatus) { - throw Exception('Shader compilation failed: ${getShaderInfoLog(shader)}'); - } - return shader; - } - - Object? createProgram() => - js_util.callMethod(glContext!, 'createProgram', const []); - - void attachShader(Object? program, Object shader) { - js_util.callMethod(glContext!, 'attachShader', [program, shader]); - } - - void linkProgram(Object? program) { - js_util.callMethod(glContext!, 'linkProgram', [program]); - if (!js_util - .callMethod(glContext!, 'getProgramParameter', [program, kLinkStatus])) { - throw Exception(getProgramInfoLog(program)); - } - } - - void useProgram(Object? program) { - js_util.callMethod(glContext!, 'useProgram', [program]); - } - - Object? createBuffer() => - js_util.callMethod(glContext!, 'createBuffer', const []); - - void bindArrayBuffer(Object? buffer) { - js_util.callMethod(glContext!, 'bindBuffer', [kArrayBuffer, buffer]); - } - - void deleteBuffer(Object buffer) { - js_util.callMethod(glContext!, 'deleteBuffer', [buffer]); - } - - void bufferData(TypedData? data, dynamic type) { - js_util.callMethod(glContext!, 'bufferData', [kArrayBuffer, data, type]); - } - - void enableVertexAttribArray(int index) { - js_util.callMethod(glContext!, 'enableVertexAttribArray', [index]); - } - - /// Clear background. - void clear() { - js_util.callMethod(glContext!, 'clear', [kColorBufferBit]); - } - - /// Destroys gl context. - void dispose() { - js_util.callMethod(_getExtension('WEBGL_lose_context'), 'loseContext', const []); - } - - void deleteProgram(Object program) { - js_util.callMethod(glContext!, 'deleteProgram', [program]); - } - - void deleteShader(Object shader) { - js_util.callMethod(glContext!, 'deleteShader', [shader]); - } - - dynamic _getExtension(String extensionName) => - js_util.callMethod(glContext!, 'getExtension', [extensionName]); - - void drawTriangles(int triangleCount, ui.VertexMode vertexMode) { - dynamic mode = _triangleTypeFromMode(vertexMode); - js_util.callMethod(glContext!, 'drawArrays', [mode, 0, triangleCount]); - } - - /// Sets affine transformation from normalized device coordinates - /// to window coordinates - void viewport(double x, double y, double width, double height) { - js_util.callMethod(glContext!, 'viewport', [x, y, width, height]); - } - - dynamic _triangleTypeFromMode(ui.VertexMode mode) { - switch (mode) { - case ui.VertexMode.triangles: - return kTriangles; - case ui.VertexMode.triangleFan: - return kTriangleFan; - case ui.VertexMode.triangleStrip: - return kTriangleStrip; - } - } - - Object? _createShader(String shaderType) => js_util.callMethod( - glContext!, 'createShader', [js_util.getProperty(glContext!, shaderType)]); - - /// Error state of gl context. - dynamic get error => js_util.callMethod(glContext!, 'getError', const []); - - /// Shader compiler error, if this returns [kFalse], to get details use - /// [getShaderInfoLog]. - dynamic get compileStatus => - _kCompileStatus ??= js_util.getProperty(glContext!, 'COMPILE_STATUS'); - - dynamic get kArrayBuffer => - _kArrayBuffer ??= js_util.getProperty(glContext!, 'ARRAY_BUFFER'); - - dynamic get kLinkStatus => - _kLinkStatus ??= js_util.getProperty(glContext!, 'LINK_STATUS'); - - dynamic get kFloat => _kFloat ??= js_util.getProperty(glContext!, 'FLOAT'); - - dynamic get kRGBA => _kRGBA ??= js_util.getProperty(glContext!, 'RGBA'); - - dynamic get kUnsignedByte => - _kUnsignedByte ??= js_util.getProperty(glContext!, 'UNSIGNED_BYTE'); - - dynamic get kStaticDraw => - _kStaticDraw ??= js_util.getProperty(glContext!, 'STATIC_DRAW'); - - dynamic get kTriangles => - _kTriangles ??= js_util.getProperty(glContext!, 'TRIANGLES'); - - dynamic get kTriangleFan => - _kTriangles ??= js_util.getProperty(glContext!, 'TRIANGLE_FAN'); - - dynamic get kTriangleStrip => - _kTriangles ??= js_util.getProperty(glContext!, 'TRIANGLE_STRIP'); - - dynamic get kColorBufferBit => - _kColorBufferBit ??= js_util.getProperty(glContext!, 'COLOR_BUFFER_BIT'); - - /// Returns reference to uniform in program. - Object? getUniformLocation(Object? program, String uniformName) { - return js_util - .callMethod(glContext!, 'getUniformLocation', [program, uniformName]); - } - - /// Sets vec2 uniform values. - void setUniform2f(Object uniform, double value1, double value2) { - return js_util - .callMethod(glContext!, 'uniform2f', [uniform, value1, value2]); - } - - /// Sets vec4 uniform values. - void setUniform4f(Object? uniform, double value1, double value2, double value3, - double value4) { - return js_util.callMethod( - glContext!, 'uniform4f', [uniform, value1, value2, value3, value4]); - } - - /// Sets mat4 uniform values. - void setUniformMatrix4fv(Object? uniform, bool transpose, Float32List? value) { - return js_util.callMethod( - glContext!, 'uniformMatrix4fv', [uniform, transpose, value]); - } - - /// Shader compile error log. - dynamic getShaderInfoLog(Object glShader) { - return js_util.callMethod(glContext!, 'getShaderInfoLog', [glShader]); - } - - /// Errors that occurred during failed linking or validation of program - /// objects. Typically called after [linkProgram]. - String? getProgramInfoLog(Object? glProgram) { - return js_util.callMethod(glContext!, 'getProgramInfoLog', [glProgram]); - } - - int? get drawingBufferWidth => - js_util.getProperty(glContext!, 'drawingBufferWidth'); - int? get drawingBufferHeight => - js_util.getProperty(glContext!, 'drawingBufferWidth'); - - html.ImageData readImageData() { - if (browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.firefox) { - const int kBytesPerPixel = 4; - final int bufferWidth = _widthInPixels!; - final int bufferHeight = _heightInPixels!; - final Uint8List pixels = - Uint8List(bufferWidth * bufferHeight * kBytesPerPixel); - js_util.callMethod(glContext!, 'readPixels', - [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); - return html.ImageData( - Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight); - } else { - const int kBytesPerPixel = 4; - final int bufferWidth = _widthInPixels!; - final int bufferHeight = _heightInPixels!; - final Uint8ClampedList pixels = - Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel); - js_util.callMethod(glContext!, 'readPixels', - [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); - return html.ImageData(pixels, bufferWidth, bufferHeight); - } - } -} - -/// Shared Cached OffscreenCanvas for webgl rendering to image. -class _OffscreenCanvas { - static html.OffscreenCanvas? _canvas; - static int _maxPixelWidth = 0; - static int _maxPixelHeight = 0; - static html.CanvasElement? _glCanvas; - static _GlContext? _cachedContext; - - _OffscreenCanvas(int width, int height) { - assert(width > 0 && height > 0); - if (width > _maxPixelWidth || height > _maxPixelHeight) { - // Allocate bigger offscreen canvas. - _canvas = html.OffscreenCanvas(width, height); - _maxPixelWidth = width; - _maxPixelHeight = height; - _cachedContext?.dispose(); - _cachedContext = null; - } - } - - static void dispose() { - _canvas = null; - _maxPixelWidth = 0; - _maxPixelHeight = 0; - _glCanvas = null; - _cachedContext = null; - } - - html.OffscreenCanvas? get canvas => _canvas; - - static _GlContext? createGlContext(int widthInPixels, int heightInPixels) { - final bool isWebKit = (browserEngine == BrowserEngine.webkit); - - if (_OffscreenCanvas.supported) { - final _OffscreenCanvas offScreenCanvas = - _OffscreenCanvas(widthInPixels, heightInPixels); - _cachedContext ??= _GlContext.fromOffscreenCanvas(offScreenCanvas.canvas!); - _cachedContext!.setViewportSize(widthInPixels, heightInPixels); - return _cachedContext; - } else { - // Allocate new canvas element is size is larger. - if (widthInPixels > _maxPixelWidth || heightInPixels > _maxPixelHeight) { - _glCanvas = html.CanvasElement( - width: widthInPixels, - height: heightInPixels, - ); - _glCanvas!.className = 'gl-canvas'; - final double cssWidth = widthInPixels / EngineWindow.browserDevicePixelRatio; - final double cssHeight = heightInPixels / EngineWindow.browserDevicePixelRatio; - _glCanvas!.style - ..position = 'absolute' - ..width = '${cssWidth}px' - ..height = '${cssHeight}px'; - _maxPixelWidth = widthInPixels; - _maxPixelHeight = heightInPixels; - _cachedContext?.dispose(); - _cachedContext = null; - } - _cachedContext ??= _GlContext.fromCanvas(_glCanvas!, isWebKit); - _cachedContext!.setViewportSize(widthInPixels, heightInPixels); - return _cachedContext; - } - } - - /// Feature detects OffscreenCanvas. - static bool get supported => - js_util.hasProperty(html.window, 'OffscreenCanvas'); -} diff --git a/lib/web_ui/lib/src/engine/html/scene.dart b/lib/web_ui/lib/src/engine/html/scene.dart index 2207deeba72d8..48d1c5c7e72f2 100644 --- a/lib/web_ui/lib/src/engine/html/scene.dart +++ b/lib/web_ui/lib/src/engine/html/scene.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../vector_math.dart'; +import 'surface.dart'; class SurfaceScene implements ui.Scene { /// This class is created by the engine, and should not be instantiated @@ -16,6 +20,7 @@ class SurfaceScene implements ui.Scene { /// Creates a raster image representation of the current state of the scene. /// This is a slow operation that is performed on a background thread. + @override Future toImage(int width, int height) { throw UnsupportedError('toImage is not supported on the Web'); } @@ -23,13 +28,14 @@ class SurfaceScene implements ui.Scene { /// Releases the resources used by this scene. /// /// After calling this function, the scene is cannot be used further. + @override void dispose() {} } /// A surface that creates a DOM element for whole app. class PersistedScene extends PersistedContainerSurface { PersistedScene(PersistedScene? oldLayer) : super(oldLayer) { - _transform = Matrix4.identity(); + transform = Matrix4.identity(); } @override @@ -40,13 +46,17 @@ class PersistedScene extends PersistedContainerSurface { // update this code when we add add2app support. final double screenWidth = html.window.innerWidth!.toDouble(); final double screenHeight = html.window.innerHeight!.toDouble(); - _localClipBounds = ui.Rect.fromLTRB(0, 0, screenWidth, screenHeight); - _localTransformInverse = Matrix4.identity(); - _projectedClip = null; + localClipBounds = ui.Rect.fromLTRB(0, 0, screenWidth, screenHeight); + projectedClip = null; } + /// Cached inverse of transform on this node. Unlike transform, this + /// Matrix only contains local transform (not chain multiplied since root). + Matrix4? _localTransformInverse; + @override - Matrix4? get localTransformInverse => _localTransformInverse; + Matrix4? get localTransformInverse => + _localTransformInverse ??= Matrix4.identity(); @override html.Element createElement() { diff --git a/lib/web_ui/lib/src/engine/html/scene_builder.dart b/lib/web_ui/lib/src/engine/html/scene_builder.dart index 1749fcb2101b0..474a7a62b724d 100644 --- a/lib/web_ui/lib/src/engine/html/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/html/scene_builder.dart @@ -2,8 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show kProfileApplyFrame, kProfilePrerollFrame, toMatrix32; +import '../picture.dart'; +import '../profiler.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import '../window.dart'; +import 'backdrop_filter.dart'; +import 'clip.dart'; +import 'color_filter.dart'; +import 'image_filter.dart'; +import 'offset.dart'; +import 'opacity.dart'; +import 'path/path.dart'; +import 'path_to_svg_clip.dart'; +import 'picture.dart'; +import 'platform_view.dart'; +import 'scene.dart'; +import 'shader_mask.dart'; +import 'shaders/shader.dart'; +import 'surface.dart'; +import 'transform.dart'; class SurfaceSceneBuilder implements ui.SceneBuilder { SurfaceSceneBuilder() { @@ -23,7 +46,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// The surface currently being built. PersistedContainerSurface get _currentSurface => _surfaceStack.last; - ui.EngineLayer _pushSurface(PersistedContainerSurface surface) { + T _pushSurface(T surface) { // Only attempt to update if the update is requested and the surface is in // the live tree. if (surface.oldLayer != null) { @@ -65,7 +88,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { double dy, { ui.OffsetEngineLayer? oldLayer, }) { - return _pushSurface(PersistedOffset(oldLayer as PersistedOffset?, dx, dy)) as ui.OffsetEngineLayer; + return _pushSurface( + PersistedOffset(oldLayer as PersistedOffset?, dx, dy)); } /// Pushes a transform operation onto the operation stack. @@ -90,13 +114,14 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { // logical device pixels. if (!ui.debugEmulateFlutterTesterEnvironment) { assert(matrix4[0] == window.devicePixelRatio && - matrix4[5] == window.devicePixelRatio); + matrix4[5] == window.devicePixelRatio); } matrix = Matrix4.identity().storage; } else { matrix = toMatrix32(matrix4); } - return _pushSurface(PersistedTransform(oldLayer as PersistedTransform?, matrix)) as ui.TransformEngineLayer; + return _pushSurface( + PersistedTransform(oldLayer as PersistedTransform?, matrix)); } /// Pushes a rectangular clip operation onto the operation stack. @@ -112,8 +137,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ClipRectEngineLayer? oldLayer, }) { assert(clipBehavior != null); // ignore: unnecessary_null_comparison - assert(clipBehavior != ui.Clip.none); - return _pushSurface(PersistedClipRect(oldLayer as PersistedClipRect?, rect)) as ui.ClipRectEngineLayer; + return _pushSurface( + PersistedClipRect(oldLayer as PersistedClipRect?, rect, clipBehavior)); } /// Pushes a rounded-rectangular clip operation onto the operation stack. @@ -127,7 +152,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.Clip? clipBehavior, ui.ClipRRectEngineLayer? oldLayer, }) { - return _pushSurface(PersistedClipRRect(oldLayer, rrect, clipBehavior)) as ui.ClipRRectEngineLayer; + return _pushSurface( + PersistedClipRRect(oldLayer, rrect, clipBehavior)); } /// Pushes a path clip operation onto the operation stack. @@ -142,8 +168,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ClipPathEngineLayer? oldLayer, }) { assert(clipBehavior != null); // ignore: unnecessary_null_comparison - assert(clipBehavior != ui.Clip.none); - return _pushSurface(PersistedClipPath(oldLayer as PersistedClipPath?, path, clipBehavior)) as ui.ClipPathEngineLayer; + return _pushSurface( + PersistedClipPath(oldLayer as PersistedClipPath?, path, clipBehavior)); } /// Pushes an opacity operation onto the operation stack. @@ -160,7 +186,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.Offset offset = ui.Offset.zero, ui.OpacityEngineLayer? oldLayer, }) { - return _pushSurface(PersistedOpacity(oldLayer as PersistedOpacity?, alpha, offset)) as ui.OpacityEngineLayer; + return _pushSurface( + PersistedOpacity(oldLayer as PersistedOpacity?, alpha, offset)); } /// Pushes a color filter operation onto the operation stack. @@ -179,7 +206,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ColorFilterEngineLayer? oldLayer, }) { assert(filter != null); // ignore: unnecessary_null_comparison - return _pushSurface(PersistedColorFilter(oldLayer as PersistedColorFilter?, filter)) as ui.ColorFilterEngineLayer; + return _pushSurface( + PersistedColorFilter(oldLayer as PersistedColorFilter?, filter)); } /// Pushes an image filter operation onto the operation stack. @@ -198,21 +226,27 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ImageFilterEngineLayer? oldLayer, }) { assert(filter != null); // ignore: unnecessary_null_comparison - return _pushSurface(PersistedImageFilter(oldLayer as PersistedImageFilter?, filter)) as ui.ImageFilterEngineLayer; + return _pushSurface( + PersistedImageFilter(oldLayer as PersistedImageFilter?, filter)); } /// Pushes a backdrop filter operation onto the operation stack. /// /// The given filter is applied to the current contents of the scene prior to - /// rasterizing the given objects. + /// rasterizing the child layers. + /// + /// The [blendMode] argument is required for [ui.SceneBuilder] compatibility, but is + /// ignored by the DOM renderer. /// /// See [pop] for details about the operation stack. @override ui.BackdropFilterEngineLayer pushBackdropFilter( ui.ImageFilter filter, { + ui.BlendMode blendMode = ui.BlendMode.srcOver, ui.BackdropFilterEngineLayer? oldLayer, }) { - return _pushSurface(PersistedBackdropFilter(oldLayer as PersistedBackdropFilter?, filter as EngineImageFilter)) as ui.BackdropFilterEngineLayer; + return _pushSurface(PersistedBackdropFilter( + oldLayer as PersistedBackdropFilter?, filter as EngineImageFilter)); } /// Pushes a shader mask operation onto the operation stack. @@ -227,8 +261,12 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.Rect maskRect, ui.BlendMode blendMode, { ui.ShaderMaskEngineLayer? oldLayer, + ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { - throw UnimplementedError(); + assert(blendMode != null); // ignore: unnecessary_null_comparison + return _pushSurface(PersistedShaderMask( + oldLayer as PersistedShaderMask?, + shader, maskRect, blendMode, filterQuality)); } /// Pushes a physical layer operation for an arbitrary shape onto the @@ -252,15 +290,14 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.Clip clipBehavior = ui.Clip.none, ui.PhysicalShapeEngineLayer? oldLayer, }) { - assert(color != null, 'color must not be null'); // ignore: unnecessary_null_comparison - return _pushSurface(PersistedPhysicalShape( + return _pushSurface(PersistedPhysicalShape( oldLayer as PersistedPhysicalShape?, path as SurfacePath, elevation, color.value, shadowColor?.value ?? 0xFF000000, clipBehavior, - )) as ui.PhysicalShapeEngineLayer; + )); } /// Add a retained engine layer subtree from previous frames. @@ -273,7 +310,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// no need to call [addToScene] for its children layers. @override void addRetained(ui.EngineLayer retainedLayer) { - final PersistedContainerSurface retainedSurface = retainedLayer as PersistedContainerSurface; + final PersistedContainerSurface retainedSurface = + retainedLayer as PersistedContainerSurface; if (assertionsEnabled) { assert(debugAssertSurfaceState(retainedSurface, PersistedSurfaceState.active, PersistedSurfaceState.released)); @@ -307,13 +345,12 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. /// - /// The "UI thread" is the thread that includes all the execution of - /// the main Dart isolate (the isolate that can call - /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "raster - /// thread" is the thread (running on the CPU) that subsequently - /// processes the [Scene] provided by the Dart code to turn it into - /// GPU commands and send it to the GPU. + /// The "UI thread" is the thread that includes all the execution of the main + /// Dart isolate (the isolate that can call [FlutterView.render]). The UI + /// thread frame time is the total time spent executing the + /// [FlutterView.onBeginFrame] callback. The "raster thread" is the thread + /// (running on the CPU) that subsequently processes the [Scene] provided by + /// the Dart code to turn it into GPU commands and send it to the GPU. /// /// See also the [PerformanceOverlayOption] enum in the rendering library. /// for more details. @@ -338,8 +375,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ) { if (!_webOnlyDidWarnAboutPerformanceOverlay) { _webOnlyDidWarnAboutPerformanceOverlay = true; - html.window.console - .warn('The performance overlay isn\'t supported on the web'); + printWarning('The performance overlay isn\'t supported on the web'); } } @@ -360,7 +396,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { if (willChangeHint) { hints |= 2; } - _addSurface(PersistedPicture(offset.dx, offset.dy, picture as EnginePicture, hints)); + _addSurface(PersistedPicture( + offset.dx, offset.dy, picture as EnginePicture, hints)); } /// Adds a backend texture to the scene. @@ -376,12 +413,11 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { bool freeze = false, ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { - assert(offset != null, 'Offset argument was null'); // ignore: unnecessary_null_comparison - _addTexture(offset.dx, offset.dy, width, height, textureId, filterQuality.index); + _addTexture(offset.dx, offset.dy, width, height, textureId, filterQuality); } - void _addTexture( - double dx, double dy, double width, double height, int textureId, int filterQuality) { + void _addTexture(double dx, double dy, double width, double height, + int textureId, ui.FilterQuality filterQuality) { // In test mode, allow this to be a no-op. if (!ui.debugEmulateFlutterTesterEnvironment) { throw UnimplementedError('Textures are not supported in Flutter Web'); @@ -411,7 +447,6 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { double width = 0.0, double height = 0.0, }) { - assert(offset != null, 'Offset argument was null'); // ignore: unnecessary_null_comparison _addPlatformView(offset.dx, offset.dy, width, height, viewId); } @@ -425,30 +460,6 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { _addSurface(PersistedPlatformView(viewId, dx, dy, width, height)); } - /// (Fuchsia-only) Adds a scene rendered by another application to the scene - /// for this application. - @override - void addChildScene({ - ui.Offset offset = ui.Offset.zero, - double width = 0.0, - double height = 0.0, - ui.SceneHost? sceneHost, - bool hitTestable = true, - }) { - _addChildScene(offset.dx, offset.dy, width, height, sceneHost, hitTestable); - } - - void _addChildScene( - double dx, - double dy, - double width, - double height, - ui.SceneHost? sceneHost, - bool hitTestable, - ) { - throw UnimplementedError(); - } - /// Sets a threshold after which additional debugging information should be /// recorded. /// @@ -514,26 +525,35 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { static void debugForgetFrameScene() { _lastFrameScene?.rootElement?.remove(); _lastFrameScene = null; - _clipIdCounter = 0; - _recycledCanvases.clear(); + resetSvgClipIds(); + recycledCanvases.clear(); } /// Finishes building the scene. /// /// Returns a [Scene] containing the objects that have been added to /// this scene builder. The [Scene] can then be displayed on the - /// screen with [Window.render]. + /// screen with [FlutterView.render]. /// /// After calling this function, the scene builder object is invalid and /// cannot be used further. @override SurfaceScene build() { + // "Build finish" and "raster start" happen back-to-back because we + // render on the same thread, so there's no overhead from hopping to + // another thread. + // + // In the HTML renderer we time the beginning of the rasterization phase + // (counter-intuitively) in SceneBuilder.build because DOM updates happen + // here. This is different from CanvasKit. + frameTimingsOnBuildFinish(); + frameTimingsOnRasterStart(); timeAction(kProfilePrerollFrame, () { while (_surfaceStack.length > 1) { // Auto-pop layers that were pushed without a corresponding pop. pop(); } - _persistedScene.preroll(); + _persistedScene.preroll(PrerollSurfaceContext()); }); return timeAction(kProfileApplyFrame, () { if (_lastFrameScene == null) { @@ -562,10 +582,3 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { throw UnimplementedError(); } } - -// HTML only supports a single radius, but Flutter ImageFilter supports separate -// horizontal and vertical radii. The best approximation we can provide is to -// average the two radii together for a single compromise value. -String _imageFilterToCss(EngineImageFilter filter) { - return 'blur(${(filter.sigmaX + filter.sigmaY) / 2}px)'; -} diff --git a/lib/web_ui/lib/src/engine/html/shader.dart b/lib/web_ui/lib/src/engine/html/shader.dart deleted file mode 100644 index 4dae88f674bf9..0000000000000 --- a/lib/web_ui/lib/src/engine/html/shader.dart +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.10 -part of engine; - -abstract class EngineGradient implements ui.Gradient { - /// Hidden constructor to prevent subclassing. - EngineGradient._(); - - /// Creates a fill style to be used in painting. - Object createPaintStyle(html.CanvasRenderingContext2D? ctx); -} - -class GradientSweep extends EngineGradient { - GradientSweep(this.center, this.colors, this.colorStops, this.tileMode, - this.startAngle, this.endAngle, this.matrix4) - : assert(_offsetIsValid(center)), - assert(colors != null), // ignore: unnecessary_null_comparison - assert(tileMode != null), // ignore: unnecessary_null_comparison - assert(startAngle != null), // ignore: unnecessary_null_comparison - assert(endAngle != null), // ignore: unnecessary_null_comparison - assert(startAngle < endAngle), - assert(matrix4 == null || _matrix4IsValid(matrix4)), - super._() { - _validateColorStops(colors, colorStops); - } - - @override - Object createPaintStyle(html.CanvasRenderingContext2D? ctx) { - throw UnimplementedError(); - } - - final ui.Offset center; - final List colors; - final List? colorStops; - final ui.TileMode tileMode; - final double startAngle; - final double endAngle; - final Float32List? matrix4; -} - -class GradientLinear extends EngineGradient { - GradientLinear( - this.from, - this.to, - this.colors, - this.colorStops, - this.tileMode, - Float64List? matrix, - ) : assert(_offsetIsValid(from)), - assert(_offsetIsValid(to)), - assert(colors != null), // ignore: unnecessary_null_comparison - assert(tileMode != null), // ignore: unnecessary_null_comparison - this.matrix4 = matrix == null ? null : _FastMatrix64(matrix), - super._() { - if (assertionsEnabled) { - _validateColorStops(colors, colorStops); - } - } - - final ui.Offset from; - final ui.Offset to; - final List colors; - final List? colorStops; - final ui.TileMode tileMode; - final _FastMatrix64? matrix4; - - @override - html.CanvasGradient createPaintStyle(html.CanvasRenderingContext2D? ctx) { - _FastMatrix64? matrix4 = this.matrix4; - html.CanvasGradient gradient; - if (matrix4 != null) { - final centerX = (from.dx + to.dx) / 2.0; - final centerY = (from.dy + to.dy) / 2.0; - matrix4.transform(from.dx - centerX, from.dy - centerY); - final double fromX = matrix4.transformedX + centerX; - final double fromY = matrix4.transformedY + centerY; - matrix4.transform(to.dx - centerX, to.dy - centerY); - gradient = ctx!.createLinearGradient(fromX, fromY, - matrix4.transformedX + centerX, matrix4.transformedY + centerY); - } else { - gradient = ctx!.createLinearGradient(from.dx, from.dy, to.dx, to.dy); - } - - final List? colorStops = this.colorStops; - if (colorStops == null) { - assert(colors.length == 2); - gradient.addColorStop(0, colorToCssString(colors[0])!); - gradient.addColorStop(1, colorToCssString(colors[1])!); - return gradient; - } - for (int i = 0; i < colors.length; i++) { - gradient.addColorStop(colorStops[i], colorToCssString(colors[i])!); - } - return gradient; - } -} - -// TODO(flutter_web): For transforms and tile modes implement as webgl -// For now only GradientRotation is supported in flutter which is implemented -// for linear gradient. -// See https://github.com/flutter/flutter/issues/32819 -class GradientRadial extends EngineGradient { - GradientRadial(this.center, this.radius, this.colors, this.colorStops, - this.tileMode, this.matrix4) - : super._(); - - final ui.Offset center; - final double radius; - final List colors; - final List? colorStops; - final ui.TileMode tileMode; - final Float32List? matrix4; - - @override - Object createPaintStyle(html.CanvasRenderingContext2D? ctx) { - if (!experimentalUseSkia) { - if (tileMode != ui.TileMode.clamp) { - throw UnimplementedError( - 'TileMode not supported in GradientRadial shader'); - } - } - final html.CanvasGradient gradient = ctx!.createRadialGradient( - center.dx, center.dy, 0, center.dx, center.dy, radius); - final List? colorStops = this.colorStops; - if (colorStops == null) { - assert(colors.length == 2); - gradient.addColorStop(0, colorToCssString(colors[0])!); - gradient.addColorStop(1, colorToCssString(colors[1])!); - return gradient; - } else { - for (int i = 0; i < colors.length; i++) { - gradient.addColorStop(colorStops[i], colorToCssString(colors[i])!); - } - } - return gradient; - } -} - -class GradientConical extends EngineGradient { - GradientConical(this.focal, this.focalRadius, this.center, this.radius, - this.colors, this.colorStops, this.tileMode, this.matrix4) - : super._(); - - final ui.Offset focal; - final double focalRadius; - final ui.Offset center; - final double radius; - final List colors; - final List? colorStops; - final ui.TileMode tileMode; - final Float32List? matrix4; - - @override - Object createPaintStyle(html.CanvasRenderingContext2D? ctx) { - throw UnimplementedError(); - } -} - -/// Backend implementation of [ui.ImageFilter]. -/// -/// Currently only `blur` is supported. -class EngineImageFilter implements ui.ImageFilter { - EngineImageFilter.blur({this.sigmaX = 0.0, this.sigmaY = 0.0}); - - final double sigmaX; - final double sigmaY; - - @override - bool operator ==(Object other) { - return other is EngineImageFilter - && other.sigmaX == sigmaX - && other.sigmaY == sigmaY; - } - - @override - int get hashCode => ui.hashValues(sigmaX, sigmaY); - - @override - String toString() { - return 'ImageFilter.blur($sigmaX, $sigmaY)'; - } -} diff --git a/lib/web_ui/lib/src/engine/html/shader_mask.dart b/lib/web_ui/lib/src/engine/html/shader_mask.dart new file mode 100644 index 0000000000000..515bbdbe6a24b --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shader_mask.dart @@ -0,0 +1,362 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show NullTreeSanitizer; +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import 'bitmap_canvas.dart'; +import 'path_to_svg_clip.dart'; +import 'shaders/shader.dart'; +import 'surface.dart'; + +/// A surface that applies a shader to its children. +/// +/// Currently there are 2 types of shaders: +/// - Gradients +/// - ImageShader +/// +/// Gradients +/// The gradients can be applied to the child tree by rendering the gradient +/// into an image and referencing the image in an svg filter to apply +/// to DOM tree. +/// +class PersistedShaderMask extends PersistedContainerSurface + implements ui.ShaderMaskEngineLayer { + PersistedShaderMask( + PersistedShaderMask? oldLayer, + this.shader, + this.maskRect, + this.blendMode, + this.filterQuality, + ) : super(oldLayer); + + html.Element? _childContainer; + final ui.Shader shader; + final ui.Rect maskRect; + final ui.BlendMode blendMode; + final ui.FilterQuality filterQuality; + html.Element? _shaderElement; + final bool isWebKit = browserEngine == BrowserEngine.webkit; + + @override + void adoptElements(PersistedShaderMask oldSurface) { + super.adoptElements(oldSurface); + _childContainer = oldSurface._childContainer; + _shaderElement = oldSurface._shaderElement; + oldSurface._childContainer = null; + oldSurface._shaderElement = null; + } + + @override + html.Element? get childContainer => _childContainer; + + @override + void discard() { + super.discard(); + domRenderer.removeResource(_shaderElement); + // Do not detach the child container from the root. It is permanently + // attached. The elements are reused together and are detached from the DOM + // together. + _childContainer = null; + } + + @override + void preroll(PrerollSurfaceContext prerollContext) { + ++prerollContext.activeShaderMaskCount; + super.preroll(prerollContext); + --prerollContext.activeShaderMaskCount; + } + + @override + html.Element createElement() { + final html.Element element = defaultCreateElement('flt-shader-mask'); + final html.Element container = html.Element.tag('flt-mask-interior'); + container.style.position = 'absolute'; + _childContainer = container; + element.append(_childContainer!); + return element; + } + + @override + void apply() { + domRenderer.removeResource(_shaderElement); + _shaderElement = null; + if (shader is ui.Gradient) { + rootElement!.style + ..left = '${maskRect.left}px' + ..top = '${maskRect.top}px' + ..width = '${maskRect.width}px' + ..height = '${maskRect.height}px'; + _childContainer!.style + ..left = '${-maskRect.left}px' + ..top = '${-maskRect.top}px'; + // Prevent ShaderMask from failing inside animations that size + // area to empty. + if (maskRect.width > 0 && maskRect.height > 0) { + _applyGradientShader(); + } + return; + } + // TODO(ferhat): Implement _applyImageShader(); + throw Exception('Shader type not supported for ShaderMask'); + } + + void _applyGradientShader() { + if (shader is EngineGradient) { + final EngineGradient gradientShader = shader as EngineGradient; + final String imageUrl = + gradientShader.createImageBitmap(maskRect, 1, true) as String; + ui.BlendMode blendModeTemp = blendMode; + switch (blendModeTemp) { + case ui.BlendMode.clear: + case ui.BlendMode.dstOut: + case ui.BlendMode.srcOut: + childContainer?.style.visibility = 'hidden'; + return; + case ui.BlendMode.dst: + case ui.BlendMode.dstIn: + // Noop. Should render existing destination. + rootElement!.style.filter = ''; + return; + case ui.BlendMode.srcOver: + // Uses source filter color. + // Since we don't have a size, we can't use background color. + // Use svg filter srcIn instead. + blendModeTemp = ui.BlendMode.srcIn; + break; + case ui.BlendMode.src: + case ui.BlendMode.dstOver: + case ui.BlendMode.srcIn: + case ui.BlendMode.srcATop: + case ui.BlendMode.dstATop: + case ui.BlendMode.xor: + case ui.BlendMode.plus: + case ui.BlendMode.modulate: + case ui.BlendMode.screen: + case ui.BlendMode.overlay: + case ui.BlendMode.darken: + case ui.BlendMode.lighten: + case ui.BlendMode.colorDodge: + case ui.BlendMode.colorBurn: + case ui.BlendMode.hardLight: + case ui.BlendMode.softLight: + case ui.BlendMode.difference: + case ui.BlendMode.exclusion: + case ui.BlendMode.multiply: + case ui.BlendMode.hue: + case ui.BlendMode.saturation: + case ui.BlendMode.color: + case ui.BlendMode.luminosity: + break; + } + + final String code = svgMaskFilterFromImageAndBlendMode( + imageUrl, blendModeTemp, maskRect.width, maskRect.height)!; + + _shaderElement = + html.Element.html(code, treeSanitizer: NullTreeSanitizer()); + if (isWebKit) { + _childContainer!.style.filter = 'url(#_fmf$_maskFilterIdCounter'; + } else { + rootElement!.style.filter = 'url(#_fmf$_maskFilterIdCounter'; + } + domRenderer.addResource(_shaderElement!); + } + } + + @override + void update(PersistedShaderMask oldSurface) { + super.update(oldSurface); + if (shader != oldSurface.shader || + maskRect != oldSurface.maskRect || + blendMode != oldSurface.blendMode) { + apply(); + } + } +} + +String? svgMaskFilterFromImageAndBlendMode( + String imageUrl, ui.BlendMode blendMode, double width, double height) { + String? svgFilter; + switch (blendMode) { + case ui.BlendMode.src: + svgFilter = _srcImageToSvg(imageUrl, width, height); + break; + case ui.BlendMode.srcIn: + case ui.BlendMode.srcATop: + svgFilter = _srcInImageToSvg(imageUrl, width, height); + break; + case ui.BlendMode.srcOut: + svgFilter = _srcOutImageToSvg(imageUrl, width, height); + break; + case ui.BlendMode.xor: + svgFilter = _xorImageToSvg(imageUrl, width, height); + break; + case ui.BlendMode.plus: + // Porter duff source + destination. + svgFilter = _compositeImageToSvg(imageUrl, 0, 1, 1, 0, width, height); + break; + case ui.BlendMode.modulate: + // Porter duff source * destination but preserves alpha. + svgFilter = _modulateImageToSvg(imageUrl, width, height); + break; + case ui.BlendMode.overlay: + // Since overlay is the same as hard-light by swapping layers, + // pass hard-light blend function. + svgFilter = _blendImageToSvg(imageUrl, 'hard-light', width, height, + swapLayers: true); + break; + // Several of the filters below (although supported) do not render the + // same (close but not exact) as native flutter when used as blend mode + // for a background-image with a background color. They only look + // identical when feBlend is used within an svg filter definition. + // + // Saturation filter uses destination when source is transparent. + // cMax = math.max(r, math.max(b, g)); + // cMin = math.min(r, math.min(b, g)); + // delta = cMax - cMin; + // lightness = (cMax + cMin) / 2.0; + // saturation = delta / (1.0 - (2 * lightness - 1.0).abs()); + case ui.BlendMode.saturation: + case ui.BlendMode.colorDodge: + case ui.BlendMode.colorBurn: + case ui.BlendMode.hue: + case ui.BlendMode.color: + case ui.BlendMode.luminosity: + case ui.BlendMode.multiply: + case ui.BlendMode.screen: + case ui.BlendMode.darken: + case ui.BlendMode.lighten: + case ui.BlendMode.hardLight: + case ui.BlendMode.softLight: + case ui.BlendMode.difference: + case ui.BlendMode.exclusion: + svgFilter = _blendImageToSvg( + imageUrl, stringForBlendMode(blendMode), width, height); + break; + case ui.BlendMode.dst: + case ui.BlendMode.dstATop: + case ui.BlendMode.dstIn: + case ui.BlendMode.dstOut: + case ui.BlendMode.dstOver: + case ui.BlendMode.clear: + case ui.BlendMode.srcOver: + throw UnsupportedError( + 'Invalid svg filter request for blend-mode $blendMode'); + } + return svgFilter; +} + +int _maskFilterIdCounter = 0; + +String _svgFilterWrapper(String content) { + _maskFilterIdCounter++; + return '$kSvgResourceHeader' + + content + + ''; +} + +/// SVG filters that contain feImage, will fail on several browsers +/// (Firefox etc) if bounds are not specified. On Firefox percentage +/// width/height 100% works however fails in Chrome 88. Webkit will +/// not render if x/y/width/height is specified. So we return explicit size +/// here unless running on webkit. +String _buildFeBlend(double width, double height) => + browserEngine == BrowserEngine.webkit + ? '' + : 'x="0" y="0" width="${width}px" height="${height}px"'; + +// The color matrix for feColorMatrix element changes colors based on +// the following: +// +// | R' | | r1 r2 r3 r4 r5 | | R | +// | G' | | g1 g2 g3 g4 g5 | | G | +// | B' | = | b1 b2 b3 b4 b5 | * | B | +// | A' | | a1 a2 a3 a4 a5 | | A | +// | 1 | | 0 0 0 0 1 | | 1 | +// +// R' = r1*R + r2*G + r3*B + r4*A + r5 +// G' = g1*R + g2*G + g3*B + g4*A + g5 +// B' = b1*R + b2*G + b3*B + b4*A + b5 +// A' = a1*R + a2*G + a3*B + a4*A + a5 +String _srcInImageToSvg(String imageUrl, double width, double height) { + return _svgFilterWrapper( + '' // Just take alpha channel of destination + '' + '' + '' + '', + ); +} + +String _srcImageToSvg(String imageUrl, double width, double height) { + return _svgFilterWrapper('' + ''); +} + +String _srcOutImageToSvg(String imageUrl, double width, double height) { + return _svgFilterWrapper('' + '' + '' + ''); +} + +String _xorImageToSvg(String imageUrl, double width, double height) { + return _svgFilterWrapper('' + '' + '' + ''); +} + +// The source image and color are composited using : +// result = k1 *in*in2 + k2*in + k3*in2 + k4. +String _compositeImageToSvg(String imageUrl, double k1, double k2, double k3, + double k4, double width, double height) { + return _svgFilterWrapper( + '' + '' + '' + '', + ); +} + +// Porter duff source * destination , keep source alpha. +// First apply color filter to source to change it to [color], then +// composite using multiplication. +String _modulateImageToSvg(String imageUrl, double width, double height) { + return _svgFilterWrapper( + '' + '' + '' + '', + ); +} + +// Uses feBlend element to blend source image with a color. +String _blendImageToSvg( + String imageUrl, String? feBlend, double width, double height, + {bool swapLayers = false}) { + return _svgFilterWrapper( + '' + '' + + (swapLayers + ? '' + : ''), + ); +} diff --git a/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart b/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart new file mode 100644 index 0000000000000..483e8fdc362e3 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart @@ -0,0 +1,268 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../browser_detection.dart'; +import '../../html_image_codec.dart'; +import '../../vector_math.dart'; +import '../offscreen_canvas.dart'; +import '../render_vertices.dart'; +import 'vertex_shaders.dart'; +import 'webgl_context.dart'; + +class EngineImageShader implements ui.ImageShader { + EngineImageShader(ui.Image image, this.tileModeX, this.tileModeY, + Float64List matrix4, this.filterQuality) + : this.image = image as HtmlImage, // ignore: unnecessary_this + this.matrix4 = Float32List.fromList(matrix4); // ignore: unnecessary_this + + final ui.TileMode tileModeX; + final ui.TileMode tileModeY; + final Float32List matrix4; + final ui.FilterQuality? filterQuality; + final HtmlImage image; + + /// Whether fill pattern requires transform to shift tiling offset. + bool requiresTileOffset = false; + + Object createPaintStyle(html.CanvasRenderingContext2D context, + ui.Rect? shaderBounds, double density) { + /// Creates a canvas rendering context pattern based on image and tile modes. + final ui.TileMode tileX = tileModeX; + final ui.TileMode tileY = tileModeY; + if (tileX != ui.TileMode.clamp && tileY != ui.TileMode.clamp) { + return context.createPattern( + _resolveTiledImageSource(image, tileX, tileY)!, + _tileModeToHtmlRepeatAttribute(tileX, tileY))!; + } else { + initWebGl(); + return _createGlShader(context, shaderBounds!, density); + } + } + + /// Converts tilemode to CSS repeat attribute. + /// + /// CSS and Canvas2D createPattern apis only support repeated tiles. + /// For mirroring we create a new image with mirror builtin so both + /// repeated and mirrored modes can be supported when applied to + /// html element background-image or used by createPattern api. + String _tileModeToHtmlRepeatAttribute( + ui.TileMode tileModeX, ui.TileMode tileModeY) { + final bool repeatX = + tileModeX == ui.TileMode.repeated || tileModeX == ui.TileMode.mirror; + final bool repeatY = + tileModeY == ui.TileMode.repeated || tileModeY == ui.TileMode.mirror; + return repeatX + ? (repeatY ? 'repeat' : 'repeat-x') + : (repeatY ? 'repeat-y' : 'no-repeat'); + } + + /// Tiles the image and returns an image or canvas element to be used as + /// source for a repeated pattern. + /// + /// Other alternative was to create a webgl shader for the area and + /// tile in the shader, but that will generate a much larger image footprint + /// when the pattern is small. So we opt here for mirroring by + /// redrawing the image 2 or 4 times into a new bitmap. + Object? _resolveTiledImageSource( + HtmlImage image, ui.TileMode tileX, ui.TileMode tileY) { + final int mirrorX = tileX == ui.TileMode.mirror ? 2 : 1; + final int mirrorY = tileY == ui.TileMode.mirror ? 2 : 1; + + /// If we have no mirror, we can use image directly as pattern. + if (mirrorX == 1 && mirrorY == 1) { + return image.imgElement; + } + + /// Create a new image by mirroring. + final int imageWidth = image.width; + final int imageHeight = image.height; + final int newWidth = imageWidth * mirrorX; + final int newHeight = imageHeight * mirrorY; + final OffScreenCanvas offscreenCanvas = OffScreenCanvas(newWidth, newHeight); + final Object renderContext = offscreenCanvas.getContext2d()!; + for (int y = 0; y < mirrorY; y++) { + for (int x = 0; x < mirrorX; x++) { + final int flipX = x != 0 ? -1 : 1; + final int flipY = y != 0 ? -1 : 1; + + /// To draw image flipped we set translate and scale and pass + /// negative width/height to drawImage. + if (flipX != 1 || flipY != 1) { + js_util.callMethod(renderContext, 'scale', [flipX, flipY]); + } + js_util.callMethod(renderContext, 'drawImage', [ + image.imgElement, + if (x == 0) 0 else -2 * imageWidth, + if (y == 0) 0 else -2 * imageHeight, + ]); + if (flipX != 1 || flipY != 1) { + /// Restore transform. This is faster than save/restore on context. + js_util.callMethod(renderContext, 'scale', [flipX, flipY]); + } + } + } + // When using OffscreenCanvas and transferToImageBitmap is supported by + // browser create ImageBitmap otherwise use more expensive canvas + // allocation. + if (OffScreenCanvas.supported && + offscreenCanvas.transferToImageBitmapSupported) { + return offscreenCanvas.transferToImageBitmap(); + } else { + final html.CanvasElement canvas = + html.CanvasElement(width: newWidth, height: newHeight); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + offscreenCanvas.transferImage(ctx); + return canvas; + } + } + + /// Creates an image with tiled/transformed images. + html.CanvasPattern _createGlShader(html.CanvasRenderingContext2D? context, + ui.Rect shaderBounds, double density) { + final Matrix4 transform = Matrix4.fromFloat32List(matrix4); + final double dpr = ui.window.devicePixelRatio; + + final int widthInPixels = (shaderBounds.width * dpr).ceil(); + final int heightInPixels = (shaderBounds.height * dpr).ceil(); + + assert(widthInPixels > 0 && heightInPixels > 0); + + /// Render tiles into a bitmap and create a canvas pattern. + final bool isWebGl2 = webGLVersion == WebGLVersion.webgl2; + + final String vertexShader = VertexShaders.writeTextureVertexShader(); + final String fragmentShader = FragmentShaders.writeTextureFragmentShader( + isWebGl2, tileModeX, tileModeY); + + /// Render gradient into a bitmap and create a canvas pattern. + final OffScreenCanvas offScreenCanvas = + OffScreenCanvas(widthInPixels, heightInPixels); + final GlContext gl = GlContext(offScreenCanvas); + gl.setViewportSize(widthInPixels, heightInPixels); + + final GlProgram glProgram = gl.cacheProgram(vertexShader, fragmentShader); + gl.useProgram(glProgram); + + const int vertexCount = 6; + final Float32List vertices = Float32List(vertexCount * 2); + final ui.Rect vRect = shaderBounds.translate(-shaderBounds.left, -shaderBounds.top); + vertices[0] = vRect.left; + vertices[1] = vRect.top; + vertices[2] = vRect.right; + vertices[3] = vRect.top; + vertices[4] = vRect.right; + vertices[5] = vRect.bottom; + vertices[6] = vRect.right; + vertices[7] = vRect.bottom; + vertices[8] = vRect.left; + vertices[9] = vRect.bottom; + vertices[10] = vRect.left; + vertices[11] = vRect.top; + + final Object positionAttributeLocation = + gl.getAttributeLocation(glProgram.program, 'position'); + + setupVertexTransforms(gl, glProgram, 0, 0, + widthInPixels.toDouble(), heightInPixels.toDouble(), transform); + + requiresTileOffset = shaderBounds.left !=0 || shaderBounds.top != 0; + + /// To map from vertex position to texture coordinate in 0..1 range, + /// we setup scalar to be used in vertex shader. + setupTextureTransform( + gl, + glProgram, + shaderBounds.left, + shaderBounds.top, + 1.0 / image.width.toDouble(), + 1.0 / image.height.toDouble()); + + /// Setup geometry. + /// + /// Create buffer for vertex coordinates. + final Object positionsBuffer = gl.createBuffer()!; + assert(positionsBuffer != null); // ignore: unnecessary_null_comparison + + Object? vao; + if (isWebGl2) { + /// Create a vertex array object. + vao = gl.createVertexArray(); + /// Set vertex array object as active one. + gl.bindVertexArray(vao!); + } + + /// Turn on position attribute. + gl.enableVertexAttribArray(positionAttributeLocation); + /// Bind buffer as position buffer and transfer data. + gl.bindArrayBuffer(positionsBuffer); + bufferVertexData(gl, vertices, ui.window.devicePixelRatio); + + /// Setup data format for attribute. + js_util.callMethod(gl.glContext, 'vertexAttribPointer', [ + positionAttributeLocation, + 2, + gl.kFloat, + false, + 0, + 0, + ]); + + /// Copy image to the texture. + final Object? texture = gl.createTexture(); + /// Texture units are a global array of references to the textures. + /// By setting activeTexture, we associate the bound texture to a unit. + /// Every time we call a texture function such as texImage2D with a target + /// like TEXTURE_2D, it looks up texture by using the currently active + /// unit. + /// In our case we have a single texture unit 0. + gl.activeTexture(gl.kTexture0); + gl.bindTexture(gl.kTexture2D, texture); + + gl.texImage2D(gl.kTexture2D, 0, gl.kRGBA, gl.kRGBA, gl.kUnsignedByte, + image.imgElement); + + if (isWebGl2) { + /// Texture REPEAT and MIRROR is only supported in WebGL 2, for + /// WebGL 1.0 we let shader compute correct uv coordinates. + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapS, + tileModeToGlWrapping(gl, tileModeX)); + + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapT, + tileModeToGlWrapping(gl, tileModeY)); + + /// Mipmapping saves your texture in different resolutions + /// so the graphics card can choose which resolution is optimal + /// without artifacts. + gl.generateMipmap(gl.kTexture2D); + } else { + /// For webgl1, if a texture is not mipmap complete, then the return + /// value of a texel fetch is (0, 0, 0, 1), so we have to set + /// minifying function to filter. + /// See https://www.khronos.org/registry/webgl/specs/1.0.0/#5.13.8. + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapS, gl.kClampToEdge); + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapT, gl.kClampToEdge); + gl.texParameteri(gl.kTexture2D, gl.kTextureMinFilter, gl.kLinear); + } + + /// Finally render triangles. + gl.clear(); + + gl.drawTriangles(vertexCount, ui.VertexMode.triangles); + + if (vao != null) { + gl.unbindVertexArray(); + } + + final Object? bitmapImage = gl.readPatternData(); + gl.bindArrayBuffer(null); + gl.bindElementArrayBuffer(null); + return context!.createPattern(bitmapImage!, 'no-repeat')!; + } +} diff --git a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart new file mode 100644 index 0000000000000..a7f5b16923eaf --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart @@ -0,0 +1,176 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'shader_builder.dart'; +import 'webgl_context.dart'; + +/// Converts colors and stops to typed array of bias, scale and threshold to use +/// in shaders. +/// +/// A color is generated by taking a t value [0..1] and computing +/// t * scale + bias. +/// +/// Example: For stops 0.0 t1, t2, 1.0 and colors c0, c1, c2, c3 +/// Given t1 colors, {List? stops}) { + // If colorStops is not provided, then only two stops, at 0.0 and 1.0, + // are implied (and colors must therefore only have two entries). + assert(stops != null || colors.length == 2); + stops ??= const [0.0, 1.0]; + final int colorCount = colors.length; + int normalizedCount = colorCount; + final bool addFirst = stops[0] != 0.0; + final bool addLast = stops.last != 1.0; + if (addFirst) { + normalizedCount++; + } + if (addLast) { + normalizedCount++; + } + final Float32List bias = Float32List(normalizedCount * 4); + final Float32List scale = Float32List(normalizedCount * 4); + final Float32List thresholds = + Float32List(4 * ((normalizedCount - 1) ~/ 4 + 1)); + int targetIndex = 0; + int thresholdIndex = 0; + if (addFirst) { + final ui.Color c = colors[0]; + bias[targetIndex++] = c.red / 255.0; + bias[targetIndex++] = c.green / 255.0; + bias[targetIndex++] = c.blue / 255.0; + bias[targetIndex++] = c.alpha / 255.0; + thresholds[thresholdIndex++] = 0.0; + } + for (final ui.Color c in colors) { + bias[targetIndex++] = c.red / 255.0; + bias[targetIndex++] = c.green / 255.0; + bias[targetIndex++] = c.blue / 255.0; + bias[targetIndex++] = c.alpha / 255.0; + } + for (final double stop in stops) { + thresholds[thresholdIndex++] = stop; + } + if (addLast) { + final ui.Color c = colors.last; + bias[targetIndex++] = c.red / 255.0; + bias[targetIndex++] = c.green / 255.0; + bias[targetIndex++] = c.blue / 255.0; + bias[targetIndex++] = c.alpha / 255.0; + thresholds[thresholdIndex++] = 1.0; + } + // Now that we have bias for each color stop, we can compute scale based + // on delta between colors. + final int lastColorIndex = 4 * (normalizedCount - 1); + for (int i = 0; i < lastColorIndex; i++) { + final int thresholdIndex = i >> 2; + scale[i] = (bias[i + 4] - bias[i]) / + (thresholds[thresholdIndex + 1] - thresholds[thresholdIndex]); + } + scale[lastColorIndex] = 0.0; + scale[lastColorIndex + 1] = 0.0; + scale[lastColorIndex + 2] = 0.0; + scale[lastColorIndex + 3] = 0.0; + // Compute bias = colorAtStop - stopValue * (scale). + for (int i = 0; i < normalizedCount; i++) { + final double t = thresholds[i]; + final int colorIndex = i * 4; + bias[colorIndex] -= t * scale[colorIndex]; + bias[colorIndex + 1] -= t * scale[colorIndex + 1]; + bias[colorIndex + 2] -= t * scale[colorIndex + 2]; + bias[colorIndex + 3] -= t * scale[colorIndex + 3]; + } + return NormalizedGradient._(normalizedCount, thresholds, scale, bias); + } + + NormalizedGradient._( + this.thresholdCount, this._thresholds, this._scale, this._bias); + + /// Sets uniforms for threshold, bias and scale for program. + void setupUniforms(GlContext gl, GlProgram glProgram) { + for (int i = 0; i < thresholdCount; i++) { + final Object biasId = gl.getUniformLocation(glProgram.program, 'bias_$i'); + gl.setUniform4f(biasId, _bias[i * 4], _bias[i * 4 + 1], _bias[i * 4 + 2], + _bias[i * 4 + 3]); + final Object scaleId = gl.getUniformLocation(glProgram.program, 'scale_$i'); + gl.setUniform4f(scaleId, _scale[i * 4], _scale[i * 4 + 1], + _scale[i * 4 + 2], _scale[i * 4 + 3]); + } + for (int i = 0; i < _thresholds.length; i += 4) { + final Object thresId = + gl.getUniformLocation(glProgram.program, 'threshold_${i ~/ 4}'); + gl.setUniform4f(thresId, _thresholds[i], _thresholds[i + 1], + _thresholds[i + 2], _thresholds[i + 3]); + } + } + + /// Returns bias component at index. + double biasAt(int index) => _bias[index]; + + /// Returns scale component at index. + double scaleAt(int index) => _scale[index]; + + /// Returns threshold at index. + double thresholdAt(int index) => _thresholds[index]; +} + +/// Writes fragment shader code to search for probe value in source data and set +/// bias and scale to be used for computation. +/// +/// Source data for thresholds is provided using ceil(count/4) packed vec4 +/// uniforms. +/// +/// Bias and scale data are vec4 uniforms that hold color data. +void writeUnrolledBinarySearch(ShaderMethod method, int start, int end, + {required String probe, + required String sourcePrefix, + required String biasName, + required String scaleName}) { + if (start == end) { + final String biasSource = '${biasName}_$start'; + method.addStatement('$biasName = $biasSource;'); + final String scaleSource = '${scaleName}_$start'; + method.addStatement('$scaleName = $scaleSource;'); + } else { + // Add probe check. + final int mid = (start + end) ~/ 2; + String thresholdAtMid = '${sourcePrefix}_${(mid + 1) ~/ 4}'; + thresholdAtMid += '.${vectorComponentIndexToName((mid + 1) % 4)}'; + method.addStatement('if ($probe < $thresholdAtMid) {'); + method.indent(); + writeUnrolledBinarySearch(method, start, mid, + probe: probe, + sourcePrefix: sourcePrefix, + biasName: biasName, + scaleName: scaleName); + method.unindent(); + method.addStatement('} else {'); + method.indent(); + writeUnrolledBinarySearch(method, mid + 1, end, + probe: probe, + sourcePrefix: sourcePrefix, + biasName: biasName, + scaleName: scaleName); + method.unindent(); + method.addStatement('}'); + } +} + +String vectorComponentIndexToName(int index) { + assert(index >= 0 && index <= 4); + return 'xyzw'[index]; +} diff --git a/lib/web_ui/lib/src/engine/html/shaders/shader.dart b/lib/web_ui/lib/src/engine/html/shaders/shader.dart new file mode 100644 index 0000000000000..a8d0948641818 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart @@ -0,0 +1,751 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../browser_detection.dart'; +import '../../util.dart'; +import '../../validators.dart'; +import '../../vector_math.dart'; +import '../offscreen_canvas.dart'; +import '../path/path_utils.dart'; +import '../render_vertices.dart'; +import 'normalized_gradient.dart'; +import 'shader_builder.dart'; +import 'vertex_shaders.dart'; +import 'webgl_context.dart'; + +const double kFltEpsilon = 1.19209290E-07; // == 1 / (2 ^ 23) +const double kFltEpsilonSquared = 1.19209290E-07 * 1.19209290E-07; + +abstract class EngineGradient implements ui.Gradient { + /// Hidden constructor to prevent subclassing. + EngineGradient._(); + + /// Creates a fill style to be used in painting. + Object createPaintStyle(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density); + + /// Creates a CanvasImageSource to paint gradient. + Object createImageBitmap( + ui.Rect? shaderBounds, double density, bool createDataUrl); +} + +class GradientSweep extends EngineGradient { + GradientSweep(this.center, this.colors, this.colorStops, this.tileMode, + this.startAngle, this.endAngle, this.matrix4) + : assert(offsetIsValid(center)), + assert(colors != null), // ignore: unnecessary_null_comparison + assert(tileMode != null), // ignore: unnecessary_null_comparison + assert(startAngle != null), // ignore: unnecessary_null_comparison + assert(endAngle != null), // ignore: unnecessary_null_comparison + assert(startAngle < endAngle), + super._() { + validateColorStops(colors, colorStops); + } + + @override + Object createImageBitmap( + ui.Rect? shaderBounds, double density, bool createDataUrl) { + assert(shaderBounds != null); + final int widthInPixels = shaderBounds!.width.ceil(); + final int heightInPixels = shaderBounds.height.ceil(); + assert(widthInPixels > 0 && heightInPixels > 0); + + initWebGl(); + // Render gradient into a bitmap and create a canvas pattern. + final OffScreenCanvas offScreenCanvas = + OffScreenCanvas(widthInPixels, heightInPixels); + final GlContext gl = GlContext(offScreenCanvas); + gl.setViewportSize(widthInPixels, heightInPixels); + + final NormalizedGradient normalizedGradient = + NormalizedGradient(colors, stops: colorStops); + + final GlProgram glProgram = gl.cacheProgram(VertexShaders.writeBaseVertexShader(), + _createSweepFragmentShader(normalizedGradient, tileMode)); + gl.useProgram(glProgram); + + final Object tileOffset = + gl.getUniformLocation(glProgram.program, 'u_tile_offset'); + final double centerX = (center.dx - shaderBounds.left) / (shaderBounds.width); + final double centerY = (center.dy - shaderBounds.top) / (shaderBounds.height); + gl.setUniform2f(tileOffset, 2 * (shaderBounds.width * (centerX - 0.5)), + 2 * (shaderBounds.height * (centerY - 0.5))); + final Object angleRange = gl.getUniformLocation(glProgram.program, 'angle_range'); + gl.setUniform2f(angleRange, startAngle, endAngle); + normalizedGradient.setupUniforms(gl, glProgram); + if (matrix4 != null) { + final Object gradientMatrix = + gl.getUniformLocation(glProgram.program, 'm_gradient'); + gl.setUniformMatrix4fv(gradientMatrix, false, matrix4!); + } + if (createDataUrl) { + return glRenderer!.drawRectToImageUrl( + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels); + } else { + return glRenderer!.drawRect( + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels)!; + } + } + + @override + Object createPaintStyle(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + final Object imageBitmap = createImageBitmap(shaderBounds, density, false); + return ctx!.createPattern(imageBitmap, 'no-repeat')!; + } + + String _createSweepFragmentShader( + NormalizedGradient gradient, ui.TileMode tileMode) { + final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); + builder.floatPrecision = ShaderPrecision.kMedium; + builder.addIn(ShaderType.kVec4, name: 'v_color'); + builder.addUniform(ShaderType.kVec2, name: 'u_resolution'); + builder.addUniform(ShaderType.kVec2, name: 'u_tile_offset'); + builder.addUniform(ShaderType.kVec2, name: 'angle_range'); + builder.addUniform(ShaderType.kMat4, name: 'm_gradient'); + final ShaderDeclaration fragColor = builder.fragmentColor; + final ShaderMethod method = builder.addMethod('main'); + // Sweep gradient + method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);'); + method.addStatement( + 'vec4 localCoord = vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1) * m_gradient;'); + method.addStatement( + 'float angle = atan(-localCoord.y, -localCoord.x) + ${math.pi};'); + method.addStatement('float sweep = angle_range.y - angle_range.x;'); + method.addStatement('angle = (angle - angle_range.x) / sweep;'); + method.addStatement('' + 'float st = angle;'); + + final String probeName = + _writeSharedGradientShader(builder, method, gradient, tileMode); + method.addStatement('${fragColor.name} = $probeName * scale + bias;'); + + final String shader = builder.build(); + return shader; + } + + final ui.Offset center; + final List colors; + final List? colorStops; + final ui.TileMode tileMode; + final double startAngle; + final double endAngle; + final Float32List? matrix4; +} + +class GradientLinear extends EngineGradient { + GradientLinear( + this.from, + this.to, + this.colors, + this.colorStops, + this.tileMode, + Float32List? matrix, + ) : assert(offsetIsValid(from)), + assert(offsetIsValid(to)), + assert(colors != null), // ignore: unnecessary_null_comparison + assert(tileMode != null), // ignore: unnecessary_null_comparison + matrix4 = matrix == null ? null : FastMatrix32(matrix), + super._() { + if (assertionsEnabled) { + validateColorStops(colors, colorStops); + } + } + + final ui.Offset from; + final ui.Offset to; + final List colors; + final List? colorStops; + final ui.TileMode tileMode; + final FastMatrix32? matrix4; + + @override + Object createPaintStyle(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + if (tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal) { + return _createCanvasGradient(ctx, shaderBounds, density); + } else { + return _createGlGradient(ctx, shaderBounds, density); + } + } + + html.CanvasGradient _createCanvasGradient(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + final FastMatrix32? matrix4 = this.matrix4; + html.CanvasGradient gradient; + final double offsetX = shaderBounds!.left; + final double offsetY = shaderBounds.top; + if (matrix4 != null) { + // The matrix is relative to shaderBounds so we shift center by + // shaderBounds top-left origin. + final double centerX = (from.dx + to.dx) / 2.0 - shaderBounds.left; + final double centerY = (from.dy + to.dy) / 2.0 - shaderBounds.top; + + matrix4.transform(from.dx - centerX, from.dy - centerY); + final double fromX = matrix4.transformedX + centerX; + final double fromY = matrix4.transformedY + centerY; + matrix4.transform(to.dx - centerX, to.dy - centerY); + gradient = ctx!.createLinearGradient( + fromX - offsetX, + fromY - offsetY, + matrix4.transformedX + centerX - offsetX, + matrix4.transformedY + centerY - offsetY); + } else { + gradient = ctx!.createLinearGradient(from.dx - offsetX, from.dy - offsetY, + to.dx - offsetX, to.dy - offsetY); + } + _addColorStopsToCanvasGradient( + gradient, colors, colorStops, tileMode == ui.TileMode.decal); + return gradient; + } + + @override + Object createImageBitmap( + ui.Rect? shaderBounds, double density, bool createDataUrl) { + assert(shaderBounds != null); + final int widthInPixels = shaderBounds!.width.ceil(); + final int heightInPixels = shaderBounds.height.ceil(); + assert(widthInPixels > 0 && heightInPixels > 0); + initWebGl(); + // Render gradient into a bitmap and create a canvas pattern. + final OffScreenCanvas offScreenCanvas = + OffScreenCanvas(widthInPixels, heightInPixels); + final GlContext gl = GlContext(offScreenCanvas); + gl.setViewportSize(widthInPixels, heightInPixels); + + final NormalizedGradient normalizedGradient = + NormalizedGradient(colors, stops: colorStops); + + final GlProgram glProgram = gl.cacheProgram(VertexShaders.writeBaseVertexShader(), + _createLinearFragmentShader(normalizedGradient, tileMode)); + gl.useProgram(glProgram); + + /// When creating an image to apply to a dom element, render + /// contents at 0,0 and adjust gradient vector for shaderBounds. + final bool translateToOrigin = createDataUrl; + + if (translateToOrigin) { + shaderBounds = shaderBounds.translate(-shaderBounds.left, -shaderBounds.top); + } + + // Setup from/to uniforms. + // + // From/to is relative to shaderBounds. + // + // To compute t value between 0..1 for any point on the screen, + // we need to use from,to point pair to construct a matrix that will + // take any fragment coordinate and transform it to a t value. + // + // We compute the matrix by: + // 1- Shift from,to vector to origin. + // 2- Rotate the vector to align with x axis. + // 3- Scale it to unit vector. + final double fromX = from.dx; + final double fromY = from.dy; + final double toX = to.dx; + final double toY = to.dy; + + final double dx = toX - fromX; + final double dy = toY - fromY; + final double length = math.sqrt(dx * dx + dy * dy); + // sin(theta) = dy / length. + // cos(theta) = dx / length. + // Flip dy for gl flip. + final double sinVal = length < kFltEpsilon ? 0 : -dy / length; + final double cosVal = length < kFltEpsilon ? 1 : dx / length; + // If tile mode is repeated we need to shift the center of from->to + // vector to the center of shader bounds. + final bool isRepeated = tileMode != ui.TileMode.clamp; + final double originX = isRepeated + ? (shaderBounds.width / 2) + : (fromX + toX) / 2.0 - shaderBounds.left; + final double originY = isRepeated + ? (shaderBounds.height / 2) + : (fromY + toY) / 2.0 - shaderBounds.top; + + final Matrix4 originTranslation = + Matrix4.translationValues(-originX, -originY, 0); + // Rotate around Z axis. + final Matrix4 rotationZ = Matrix4.identity(); + final Float32List storage = rotationZ.storage; + storage[0] = cosVal; + // Sign is flipped since gl coordinate system is flipped around y axis. + storage[1] = sinVal; + storage[4] = -sinVal; + storage[5] = cosVal; + final Matrix4 gradientTransform = Matrix4.identity(); + // We compute location based on gl_FragCoord to center distance which + // returns 0.0 at center. To make sure we align center of gradient to this + // point, we shift by 0.5 to get st value for center of gradient. + if (tileMode != ui.TileMode.repeated) { + gradientTransform.translate(0.5, 0); + } + if (length > kFltEpsilon) { + gradientTransform.scale(1.0 / length); + } + if (matrix4 != null) { + // Flutter GradientTransform is defined in shaderBounds coordinate system + // with flipped y axis. + // We flip y axis, translate to center, multiply matrix and translate + // and flip back so it is applied correctly. + final Matrix4 m4 = Matrix4.fromFloat32List(matrix4!.matrix); + gradientTransform.scale(1, -1); + gradientTransform.translate( + -shaderBounds.center.dx, -shaderBounds.center.dy); + gradientTransform.multiply(m4); + gradientTransform.translate( + shaderBounds.center.dx, shaderBounds.center.dy); + gradientTransform.scale(1, -1); + } + + gradientTransform.multiply(rotationZ); + gradientTransform.multiply(originTranslation); + // Setup gradient uniforms for t search. + normalizedGradient.setupUniforms(gl, glProgram); + // Setup matrix transform uniform. + final Object gradientMatrix = + gl.getUniformLocation(glProgram.program, 'm_gradient'); + gl.setUniformMatrix4fv(gradientMatrix, false, gradientTransform.storage); + + final Object uRes = gl.getUniformLocation(glProgram.program, 'u_resolution'); + gl.setUniform2f(uRes, widthInPixels.toDouble(), heightInPixels.toDouble()); + + if (createDataUrl) { + return glRenderer!.drawRectToImageUrl( + ui.Rect.fromLTWH(0, 0, shaderBounds.width, + shaderBounds.height) /* !! shaderBounds */, + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels, + ); + } else { + return glRenderer!.drawRect( + ui.Rect.fromLTWH(0, 0, shaderBounds.width, + shaderBounds.height) /* !! shaderBounds */, + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels, + )!; + } + } + + /// Creates a linear gradient with tiling repeat or mirror. + html.CanvasPattern _createGlGradient(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + final Object imageBitmap = createImageBitmap(shaderBounds, density, false); + return ctx!.createPattern(imageBitmap, 'no-repeat')!; + } + + String _createLinearFragmentShader( + NormalizedGradient gradient, ui.TileMode tileMode) { + final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); + builder.floatPrecision = ShaderPrecision.kMedium; + builder.addIn(ShaderType.kVec4, name: 'v_color'); + builder.addUniform(ShaderType.kVec2, name: 'u_resolution'); + builder.addUniform(ShaderType.kMat4, name: 'm_gradient'); + final ShaderDeclaration fragColor = builder.fragmentColor; + final ShaderMethod method = builder.addMethod('main'); + // Linear gradient. + // Multiply with m_gradient transform to convert from fragment coordinate to + // distance on the from-to line. + method.addStatement('vec4 localCoord = m_gradient * vec4(gl_FragCoord.x, ' + 'u_resolution.y - gl_FragCoord.y, 0, 1);'); + method.addStatement('float st = localCoord.x;'); + final String probeName = + _writeSharedGradientShader(builder, method, gradient, tileMode); + method.addStatement('${fragColor.name} = $probeName * scale + bias;'); + final String shader = builder.build(); + return shader; + } +} + +void _addColorStopsToCanvasGradient(html.CanvasGradient gradient, + List colors, List? colorStops, bool isDecal) { + double scale, offset; + if (isDecal) { + scale = 0.999; + offset = (1.0 - scale) / 2.0; + gradient.addColorStop(0, '#00000000'); + } else { + scale = 1.0; + offset = 0.0; + } + if (colorStops == null) { + assert(colors.length == 2); + gradient.addColorStop(offset, colorToCssString(colors[0])!); + gradient.addColorStop(1 - offset, colorToCssString(colors[1])!); + } else { + for (int i = 0; i < colors.length; i++) { + final double colorStop = colorStops[i].clamp(0.0, 1.0); + gradient.addColorStop( + colorStop * scale + offset, colorToCssString(colors[i])!); + } + } + if (isDecal) { + gradient.addColorStop(1, '#00000000'); + } +} + +/// Writes shader code to map fragment value to gradient color. +/// +/// Returns name of gradient treshold variable to use to compute color. +String _writeSharedGradientShader(ShaderBuilder builder, ShaderMethod method, + NormalizedGradient gradient, ui.TileMode tileMode) { + method.addStatement('vec4 bias;'); + method.addStatement('vec4 scale;'); + // Write uniforms for each threshold, bias and scale. + for (int i = 0; i < (gradient.thresholdCount - 1) ~/ 4 + 1; i++) { + builder.addUniform(ShaderType.kVec4, name: 'threshold_$i'); + } + for (int i = 0; i < gradient.thresholdCount; i++) { + builder.addUniform(ShaderType.kVec4, name: 'bias_$i'); + builder.addUniform(ShaderType.kVec4, name: 'scale_$i'); + } + + // Use st variable name if clamped or decaled, otherwise write code to compute + // tiled_st. + String probeName = 'st'; + switch (tileMode) { + case ui.TileMode.clamp: + method.addStatement('float tiled_st = clamp(st, 0.0, 1.0);'); + probeName = 'tiled_st'; + break; + case ui.TileMode.decal: + break; + case ui.TileMode.repeated: + // st represents our distance from center. Flutter maps the center to + // center of gradient ramp so we need to add 0.5 to make sure repeated + // pattern center is at origin. + method.addStatement('float tiled_st = fract(st);'); + probeName = 'tiled_st'; + break; + case ui.TileMode.mirror: + method.addStatement('float t_1 = (st - 1.0);'); + method.addStatement( + 'float tiled_st = abs((t_1 - 2.0 * floor(t_1 * 0.5)) - 1.0);'); + probeName = 'tiled_st'; + break; + } + writeUnrolledBinarySearch(method, 0, gradient.thresholdCount - 1, + probe: probeName, + sourcePrefix: 'threshold', + biasName: 'bias', + scaleName: 'scale'); + return probeName; +} + +class GradientRadial extends EngineGradient { + GradientRadial(this.center, this.radius, this.colors, this.colorStops, + this.tileMode, this.matrix4) + : super._(); + + final ui.Offset center; + final double radius; + final List colors; + final List? colorStops; + final ui.TileMode tileMode; + final Float32List? matrix4; + + @override + Object createPaintStyle(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + if (tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal) { + return _createCanvasGradient(ctx, shaderBounds, density); + } else { + return _createGlGradient(ctx, shaderBounds, density); + } + } + + Object _createCanvasGradient(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + final double offsetX = shaderBounds!.left; + final double offsetY = shaderBounds.top; + final html.CanvasGradient gradient = ctx!.createRadialGradient( + center.dx - offsetX, + center.dy - offsetY, + 0, + center.dx - offsetX, + center.dy - offsetY, + radius); + _addColorStopsToCanvasGradient( + gradient, colors, colorStops, tileMode == ui.TileMode.decal); + return gradient; + } + + @override + Object createImageBitmap( + ui.Rect? shaderBounds, double density, bool createDataUrl) { + assert(shaderBounds != null); + final int widthInPixels = shaderBounds!.width.ceil(); + final int heightInPixels = shaderBounds.height.ceil(); + assert(widthInPixels > 0 && heightInPixels > 0); + + initWebGl(); + // Render gradient into a bitmap and create a canvas pattern. + final OffScreenCanvas offScreenCanvas = + OffScreenCanvas(widthInPixels, heightInPixels); + final GlContext gl = GlContext(offScreenCanvas); + gl.setViewportSize(widthInPixels, heightInPixels); + + final NormalizedGradient normalizedGradient = + NormalizedGradient(colors, stops: colorStops); + + final GlProgram glProgram = gl.cacheProgram( + VertexShaders.writeBaseVertexShader(), + _createRadialFragmentShader( + normalizedGradient, shaderBounds, tileMode)); + gl.useProgram(glProgram); + + final Object tileOffset = + gl.getUniformLocation(glProgram.program, 'u_tile_offset'); + final double centerX = (center.dx - shaderBounds.left) / (shaderBounds.width); + final double centerY = (center.dy - shaderBounds.top) / (shaderBounds.height); + gl.setUniform2f(tileOffset, 2 * (shaderBounds.width * (centerX - 0.5)), + 2 * (shaderBounds.height * (centerY - 0.5))); + final Object radiusUniform = gl.getUniformLocation(glProgram.program, 'u_radius'); + gl.setUniform1f(radiusUniform, radius); + normalizedGradient.setupUniforms(gl, glProgram); + + final Object gradientMatrix = + gl.getUniformLocation(glProgram.program, 'm_gradient'); + gl.setUniformMatrix4fv(gradientMatrix, false, + matrix4 == null ? Matrix4.identity().storage : matrix4!); + + if (createDataUrl) { + return glRenderer!.drawRectToImageUrl( + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels); + } else { + return glRenderer!.drawRect( + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels)!; + } + } + + /// Creates a radial gradient with tiling repeat or mirror. + html.CanvasPattern _createGlGradient(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + final Object imageBitmap = createImageBitmap(shaderBounds, density, false); + return ctx!.createPattern(imageBitmap, 'no-repeat')!; + } + + String _createRadialFragmentShader( + NormalizedGradient gradient, ui.Rect shaderBounds, ui.TileMode tileMode) { + final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); + builder.floatPrecision = ShaderPrecision.kMedium; + builder.addIn(ShaderType.kVec4, name: 'v_color'); + builder.addUniform(ShaderType.kVec2, name: 'u_resolution'); + builder.addUniform(ShaderType.kVec2, name: 'u_tile_offset'); + builder.addUniform(ShaderType.kFloat, name: 'u_radius'); + builder.addUniform(ShaderType.kMat4, name: 'm_gradient'); + final ShaderDeclaration fragColor = builder.fragmentColor; + final ShaderMethod method = builder.addMethod('main'); + // Sweep gradient + method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);'); + method.addStatement( + 'vec4 localCoord = vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1) * m_gradient;'); + method.addStatement('float dist = length(localCoord);'); + method.addStatement('' + 'float st = abs(dist / u_radius);'); + final String probeName = + _writeSharedGradientShader(builder, method, gradient, tileMode); + method.addStatement('${fragColor.name} = $probeName * scale + bias;'); + final String shader = builder.build(); + return shader; + } +} + +// TODO(ferhat): Implement focal https://github.com/flutter/flutter/issues/76643. +class GradientConical extends GradientRadial { + GradientConical( + this.focal, + this.focalRadius, + ui.Offset center, + double radius, + List colors, + List? colorStops, + ui.TileMode tileMode, + Float32List? matrix4) + : super(center, radius, colors, colorStops, tileMode, matrix4); + + final ui.Offset focal; + final double focalRadius; + + @override + Object createPaintStyle(html.CanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, double density) { + if ((tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal) && + focalRadius == 0.0 && + focal == const ui.Offset(0, 0)) { + return _createCanvasGradient(ctx, shaderBounds, density); + } else { + initWebGl(); + return _createGlGradient(ctx, shaderBounds, density); + } + } + + @override + String _createRadialFragmentShader( + NormalizedGradient gradient, ui.Rect shaderBounds, ui.TileMode tileMode) { + /// If distance between centers is nearly zero we can pretend we're radial + /// to prevent divide by zero in computing gradient. + final double centerDistanceX = center.dx - focal.dx; + final double centerDistanceY = center.dy - focal.dy; + final double centerDistanceSq = + centerDistanceX * centerDistanceX + centerDistanceY * centerDistanceY; + if (centerDistanceSq < kFltEpsilonSquared) { + return super + ._createRadialFragmentShader(gradient, shaderBounds, tileMode); + } + final double centerDistance = math.sqrt(centerDistanceSq); + double r0 = focalRadius / centerDistance; + double r1 = radius / centerDistance; + double fFocalX = r0 / (r0 - r1); + + if ((fFocalX - 1).abs() < SPath.scalarNearlyZero) { + // swap r0, r1 + final double temp = r0; + r0 = r1; + r1 = temp; + fFocalX = 0.0; // because r0 is now 0 + } + + final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); + builder.floatPrecision = ShaderPrecision.kMedium; + builder.addIn(ShaderType.kVec4, name: 'v_color'); + builder.addUniform(ShaderType.kVec2, name: 'u_resolution'); + builder.addUniform(ShaderType.kVec2, name: 'u_tile_offset'); + builder.addUniform(ShaderType.kFloat, name: 'u_radius'); + builder.addUniform(ShaderType.kMat4, name: 'm_gradient'); + final ShaderDeclaration fragColor = builder.fragmentColor; + final ShaderMethod method = builder.addMethod('main'); + // Sweep gradient + method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);'); + method.addStatement( + 'vec4 localCoord = vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1) * m_gradient;'); + method.addStatement('float dist = length(localCoord);'); + final String f = (focalRadius / + (math.min(shaderBounds.width, shaderBounds.height) / 2.0)) + .toStringAsPrecision(8); + method.addStatement(focalRadius == 0.0 + ? 'float st = dist / u_radius;' + : 'float st = ((dist / u_radius) - $f) / (1.0 - $f);'); + if (tileMode == ui.TileMode.clamp) { + method.addStatement('if (st < 0.0) { st = -1.0; }'); + } + final String probeName = + _writeSharedGradientShader(builder, method, gradient, tileMode); + method.addStatement('${fragColor.name} = $probeName * scale + bias;'); + return builder.build(); + } +} + +/// Backend implementation of [ui.ImageFilter]. +/// +/// Currently only `blur` and `matrix` are supported. +abstract class EngineImageFilter implements ui.ImageFilter { + factory EngineImageFilter.blur({ + required double sigmaX, + required double sigmaY, + required ui.TileMode tileMode, + }) = _BlurEngineImageFilter; + + factory EngineImageFilter.matrix({ + required Float64List matrix, + required ui.FilterQuality filterQuality, + }) = _MatrixEngineImageFilter; + + EngineImageFilter._(); + + String get filterAttribute => ''; + String get transformAttribute => ''; +} + +class _BlurEngineImageFilter extends EngineImageFilter { + _BlurEngineImageFilter({ this.sigmaX = 0.0, this.sigmaY = 0.0, this.tileMode = ui.TileMode.clamp }) : super._(); + + final double sigmaX; + final double sigmaY; + final ui.TileMode tileMode; + + // TODO(ferhat): implement TileMode. + @override + String get filterAttribute => blurSigmasToCssString(sigmaX, sigmaY); + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is _BlurEngineImageFilter && + other.tileMode == tileMode && + other.sigmaX == sigmaX && + other.sigmaY == sigmaY; + } + + @override + int get hashCode => ui.hashValues(sigmaX, sigmaY, tileMode); + + @override + String toString() { + return 'ImageFilter.blur($sigmaX, $sigmaY, $tileMode)'; + } +} + +class _MatrixEngineImageFilter extends EngineImageFilter { + _MatrixEngineImageFilter({ required Float64List matrix, required this.filterQuality }) + : webMatrix = Float64List.fromList(matrix), + super._(); + + final Float64List webMatrix; + final ui.FilterQuality filterQuality; + + // TODO(yjbanov): implement FilterQuality. + @override + String get transformAttribute => float64ListToCssTransform(webMatrix); + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) + return false; + return other is _MatrixEngineImageFilter + && other.filterQuality == filterQuality + && listEquals(other.webMatrix, webMatrix); + } + + @override + int get hashCode => ui.hashValues(ui.hashList(webMatrix), filterQuality); + + @override + String toString() { + return 'ImageFilter.matrix($webMatrix, $filterQuality)'; + } +} diff --git a/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart b/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart new file mode 100644 index 0000000000000..aa34f0191c341 --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart @@ -0,0 +1,425 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/ui.dart' as ui; + +import '../../browser_detection.dart'; +import '../../util.dart'; + +/// Creates shader program for target webgl version. +/// +/// See spec at https://www.khronos.org/registry/webgl/specs/latest/1.0/. +/// +/// Differences in WebGL2 vs WebGL1. +/// - WebGL2 needs '#version 300 es' to enable the new shading language +/// - vertex attributes have the qualifier 'in' instead of 'attribute' +/// - GLSL 3.00 defines texture and other new and future reserved words. +/// - varying is now called `in`. +/// - GLSL 1.00 has a predefined variable gl_FragColor which now needs to be +/// defined as `out vec4 fragmentColor`. +/// - Texture lookup functions texture2D and textureCube have now been +/// replaced with texture. +/// +/// Example usage: +/// ShaderBuilder builder = ShaderBuilder(WebGlVersion.webgl2); +/// ShaderDeclaration u1 = builder.addUniform(ShaderType.kVec4); +/// ShaderMethod method = builder.addMethod('main'); +/// method.addStatement('${u1.name} = vec4(1.0, 1.0, 1.0, 0.0);'); +/// source = builder.build(); +class ShaderBuilder { + /// WebGL version. + final int version; + final List declarations = []; + final List _methods = []; + + /// Precision for integer variables. + int? integerPrecision; + + /// Precision floating point variables. + int? floatPrecision; + + /// Counter for generating unique name if name is not specified for attribute. + int _attribCounter = 0; + + /// Counter for generating unique name if name is not specified for varying. + int _varyingCounter = 0; + + /// Counter for generating unique name if name is not specified for uniform. + int _uniformCounter = 0; + + /// Counter for generating unique name if name is not specified for constant. + int _constCounter = 0; + + final bool isWebGl2; + final bool _isFragmentShader; + + static const String kOpenGlEs3Header = '#version 300 es'; + + /// Lazily allocated fragment color output. + ShaderDeclaration? _fragmentColorDeclaration; + + ShaderBuilder(this.version) : isWebGl2 = version == WebGLVersion.webgl2, + _isFragmentShader = false; + + ShaderBuilder.fragment(this.version) : + isWebGl2 = version == WebGLVersion.webgl2, + _isFragmentShader = true; + + /// Returns fragment color declaration for fragment shader. + /// + /// This is hard coded for webgl1 as gl_FragColor. + ShaderDeclaration get fragmentColor { + _fragmentColorDeclaration ??= ShaderDeclaration( + isWebGl2 ? 'gFragColor' : 'gl_FragColor', + ShaderType.kVec4, + ShaderStorageQualifier.kVarying); + return _fragmentColorDeclaration!; + } + + /// Adds an attribute. + /// + /// The attribute variable is assigned a value from a object buffer as a + /// series of graphics primitives are rendered. The value is only accessible + /// in the vertex shader. + ShaderDeclaration addIn(int dataType, {String? name}) { + final ShaderDeclaration attrib = ShaderDeclaration( + name ?? 'attr_${_attribCounter++}', + dataType, + ShaderStorageQualifier.kAttribute); + declarations.add(attrib); + return attrib; + } + + /// Adds a constant. + ShaderDeclaration addConst(int dataType, String value, {String? name}) { + final ShaderDeclaration declaration = ShaderDeclaration.constant( + name ?? 'c_${_constCounter++}', dataType, value); + declarations.add(declaration); + return declaration; + } + + /// Adds a uniform variable. + /// + /// The variable is assigned a value before a gl.draw call. + /// It is accessible in both the vertex and fragment shaders. + /// + ShaderDeclaration addUniform(int dataType, {String? name}) { + final ShaderDeclaration uniform = ShaderDeclaration( + name ?? 'uni_${_uniformCounter++}', + dataType, + ShaderStorageQualifier.kUniform); + declarations.add(uniform); + return uniform; + } + + /// Adds a varying variable. + /// + /// The variable is assigned a value by a vertex shader and + /// interpolated across the surface of a graphics primitive for each + /// input to a fragment shader. + /// It can be used in a fragment shader, but not changed. + ShaderDeclaration addOut(int dataType, {String? name}) { + final ShaderDeclaration varying = ShaderDeclaration( + name ?? 'output_${_varyingCounter++}', + dataType, + ShaderStorageQualifier.kVarying); + declarations.add(varying); + return varying; + } + + void _writeVariableDeclaration(StringBuffer sb, ShaderDeclaration variable) { + switch (variable.storage) { + case ShaderStorageQualifier.kConst: + _buffer.write('const '); + break; + case ShaderStorageQualifier.kAttribute: + _buffer.write(isWebGl2 ? 'in ' + : _isFragmentShader ? 'varying ' : 'attribute '); + break; + case ShaderStorageQualifier.kUniform: + _buffer.write('uniform '); + break; + case ShaderStorageQualifier.kVarying: + _buffer.write(isWebGl2 ? 'out ' : 'varying '); + break; + } + _buffer.write('${typeToString(variable.dataType)} ${variable.name}'); + if (variable.storage == ShaderStorageQualifier.kConst) { + _buffer.write(' = ${variable.constValue}'); + } + _buffer.writeln(';'); + } + + final StringBuffer _buffer = StringBuffer(); + + static String typeToString(int dataType) { + switch (dataType) { + case ShaderType.kBool: + return 'bool'; + case ShaderType.kInt: + return 'int'; + case ShaderType.kFloat: + return 'float'; + case ShaderType.kBVec2: + return 'bvec2'; + case ShaderType.kBVec3: + return 'bvec3'; + case ShaderType.kBVec4: + return 'bvec4'; + case ShaderType.kIVec2: + return 'ivec2'; + case ShaderType.kIVec3: + return 'ivec3'; + case ShaderType.kIVec4: + return 'ivec4'; + case ShaderType.kVec2: + return 'vec2'; + case ShaderType.kVec3: + return 'vec3'; + case ShaderType.kVec4: + return 'vec4'; + case ShaderType.kMat2: + return 'mat2'; + case ShaderType.kMat3: + return 'mat3'; + case ShaderType.kMat4: + return 'mat4'; + case ShaderType.kSampler1D: + return 'sampler1D'; + case ShaderType.kSampler2D: + return 'sampler2D'; + case ShaderType.kSampler3D: + return 'sampler3D'; + case ShaderType.kVoid: + return 'void'; + } + throw ArgumentError(); + } + + ShaderMethod addMethod(String name) { + final ShaderMethod method = ShaderMethod(name); + _methods.add(method); + return method; + } + + String build() { + // Write header. + if (isWebGl2) { + _buffer.writeln(kOpenGlEs3Header); + } + // Write optional precision. + if (integerPrecision != null) { + _buffer + .writeln('precision ${_precisionToString(integerPrecision!)} int;'); + } + if (floatPrecision != null) { + _buffer + .writeln('precision ${_precisionToString(floatPrecision!)} float;'); + } + if (isWebGl2 && _fragmentColorDeclaration != null) { + _writeVariableDeclaration(_buffer, _fragmentColorDeclaration!); + } + for (final ShaderDeclaration decl in declarations) { + _writeVariableDeclaration(_buffer, decl); + } + for (final ShaderMethod method in _methods) { + method.write(_buffer); + } + return _buffer.toString(); + } + + String _precisionToString(int precision) => precision == ShaderPrecision.kLow + ? 'lowp' + : precision == ShaderPrecision.kMedium ? 'mediump' : 'highp'; + + String get texture2DFunction => isWebGl2 ? 'texture' : 'texture2D'; +} + +class ShaderMethod { + ShaderMethod(this.name); + + final String returnType = 'void'; + final String name; + final List _statements = []; + int _indentLevel = 1; + + void indent() { + ++_indentLevel; + } + + void unindent() { + assert(_indentLevel != 1); + --_indentLevel; + } + + void addStatement(String statement) { + if (assertionsEnabled) { + _statements.add(' ' * _indentLevel + statement); + } else { + _statements.add(statement); + } + } + + /// Adds statements to compute tiling in 0..1 coordinate space. + /// + /// For clamp we simply assign source value to destination. + /// + /// For repeat, we use fractional part of source value. + /// float destination = fract(source); + /// + /// For mirror, we repeat every 2 units, by scaling and measuring distance + /// from floor. + /// float destination = 1.0 - source; + /// destination = abs((destination - 2.0 * floor(destination * 0.5)) - 1.0); + void addTileStatements(String source, String destination, + ui.TileMode tileMode) { + switch(tileMode) { + case ui.TileMode.repeated: + addStatement('float $destination = fract($source);'); + break; + case ui.TileMode.mirror: + addStatement('float $destination = ($source - 1.0);'); + addStatement( + '$destination = ' + 'abs(($destination - 2.0 * floor($destination * 0.5)) - 1.0);'); + break; + case ui.TileMode.clamp: + case ui.TileMode.decal: + addStatement('float $destination = $source;'); + break; + } + } + + void write(StringBuffer buffer) { + buffer.writeln('$returnType $name() {'); + _statements.forEach(buffer.writeln); + buffer.writeln('}'); + } +} + +/// WebGl Shader data types. +abstract class ShaderType { + // Basic types. + static const int kBool = 0; + static const int kInt = 1; + static const int kFloat = 2; + // Vector types. + static const int kBVec2 = 3; + static const int kBVec3 = 4; + static const int kBVec4 = 5; + static const int kIVec2 = 6; + static const int kIVec3 = 7; + static const int kIVec4 = 8; + static const int kVec2 = 9; + static const int kVec3 = 10; + static const int kVec4 = 11; + static const int kMat2 = 12; + static const int kMat3 = 13; + static const int kMat4 = 14; + // Textures. + static const int kSampler1D = 15; + static const int kSampler2D = 16; + static const int kSampler3D = 17; + // Other. + static const int kVoid = 18; +} + +/// Precision of int and float types. +/// +/// Integers: 8 bit, 10 bit and 16 bits. +/// Float: 8 bit. 14 bit and 62 bits. +abstract class ShaderPrecision { + static const int kLow = 0; + static const int kMedium = 1; + static const int kHigh = 2; +} + +/// GL Variable storage qualifiers. +abstract class ShaderStorageQualifier { + static const int kConst = 0; + static const int kAttribute = 1; + static const int kUniform = 2; + static const int kVarying = 3; +} + +/// Shader variable and constant declaration. +class ShaderDeclaration { + final String name; + final int dataType; + final int storage; + final String constValue; + ShaderDeclaration(this.name, this.dataType, this.storage) + : assert(!_isGLSLReservedWord(name)), + constValue = ''; + + /// Constructs a constant. + ShaderDeclaration.constant(this.name, this.dataType, this.constValue) + : storage = ShaderStorageQualifier.kConst; +} + +// These are used only in debug mode to assert if used as variable name. +// https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.10.pdf +const List _kReservedWords = [ + 'attribute', + 'const', + 'uniform', + 'varying', + 'layout', + 'centroid', + 'flat', + 'smooth', + 'noperspective', + 'patch', 'sample', + 'break', 'continue', + 'do', 'for', 'while', 'switch', 'case', 'default', 'if', 'else', + 'subroutine', + 'in', 'out', 'inout', 'float', 'double', 'int', + 'void', + 'bool', 'true', 'false', + 'invariant', + 'discard', 'return', + 'mat2', 'mat3', 'mat4', 'dmat2', 'dmat3', 'dmat4', + 'mat2x2', 'mat2x3', 'mat2x4', 'dmat2x2', 'dmat2x3', 'dmat2x4', + 'mat3x2', 'mat3x3', 'mat3x4', 'dmat3x2', 'dmat3x3', 'dmat3x4', + 'mat4x2', 'mat4x3', 'mat4x4', 'dmat4x2', 'dmat4x3', 'dmat4x4', + 'vec2', 'vec3', 'vec4', 'ivec2', 'ivec3', 'ivec4', 'bvec2', 'bvec3', 'bvec4', + 'dvec2', 'dvec3', 'dvec4', + 'uint', 'uvec2', 'uvec3', 'uvec4', + 'lowp', 'mediump', 'highp', 'precision', + 'sampler1D', 'sampler2D', 'sampler3D', 'samplerCube', + 'sampler1DShadow', 'sampler2DShadow', 'samplerCubeShadow', + 'sampler1DArray', 'sampler2DArray', + 'sampler1DArrayShadow', 'sampler2DArrayShadow', + 'isampler1D', 'isampler2D', 'isampler3D', 'isamplerCube', + 'isampler1DArray', 'isampler2DArray', + 'usampler1D', 'usampler2D', 'usampler3D', 'usamplerCube', + 'usampler1DArray', 'usampler2DArray', + 'sampler2DRect', 'sampler2DRectShadow', 'isampler2DRect', 'usampler2DRect', + 'samplerBuffer', 'isamplerBuffer', 'usamplerBuffer', + 'sampler2DMS', 'isampler2DMS', 'usampler2DMS', + 'sampler2DMSArray', 'isampler2DMSArray', 'usampler2DMSArray', + 'samplerCubeArray', 'samplerCubeArrayShadow', 'isamplerCubeArray', + 'usamplerCubeArray', + 'struct', + 'texture', + + // Reserved for future use, see + // https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.10.pdf + 'active', 'asm', 'cast', 'class', 'common', 'enum', 'extern', 'external', + 'filter', 'fixed', 'fvec2', 'fvec3', 'fvec4', 'goto', 'half', 'hvec2', + 'hvec3', 'hvec4', 'iimage1D', 'iimage1DArray', 'iimage2D', 'iimage2DArray', + 'iimage3D', 'iimageBuffer', 'iimageCube', 'image1D', 'image1DArray', + 'image1DArrayShadow', 'image1DShadow', 'image2D', 'image2DArray', + 'image2DArrayShadow', 'image2DShadow', 'image3D', 'imageBuffer', + 'imageCube', 'inline', 'input', 'interface', 'long', + 'namespace', 'noinline', 'output', 'packed', 'partition', 'public', + 'row_majo', 'short', 'sizeof', 'static', 'superp', 'template', 'this', + 'typedef', 'uimage1D', 'uimage1DArray', 'uimage2D', 'uimage2DArray', + 'uimage3D', 'uimageBuffer', 'uimageCube', 'union', 'unsigned', + 'using', 'volatile', +]; + +bool _isGLSLReservedWord(String name) { + return _kReservedWords.contains(name); +} diff --git a/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart b/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart new file mode 100644 index 0000000000000..b2dd353abcf8d --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart @@ -0,0 +1,108 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../browser_detection.dart'; +import 'shader_builder.dart'; + +/// Provides common shaders used for gradients and drawVertices APIs. +abstract class VertexShaders { + // This class is not meant to be instantiated or extended; this constructor + // prevents instantiation and extension. + VertexShaders._(); + + static final Uint16List vertexIndicesForRect = + Uint16List.fromList([0, 1, 2, 2, 3, 0]); + + /// Cached vertex shaders. + static String? _baseVertexShader; + static String? _textureVertexShader; + + /// Creates a vertex shader transforms pixel space [Vertices.positions] to + /// final clipSpace -1..1 coordinates with inverted Y Axis. + /// #version 300 es + /// layout (location=0) in vec4 position; + /// layout (location=1) in vec4 color; + /// uniform mat4 u_ctransform; + /// uniform vec4 u_scale; + /// uniform vec4 u_shift; + /// out vec4 vColor; + /// void main() { + /// gl_Position = ((u_ctransform * position) * u_scale) + u_shift; + /// v_color = color.zyxw; + /// } + static String writeBaseVertexShader() { + if (_baseVertexShader == null) { + final ShaderBuilder builder = ShaderBuilder(webGLVersion); + builder.addIn(ShaderType.kVec4, name: 'position'); + builder.addIn(ShaderType.kVec4, name: 'color'); + builder.addUniform(ShaderType.kMat4, name: 'u_ctransform'); + builder.addUniform(ShaderType.kVec4, name: 'u_scale'); + builder.addUniform(ShaderType.kVec4, name: 'u_shift'); + builder.addOut(ShaderType.kVec4, name: 'v_color'); + final ShaderMethod method = builder.addMethod('main'); + method.addStatement( + 'gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); + method.addStatement('v_color = color.zyxw;'); + _baseVertexShader = builder.build(); + } + return _baseVertexShader!; + } + + static String writeTextureVertexShader() { + if (_textureVertexShader == null) { + final ShaderBuilder builder = ShaderBuilder(webGLVersion); + builder.addIn(ShaderType.kVec4, name: 'position'); + builder.addUniform(ShaderType.kMat4, name: 'u_ctransform'); + builder.addUniform(ShaderType.kVec4, name: 'u_scale'); + builder.addUniform(ShaderType.kVec4, name: 'u_textransform'); + builder.addUniform(ShaderType.kVec4, name: 'u_shift'); + builder.addOut(ShaderType.kVec2, name: 'v_texcoord'); + final ShaderMethod method = builder.addMethod('main'); + method.addStatement( + 'gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); + method.addStatement('v_texcoord = vec2((u_textransform.z + position.x) * u_textransform.x, ' + '((u_textransform.w + position.y) * u_textransform.y));'); + _textureVertexShader = builder.build(); + } + return _textureVertexShader!; + } +} + +abstract class FragmentShaders { + // This class is not meant to be instantiated or extended; this constructor + // prevents instantiation and extension. + FragmentShaders._(); + + static String writeTextureFragmentShader( + bool isWebGl2, ui.TileMode? tileModeX, ui.TileMode? tileModeY) { + final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); + builder.floatPrecision = ShaderPrecision.kMedium; + builder.addIn(ShaderType.kVec2, name: 'v_texcoord'); + builder.addUniform(ShaderType.kSampler2D, name: 'u_texture'); + final ShaderMethod method = builder.addMethod('main'); + if (isWebGl2 || + tileModeX == null || + tileModeY == null || + (tileModeX == ui.TileMode.clamp && tileModeY == ui.TileMode.clamp)) { + method.addStatement('${builder.fragmentColor.name} = ' + '${builder.texture2DFunction}(u_texture, v_texcoord);'); + } else { + // Repeat and mirror are not supported for webgl1. Write code to + // adjust texture coordinate. + // + // This will write u and v floats, clamp/repeat and mirror the value and + // pass it to sampler. + method.addTileStatements('v_texcoord.x', 'u', tileModeX); + method.addTileStatements('v_texcoord.y', 'v', tileModeY); + method.addStatement('vec2 uv = vec2(u, v);'); + method.addStatement('${builder.fragmentColor.name} = ' + '${builder.texture2DFunction}(u_texture, uv);'); + } + return builder.build(); + } +} diff --git a/lib/web_ui/lib/src/engine/html/shaders/webgl_context.dart b/lib/web_ui/lib/src/engine/html/shaders/webgl_context.dart new file mode 100644 index 0000000000000..1343647ec6eba --- /dev/null +++ b/lib/web_ui/lib/src/engine/html/shaders/webgl_context.dart @@ -0,0 +1,555 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../browser_detection.dart'; +import '../../vector_math.dart'; +import '../offscreen_canvas.dart'; + +/// Compiled and cached gl program. +class GlProgram { + final Object program; + GlProgram(this.program); +} + +/// JS Interop helper for webgl apis. +class GlContext { + final Object glContext; + final bool isOffscreen; + dynamic _kCompileStatus; + dynamic _kArrayBuffer; + dynamic _kElementArrayBuffer; + dynamic _kStaticDraw; + dynamic _kFloat; + dynamic _kColorBufferBit; + dynamic _kTexture2D; + dynamic _kTextureWrapS; + dynamic _kTextureWrapT; + dynamic _kRepeat; + dynamic _kClampToEdge; + dynamic _kMirroredRepeat; + dynamic _kTriangles; + dynamic _kLinkStatus; + dynamic _kUnsignedByte; + dynamic _kUnsignedShort; + dynamic _kRGBA; + dynamic _kLinear; + dynamic _kTextureMinFilter; + int? _kTexture0; + + Object? _canvas; + int? _widthInPixels; + int? _heightInPixels; + static late Map _programCache; + + factory GlContext(OffScreenCanvas offScreenCanvas) { + return OffScreenCanvas.supported + ? GlContext._fromOffscreenCanvas(offScreenCanvas.offScreenCanvas!) + : GlContext._fromCanvasElement( + offScreenCanvas.canvasElement!, webGLVersion == WebGLVersion.webgl1); + } + + GlContext._fromOffscreenCanvas(html.OffscreenCanvas canvas) + : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false})!, + isOffscreen = true { + _programCache = {}; + _canvas = canvas; + } + + GlContext._fromCanvasElement(html.CanvasElement canvas, bool useWebGl1) + : glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', + {'premultipliedAlpha': false})!, + isOffscreen = false { + _programCache = {}; + _canvas = canvas; + } + + void setViewportSize(int width, int height) { + _widthInPixels = width; + _heightInPixels = height; + } + + /// Draws Gl context contents to canvas context. + void drawImage(html.CanvasRenderingContext2D context, + double left, double top) { + // Actual size of canvas may be larger than viewport size. Use + // source/destination to draw part of the image data. + js_util.callMethod(context, 'drawImage', + [_canvas, 0, 0, _widthInPixels, _heightInPixels, + left, top, _widthInPixels, _heightInPixels]); + } + + GlProgram cacheProgram( + String vertexShaderSource, String fragmentShaderSource) { + final String cacheKey = '$vertexShaderSource||$fragmentShaderSource'; + GlProgram? cachedProgram = _programCache[cacheKey]; + if (cachedProgram == null) { + // Create and compile shaders. + final Object vertexShader = compileShader('VERTEX_SHADER', vertexShaderSource); + final Object fragmentShader = + compileShader('FRAGMENT_SHADER', fragmentShaderSource); + // Create a gl program and link shaders. + final Object program = createProgram(); + attachShader(program, vertexShader); + attachShader(program, fragmentShader); + linkProgram(program); + cachedProgram = GlProgram(program); + _programCache[cacheKey] = cachedProgram; + } + return cachedProgram; + } + + Object compileShader(String shaderType, String source) { + final Object? shader = _createShader(shaderType); + if (shader == null) { + throw Exception(error); + } + js_util.callMethod(glContext, 'shaderSource', [shader, source]); + js_util.callMethod(glContext, 'compileShader', [shader]); + final bool shaderStatus = js_util.callMethod( + glContext, + 'getShaderParameter', + [shader, compileStatus], + ) as bool; + if (!shaderStatus) { + throw Exception('Shader compilation failed: ${getShaderInfoLog(shader)}'); + } + return shader; + } + + Object createProgram() => + js_util.callMethod(glContext, 'createProgram', const []) as Object; + + void attachShader(Object? program, Object shader) { + js_util.callMethod(glContext, 'attachShader', [program, shader]); + } + + void linkProgram(Object program) { + js_util.callMethod(glContext, 'linkProgram', [program]); + final bool programStatus = js_util.callMethod( + glContext, + 'getProgramParameter', + [program, kLinkStatus], + ) as bool; + if (!programStatus) { + throw Exception(getProgramInfoLog(program)); + } + } + + void useProgram(GlProgram program) { + js_util.callMethod(glContext, 'useProgram', [program.program]); + } + + Object? createBuffer() => + js_util.callMethod(glContext, 'createBuffer', const []); + + void bindArrayBuffer(Object? buffer) { + js_util.callMethod(glContext, 'bindBuffer', [kArrayBuffer, buffer]); + } + + Object? createVertexArray() => + js_util.callMethod(glContext, 'createVertexArray', const []); + + void bindVertexArray(Object vertexObjectArray) { + js_util.callMethod(glContext, 'bindVertexArray', + [vertexObjectArray]); + } + + void unbindVertexArray() { + js_util.callMethod(glContext, 'bindVertexArray', + [null]); + } + + void bindElementArrayBuffer(Object? buffer) { + js_util.callMethod(glContext, 'bindBuffer', [kElementArrayBuffer, buffer]); + } + + Object? createTexture() => + js_util.callMethod(glContext, 'createTexture', const []); + + void generateMipmap(dynamic target) => + js_util.callMethod(glContext, 'generateMipmap', [target]); + + void bindTexture(dynamic target, Object? buffer) { + js_util.callMethod(glContext, 'bindTexture', [target, buffer]); + } + + void activeTexture(int textureUnit) { + js_util.callMethod(glContext, 'activeTexture', [textureUnit]); + } + + void texImage2D(dynamic target, int level, dynamic internalFormat, + dynamic format, dynamic dataType, + dynamic pixels, {int? width, int? height, int border = 0}) { + if (width == null) { + js_util.callMethod(glContext, 'texImage2D', [ + target, level, internalFormat, format, dataType, pixels]); + } else { + js_util.callMethod(glContext, 'texImage2D', [ + target, level, internalFormat, width, height, border, format, dataType, + pixels]); + } + } + + void texParameteri(dynamic target, dynamic parameterName, dynamic value) { + js_util.callMethod(glContext, 'texParameteri', [ + target, parameterName, value]); + } + + void deleteBuffer(Object buffer) { + js_util.callMethod(glContext, 'deleteBuffer', [buffer]); + } + + void bufferData(TypedData? data, dynamic type) { + js_util.callMethod(glContext, 'bufferData', [kArrayBuffer, data, type]); + } + + void bufferElementData(TypedData? data, dynamic type) { + js_util.callMethod(glContext, 'bufferData', [kElementArrayBuffer, data, type]); + } + + void enableVertexAttribArray(dynamic index) { + js_util.callMethod(glContext, 'enableVertexAttribArray', [index]); + } + + /// Clear background. + void clear() { + js_util.callMethod(glContext, 'clear', [kColorBufferBit]); + } + + /// Destroys gl context. + void dispose() { + js_util.callMethod( + _getExtension('WEBGL_lose_context') as Object, + 'loseContext', + const [], + ); + } + + void deleteProgram(Object program) { + js_util.callMethod(glContext, 'deleteProgram', [program]); + } + + void deleteShader(Object shader) { + js_util.callMethod(glContext, 'deleteShader', [shader]); + } + + dynamic _getExtension(String extensionName) => + js_util.callMethod(glContext, 'getExtension', [extensionName]); + + void drawTriangles(int triangleCount, ui.VertexMode vertexMode) { + final dynamic mode = _triangleTypeFromMode(vertexMode); + js_util.callMethod(glContext, 'drawArrays', [mode, 0, triangleCount]); + } + + void drawElements(dynamic type, int indexCount, dynamic indexType) { + js_util.callMethod(glContext, 'drawElements', [type, indexCount, indexType, 0]); + } + + /// Sets affine transformation from normalized device coordinates + /// to window coordinates + void viewport(double x, double y, double width, double height) { + js_util.callMethod(glContext, 'viewport', [x, y, width, height]); + } + + dynamic _triangleTypeFromMode(ui.VertexMode mode) { + switch (mode) { + case ui.VertexMode.triangles: + return kTriangles; + case ui.VertexMode.triangleFan: + return kTriangleFan; + case ui.VertexMode.triangleStrip: + return kTriangleStrip; + } + } + + Object? _createShader(String shaderType) => js_util.callMethod( + glContext, 'createShader', [js_util.getProperty(glContext, shaderType)]); + + /// Error state of gl context. + dynamic get error => js_util.callMethod(glContext, 'getError', const []); + + /// Shader compiler error, if this returns [kFalse], to get details use + /// [getShaderInfoLog]. + dynamic get compileStatus => + _kCompileStatus ??= js_util.getProperty(glContext, 'COMPILE_STATUS'); + + dynamic get kArrayBuffer => + _kArrayBuffer ??= js_util.getProperty(glContext, 'ARRAY_BUFFER'); + + dynamic get kElementArrayBuffer => + _kElementArrayBuffer ??= js_util.getProperty(glContext, + 'ELEMENT_ARRAY_BUFFER'); + + dynamic get kLinkStatus => + _kLinkStatus ??= js_util.getProperty(glContext, 'LINK_STATUS'); + + dynamic get kFloat => _kFloat ??= js_util.getProperty(glContext, 'FLOAT'); + + dynamic get kRGBA => _kRGBA ??= js_util.getProperty(glContext, 'RGBA'); + + dynamic get kUnsignedByte => + _kUnsignedByte ??= js_util.getProperty(glContext, 'UNSIGNED_BYTE'); + + dynamic get kUnsignedShort => + _kUnsignedShort ??= js_util.getProperty(glContext, 'UNSIGNED_SHORT'); + + dynamic get kStaticDraw => + _kStaticDraw ??= js_util.getProperty(glContext, 'STATIC_DRAW'); + + dynamic get kTriangles => + _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLES'); + + dynamic get kTriangleFan => + _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_FAN'); + + dynamic get kTriangleStrip => + _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_STRIP'); + + dynamic get kColorBufferBit => + _kColorBufferBit ??= js_util.getProperty(glContext, 'COLOR_BUFFER_BIT'); + + dynamic get kTexture2D => + _kTexture2D ??= js_util.getProperty(glContext, 'TEXTURE_2D'); + + int get kTexture0 => + _kTexture0 ??= js_util.getProperty(glContext, 'TEXTURE0') as int; + + dynamic get kTextureWrapS => + _kTextureWrapS ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_S'); + + dynamic get kTextureWrapT => + _kTextureWrapT ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_T'); + + dynamic get kRepeat => + _kRepeat ??= js_util.getProperty(glContext, 'REPEAT'); + + dynamic get kClampToEdge => + _kClampToEdge ??= js_util.getProperty(glContext, 'CLAMP_TO_EDGE'); + + dynamic get kMirroredRepeat => + _kMirroredRepeat ??= js_util.getProperty(glContext, 'MIRRORED_REPEAT'); + + dynamic get kLinear => + _kLinear ??= js_util.getProperty(glContext, 'LINEAR'); + + dynamic get kTextureMinFilter => + _kTextureMinFilter ??= js_util.getProperty(glContext, + 'TEXTURE_MIN_FILTER'); + + /// Returns reference to uniform in program. + Object getUniformLocation(Object program, String uniformName) { + final Object? res = js_util + .callMethod(glContext, 'getUniformLocation', [program, uniformName]); + if (res == null) { + throw Exception('$uniformName not found'); + } else { + return res; + } + } + + /// Returns true if uniform exists. + bool containsUniform(Object program, String uniformName) { + final Object? res = js_util + .callMethod(glContext, 'getUniformLocation', [program, uniformName]); + return res != null; + } + + /// Returns reference to uniform in program. + Object getAttributeLocation(Object program, String attribName) { + final Object? res = js_util + .callMethod(glContext, 'getAttribLocation', [program, attribName]); + if (res == null) { + throw Exception('$attribName not found'); + } else { + return res; + } + } + + /// Sets float uniform value. + void setUniform1f(Object uniform, double value) { + js_util.callMethod(glContext, 'uniform1f', [uniform, value]); + } + + /// Sets vec2 uniform values. + void setUniform2f(Object uniform, double value1, double value2) { + js_util.callMethod(glContext, 'uniform2f', [uniform, value1, value2]); + } + + /// Sets vec4 uniform values. + void setUniform4f(Object uniform, double value1, double value2, double value3, + double value4) { + js_util.callMethod( + glContext, 'uniform4f', [uniform, value1, value2, value3, value4]); + } + + /// Sets mat4 uniform values. + void setUniformMatrix4fv(Object uniform, bool transpose, Float32List value) { + js_util.callMethod( + glContext, 'uniformMatrix4fv', [uniform, transpose, value]); + } + + /// Shader compile error log. + dynamic getShaderInfoLog(Object glShader) { + return js_util.callMethod(glContext, 'getShaderInfoLog', [glShader]); + } + + /// Errors that occurred during failed linking or validation of program + /// objects. Typically called after [linkProgram]. + String? getProgramInfoLog(Object glProgram) { + return js_util.callMethod(glContext, 'getProgramInfoLog', [glProgram]) as String?; + } + + int? get drawingBufferWidth => + js_util.getProperty(glContext, 'drawingBufferWidth') as int?; + int? get drawingBufferHeight => + js_util.getProperty(glContext, 'drawingBufferWidth') as int?; + + /// Reads gl contents as image data. + /// + /// Warning: data is read bottom up (flipped). + html.ImageData readImageData() { + const int kBytesPerPixel = 4; + final int bufferWidth = _widthInPixels!; + final int bufferHeight = _heightInPixels!; + if (browserEngine == BrowserEngine.webkit || + browserEngine == BrowserEngine.firefox) { + final Uint8List pixels = + Uint8List(bufferWidth * bufferHeight * kBytesPerPixel); + js_util.callMethod(glContext, 'readPixels', + [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); + return html.ImageData( + Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight); + } else { + final Uint8ClampedList pixels = + Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel); + js_util.callMethod(glContext, 'readPixels', + [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); + return html.ImageData(pixels, bufferWidth, bufferHeight); + } + } + + /// Returns image data in a form that can be used to create Canvas + /// context patterns. + Object? readPatternData() { + // When using OffscreenCanvas and transferToImageBitmap is supported by + // browser create ImageBitmap otherwise use more expensive canvas + // allocation. + if (_canvas != null && + js_util.hasProperty(_canvas!, 'transferToImageBitmap')) { + js_util.callMethod(_canvas!, 'getContext', ['webgl2']); + final Object? imageBitmap = js_util.callMethod(_canvas!, 'transferToImageBitmap', + []); + return imageBitmap; + } else { + final html.CanvasElement canvas = html.CanvasElement(width: _widthInPixels, height: _heightInPixels); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + drawImage(ctx, 0, 0); + return canvas; + } + } + + /// Returns image data in data url format. + String toImageUrl() { + final html.CanvasElement canvas = html.CanvasElement(width: _widthInPixels, height: _heightInPixels); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + drawImage(ctx, 0, 0); + final String dataUrl = canvas.toDataUrl(); + canvas.width = 0; + canvas.height = 0; + return dataUrl; + } +} + +// ignore: avoid_classes_with_only_static_members +/// Creates gl context from cached OffscreenCanvas for webgl rendering to image. +class GlContextCache { + static int _maxPixelWidth = 0; + static int _maxPixelHeight = 0; + static GlContext? _cachedContext; + static OffScreenCanvas? _offScreenCanvas; + + static void dispose() { + _maxPixelWidth = 0; + _maxPixelHeight = 0; + _cachedContext = null; + _offScreenCanvas?.dispose(); + } + + static GlContext? createGlContext(int widthInPixels, int heightInPixels) { + if (widthInPixels > _maxPixelWidth || heightInPixels > _maxPixelHeight) { + _cachedContext?.dispose(); + _cachedContext = null; + _offScreenCanvas = null; + _maxPixelWidth = math.max(_maxPixelWidth, widthInPixels); + _maxPixelHeight = math.max(_maxPixelHeight, widthInPixels); + } + _offScreenCanvas ??= OffScreenCanvas(widthInPixels, heightInPixels); + _cachedContext ??= GlContext(_offScreenCanvas!); + _cachedContext!.setViewportSize(widthInPixels, heightInPixels); + return _cachedContext; + } +} + +void setupVertexTransforms( + GlContext gl, + GlProgram glProgram, + double offsetX, + double offsetY, + double widthInPixels, + double heightInPixels, + Matrix4 transform) { + final Object transformUniform = + gl.getUniformLocation(glProgram.program, 'u_ctransform'); + final Matrix4 transformAtOffset = transform.clone() + ..translate(-offsetX, -offsetY); + gl.setUniformMatrix4fv(transformUniform, false, transformAtOffset.storage); + + // Set uniform to scale 0..width/height pixels coordinates to -1..1 + // clipspace range and flip the Y axis. + final Object resolution = gl.getUniformLocation(glProgram.program, 'u_scale'); + gl.setUniform4f(resolution, 2.0 / widthInPixels.toDouble(), + -2.0 / heightInPixels.toDouble(), 1, 1); + final Object shift = gl.getUniformLocation(glProgram.program, 'u_shift'); + gl.setUniform4f(shift, -1, 1, 0, 0); +} + +void setupTextureTransform( + GlContext gl, GlProgram glProgram, double offsetx, double offsety, double sx, double sy) { + final Object scalar = gl.getUniformLocation(glProgram.program, 'u_textransform'); + gl.setUniform4f(scalar, sx, sy, offsetx, offsety); +} + +void bufferVertexData(GlContext gl, Float32List positions, + double devicePixelRatio) { + if (devicePixelRatio == 1.0) { + gl.bufferData(positions, gl.kStaticDraw); + } else { + final int length = positions.length; + final Float32List scaledList = Float32List(length); + for (int i = 0; i < length; i++) { + scaledList[i] = positions[i] * devicePixelRatio; + } + gl.bufferData(scaledList, gl.kStaticDraw); + } +} + +dynamic tileModeToGlWrapping(GlContext gl, ui.TileMode tileMode) { + switch (tileMode) { + case ui.TileMode.clamp: + return gl.kClampToEdge; + case ui.TileMode.decal: + return gl.kClampToEdge; + case ui.TileMode.mirror: + return gl.kMirroredRepeat; + case ui.TileMode.repeated: + return gl.kRepeat; + } +} \ No newline at end of file diff --git a/lib/web_ui/lib/src/engine/html/surface.dart b/lib/web_ui/lib/src/engine/html/surface.dart index 68efb10a92556..ca2104adf3ab1 100644 --- a/lib/web_ui/lib/src/engine/html/surface.dart +++ b/lib/web_ui/lib/src/engine/html/surface.dart @@ -2,19 +2,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import '../frame_reference.dart'; +import '../onscreen_logging.dart'; +import '../semantics.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'picture.dart'; +import 'scene.dart'; +import 'surface_stats.dart'; /// When `true` prints statistics about what happened to the surface tree when /// it was composited. /// /// Also paints an on-screen overlay with the numbers visualized as a timeline. -const bool _debugExplainSurfaceStats = false; +const bool debugExplainSurfaceStats = false; /// When `true` shows an overlay that contains stats about canvas reuse. /// /// The overlay also includes a button to reset the stats. -const bool _debugShowCanvasReuseStats = false; +const bool debugShowCanvasReuseStats = false; /// When `true` renders the outlines of clip layers on the screen instead of /// clipping the contents. @@ -28,41 +39,44 @@ bool debugShowClipLayers = false; /// As we improve canvas utilization we should decrease this number. It is /// unlikely that we will hit 1.0, but something around 3.0 should be /// reasonable. -const double _kScreenPixelRatioWarningThreshold = 6.0; +const double kScreenPixelRatioWarningThreshold = 6.0; /// Performs any outstanding painting work enqueued by [PersistedPicture]s. void commitScene(PersistedScene scene) { - if (_paintQueue.isNotEmpty) { - if (_paintQueue.length > 1) { - // Sort paint requests in decreasing canvas size order. Paint requests - // attempt to reuse canvases. For efficiency we want the biggest pictures - // to find canvases before the smaller ones claim them. - _paintQueue.sort((_PaintRequest a, _PaintRequest b) { - final double aSize = a.canvasSize.height * a.canvasSize.width; - final double bSize = b.canvasSize.height * b.canvasSize.width; - return bSize.compareTo(aSize); - }); - } + if (paintQueue.isNotEmpty) { + try { + if (paintQueue.length > 1) { + // Sort paint requests in decreasing canvas size order. Paint requests + // attempt to reuse canvases. For efficiency we want the biggest pictures + // to find canvases before the smaller ones claim them. + paintQueue.sort((PaintRequest a, PaintRequest b) { + final double aSize = a.canvasSize.height * a.canvasSize.width; + final double bSize = b.canvasSize.height * b.canvasSize.width; + return bSize.compareTo(aSize); + }); + } - for (_PaintRequest request in _paintQueue) { - request.paintCallback(); + for (final PaintRequest request in paintQueue) { + request.paintCallback(); + } + } finally { + paintQueue = []; } - _paintQueue = <_PaintRequest>[]; } // After the update the retained surfaces are back to active. - if (_retainedSurfaces.isNotEmpty) { - for (int i = 0; i < _retainedSurfaces.length; i++) { - final PersistedSurface retainedSurface = _retainedSurfaces[i]; + if (retainedSurfaces.isNotEmpty) { + for (int i = 0; i < retainedSurfaces.length; i++) { + final PersistedSurface retainedSurface = retainedSurfaces[i]; assert(debugAssertSurfaceState( retainedSurface, PersistedSurfaceState.pendingRetention)); retainedSurface.state = PersistedSurfaceState.active; } - _retainedSurfaces = []; + retainedSurfaces = []; } - if (_debugExplainSurfaceStats) { - _debugPrintSurfaceStats(scene, _debugFrameNumber); - _debugRepaintSurfaceStatsOverlay(scene); + if (debugExplainSurfaceStats) { + debugPrintSurfaceStats(scene, debugFrameNumber); + debugRepaintSurfaceStatsOverlay(scene); } assert(() { @@ -75,16 +89,16 @@ void commitScene(PersistedScene scene) { return true; }()); - for (int i = 0; i < _frameReferences.length; i++) { - _frameReferences[i].value = null; + for (int i = 0; i < frameReferences.length; i++) { + frameReferences[i].value = null; } - _frameReferences = >[]; + frameReferences = >[]; - if (_debugExplainSurfaceStats) { - _surfaceStats = {}; + if (debugExplainSurfaceStats) { + surfaceStats = {}; } assert(() { - _debugFrameNumber++; + debugFrameNumber++; return true; }()); } @@ -166,7 +180,7 @@ class PersistedSurfaceException implements Exception { bool debugAssertSurfaceState( PersistedSurface surface, PersistedSurfaceState state1, [PersistedSurfaceState? state2, PersistedSurfaceState? state3]) { - final List validStates = [state1, state2, state3]; + final List validStates = [state1, state2, state3]; if (validStates.contains(surface.state)) { return true; @@ -185,7 +199,7 @@ bool debugAssertSurfaceState( abstract class PersistedSurface implements ui.EngineLayer { /// Creates a persisted surface. PersistedSurface(PersistedSurface? oldLayer) - : _oldLayer = FrameReference( + : _oldLayer = FrameReference( oldLayer != null && oldLayer.isActive ? oldLayer : null); /// The surface that is being updated using this surface. @@ -344,21 +358,22 @@ abstract class PersistedSurface implements ui.EngineLayer { /// such as on the very first frame. @mustCallSuper void build() { - if (rootElement != null) { - try { - throw ''; - } catch (_, stack) { - print( - 'Attempted to build a $runtimeType, but it already has an HTML element ${rootElement!.tagName}.'); - print(stack.toString().split('\n').take(20).join('\n')); + if (assertionsEnabled) { + final html.Element? existingElement = rootElement; + if (existingElement != null) { + throw PersistedSurfaceException( + this, + 'Attempted to build a $runtimeType, but it already has an HTML ' + 'element ${existingElement.tagName}.', + ); } } - assert(rootElement == null); assert(debugAssertSurfaceState(this, PersistedSurfaceState.created)); rootElement = createElement(); + assert(rootElement != null); applyWebkitClipFix(rootElement); - if (_debugExplainSurfaceStats) { - _surfaceStatsFor(this).allocatedDomNodeCount++; + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).allocatedDomNodeCount++; } apply(); state = PersistedSurfaceState.active; @@ -377,14 +392,15 @@ abstract class PersistedSurface implements ui.EngineLayer { PersistedSurfaceState.pendingUpdate)); assert(() { if (oldSurface.isPendingUpdate) { - final PersistedContainerSurface self = this as PersistedContainerSurface; + final PersistedContainerSurface self = + this as PersistedContainerSurface; assert(identical(self.oldLayer, oldSurface)); } return true; }()); rootElement = oldSurface.rootElement; - if (_debugExplainSurfaceStats) { - _surfaceStatsFor(this).reuseElementCount++; + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).reuseElementCount++; } // We took ownership of the old element. @@ -429,13 +445,13 @@ abstract class PersistedSurface implements ui.EngineLayer { // it is set to active state. We do not set the state to active // immediately. Otherwise, another surface could match on it and steal // this surface's DOM elements. - _retainedSurfaces.add(this); + retainedSurfaces.add(this); } if (assertionsEnabled) { rootElement!.setAttribute('flt-layer-state', 'retained'); } - if (_debugExplainSurfaceStats) { - _surfaceStatsFor(this).retainSurfaceCount++; + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).retainSurfaceCount++; } } @@ -466,6 +482,10 @@ abstract class PersistedSurface implements ui.EngineLayer { state = PersistedSurfaceState.released; } + @override + @mustCallSuper + void dispose() {} + @mustCallSuper void debugValidate(List validationErrors) { if (rootElement == null) { @@ -504,8 +524,7 @@ abstract class PersistedSurface implements ui.EngineLayer { /// transforms as well as this layer's transform (if any). /// /// The value is update by [recomputeTransformAndClip]. - Matrix4? get transform => _transform; - Matrix4? _transform; + Matrix4? transform; /// The intersection at this surface level. /// @@ -513,20 +532,16 @@ abstract class PersistedSurface implements ui.EngineLayer { /// the clip added by this layer (if any). /// /// The value is update by [recomputeTransformAndClip]. - ui.Rect? _projectedClip; + ui.Rect? projectedClip; /// Bounds of clipping performed by this layer. - ui.Rect? _localClipBounds; - // Cached inverse of transform on this node. Unlike transform, this - // Matrix only contains local transform (not chain multiplied since root). - Matrix4? _localTransformInverse; + ui.Rect? localClipBounds; /// The inverse of the local transform that this surface applies to its children. /// /// The default implementation is identity transform. Concrete /// implementations may override this getter to supply a different transform. - Matrix4? get localTransformInverse => - _localTransformInverse ??= Matrix4.identity(); + Matrix4? get localTransformInverse => null; /// Recomputes [transform] and [globalClip] fields. /// @@ -536,10 +551,9 @@ abstract class PersistedSurface implements ui.EngineLayer { /// /// This method is called by the [preroll] method. void recomputeTransformAndClip() { - _transform = parent!._transform; - _localClipBounds = null; - _localTransformInverse = null; - _projectedClip = null; + transform = parent!.transform; + localClipBounds = null; + projectedClip = null; } /// Performs computations before [build], [update], or [retain] are called. @@ -550,7 +564,7 @@ abstract class PersistedSurface implements ui.EngineLayer { /// /// This method recursively walks the surface tree calling `preroll` on all /// descendants. - void preroll() { + void preroll(PrerollSurfaceContext prerollContext) { recomputeTransformAndClip(); } @@ -565,7 +579,8 @@ abstract class PersistedSurface implements ui.EngineLayer { buffer.writeln('>'); debugPrintChildren(buffer, indent); if (rootElement != null) { - buffer.writeln('${' ' * indent}'); + buffer + .writeln('${' ' * indent}'); } else { buffer.writeln('${' ' * indent}'); } @@ -632,20 +647,19 @@ abstract class PersistedContainerSurface extends PersistedSurface { } @override - void preroll() { - super.preroll(); + void preroll(PrerollSurfaceContext prerollContext) { + super.preroll(prerollContext); final int length = _children.length; for (int i = 0; i < length; i += 1) { - _children[i].preroll(); + _children[i].preroll(prerollContext); } } @override void recomputeTransformAndClip() { - _transform = parent!._transform; - _localClipBounds = null; - _localTransformInverse = null; - _projectedClip = null; + transform = parent!.transform; + localClipBounds = null; + projectedClip = null; } @override @@ -665,7 +679,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { assert(oldLayer.rootElement != null); assert(debugAssertSurfaceState( oldLayer, PersistedSurfaceState.pendingUpdate)); - child.update(child.oldLayer as PersistedContainerSurface); + child.update(oldLayer as PersistedContainerSurface); } else { assert(debugAssertSurfaceState(child, PersistedSurfaceState.created)); assert(child.rootElement == null); @@ -770,7 +784,8 @@ abstract class PersistedContainerSurface extends PersistedSurface { newChild, PersistedSurfaceState.pendingRetention)); } else if (newChild is PersistedContainerSurface && newChild.oldLayer != null) { - final PersistedContainerSurface oldLayer = newChild.oldLayer as PersistedContainerSurface; + final PersistedContainerSurface oldLayer = + newChild.oldLayer! as PersistedContainerSurface; assert(debugAssertSurfaceState( oldLayer, PersistedSurfaceState.pendingUpdate)); newChild.update(oldLayer); @@ -834,7 +849,8 @@ abstract class PersistedContainerSurface extends PersistedSurface { assert(newChild.rootElement == null); assert(newChild.oldLayer!.rootElement != null); - final PersistedContainerSurface oldLayer = newChild.oldLayer as PersistedContainerSurface; + final PersistedContainerSurface oldLayer = + newChild.oldLayer! as PersistedContainerSurface; // Move the HTML node if necessary. if (oldLayer.rootElement!.parent != childContainer) { @@ -933,7 +949,8 @@ abstract class PersistedContainerSurface extends PersistedSurface { newChild, PersistedSurfaceState.pendingRetention)); } else if (newChild is PersistedContainerSurface && newChild.oldLayer != null) { - final PersistedContainerSurface oldLayer = newChild.oldLayer as PersistedContainerSurface; + final PersistedContainerSurface oldLayer = + newChild.oldLayer! as PersistedContainerSurface; isReparenting = oldLayer.rootElement!.parent != containerElement; matchedOldChild = oldLayer; assert(debugAssertSurfaceState( @@ -947,7 +964,8 @@ abstract class PersistedContainerSurface extends PersistedSurface { if (matchedOldChild != null) { assert(debugAssertSurfaceState( matchedOldChild, PersistedSurfaceState.active)); - isReparenting = matchedOldChild.rootElement!.parent != containerElement; + isReparenting = + matchedOldChild.rootElement!.parent != containerElement; newChild.update(matchedOldChild); assert(debugAssertSurfaceState( matchedOldChild, PersistedSurfaceState.released)); @@ -1015,7 +1033,8 @@ abstract class PersistedContainerSurface extends PersistedSurface { /// Performs the minimum number of DOM moves necessary to put all children in /// the right place in the DOM. void _insertChildDomNodes(List? indexMapNew, List indexMapOld) { - final List stationaryIndices = longestIncreasingSubsequence(indexMapOld); + final List stationaryIndices = + longestIncreasingSubsequence(indexMapOld); // Convert to stationary new indices for (int i = 0; i < stationaryIndices.length; i++) { @@ -1026,9 +1045,11 @@ abstract class PersistedContainerSurface extends PersistedSurface { final html.Element? containerElement = childContainer; for (int i = _children.length - 1; i >= 0; i -= 1) { final int indexInNew = indexMapNew!.indexOf(i); - final bool isStationary = indexInNew != -1 && stationaryIndices.contains(i); + final bool isStationary = + indexInNew != -1 && stationaryIndices.contains(i); final PersistedSurface child = _children[i]; - final html.HtmlElement childElement = child.rootElement as html.HtmlElement; + final html.HtmlElement childElement = + child.rootElement! as html.HtmlElement; assert(childElement != null); // ignore: unnecessary_null_comparison if (!isStationary) { if (refNode == null) { @@ -1088,7 +1109,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { for (int indexInOld = 0; indexInOld < oldChildCount; indexInOld += 1) { final PersistedSurface? oldChild = oldChildren[indexInOld]; final bool childAlreadyClaimed = oldChild == null; - if (childAlreadyClaimed || !newChild.canUpdateAsMatch(oldChild!)) { + if (childAlreadyClaimed || !newChild.canUpdateAsMatch(oldChild)) { continue; } allMatches.add(_PersistedSurfaceMatch( @@ -1202,3 +1223,13 @@ class _PersistedSurfaceMatch { } } } + +/// Data used during preroll to pass rendering hints efficiently to children +/// by optimizing (prevent parent lookups) and in cases like svg filters +/// drive the decision on whether canvas elements can be used to render. +class PrerollSurfaceContext { + /// Number of active color filters in parent surfaces. + int activeColorFilterCount = 0; + /// Number of active shader masks in parent surfaces. + int activeShaderMaskCount = 0; +} diff --git a/lib/web_ui/lib/src/engine/html/surface_stats.dart b/lib/web_ui/lib/src/engine/html/surface_stats.dart index 911a825ad03ab..73cc6e46dc396 100644 --- a/lib/web_ui/lib/src/engine/html/surface_stats.dart +++ b/lib/web_ui/lib/src/engine/html/surface_stats.dart @@ -2,30 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; + +import '../platform_dispatcher.dart'; +import 'bitmap_canvas.dart'; +import 'dom_canvas.dart'; +import 'picture.dart'; +import 'scene.dart'; +import 'surface.dart'; /// Surfaces that were retained this frame. /// /// Surfaces should be added to this list directly. Instead, if a surface needs /// to be retained call [_retainSurface]. -List get debugRetainedSurfaces => _retainedSurfaces; -List _retainedSurfaces = []; +List retainedSurfaces = []; /// Maps every surface currently active on the screen to debug statistics. -Map _surfaceStats = - {}; +Map surfaceStats = + {}; -List> _surfaceStatsTimeline = - >[]; +List> _surfaceStatsTimeline = + >[]; /// Returns debug statistics for the given [surface]. -_DebugSurfaceStats _surfaceStatsFor(PersistedSurface surface) { - if (!_debugExplainSurfaceStats) { +DebugSurfaceStats surfaceStatsFor(PersistedSurface surface) { + if (!debugExplainSurfaceStats) { throw Exception( - '_surfaceStatsFor is only available when _debugExplainSurfaceStats is set to true.'); + 'surfaceStatsFor is only available when debugExplainSurfaceStats is set to true.'); } - return _surfaceStats.putIfAbsent(surface, () => _DebugSurfaceStats(surface)); + return surfaceStats.putIfAbsent(surface, () => DebugSurfaceStats(surface)); } /// Compositor information collected for one frame useful for assessing the @@ -35,8 +41,8 @@ _DebugSurfaceStats _surfaceStatsFor(PersistedSurface surface) { /// /// For stats pertaining to a single surface the numeric counter fields are /// typically either 0 or 1. For aggregated stats, the numbers can be >1. -class _DebugSurfaceStats { - _DebugSurfaceStats(this.surface); +class DebugSurfaceStats { + DebugSurfaceStats(this.surface); /// The surface these stats are for, or `null` if these are aggregated stats. final PersistedSurface? surface; @@ -76,7 +82,7 @@ class _DebugSurfaceStats { int allocatedDomNodeCount = 0; /// Adds all counters of [oneSurfaceStats] into this object. - void aggregate(_DebugSurfaceStats oneSurfaceStats) { + void aggregate(DebugSurfaceStats oneSurfaceStats) { retainSurfaceCount += oneSurfaceStats.retainSurfaceCount; reuseElementCount += oneSurfaceStats.reuseElementCount; paintCount += oneSurfaceStats.paintCount; @@ -90,14 +96,14 @@ class _DebugSurfaceStats { html.CanvasRenderingContext2D? _debugSurfaceStatsOverlayCtx; -void _debugRepaintSurfaceStatsOverlay(PersistedScene scene) { +void debugRepaintSurfaceStatsOverlay(PersistedScene scene) { final int overlayWidth = html.window.innerWidth!; const int rowHeight = 30; const int rowCount = 4; const int overlayHeight = rowHeight * rowCount; const int strokeWidth = 2; - _surfaceStatsTimeline.add(_surfaceStats); + _surfaceStatsTimeline.add(surfaceStats); while (_surfaceStatsTimeline.length > (overlayWidth / strokeWidth)) { _surfaceStatsTimeline.removeAt(0); @@ -125,23 +131,23 @@ void _debugRepaintSurfaceStatsOverlay(PersistedScene scene) { ..fill(); final double physicalScreenWidth = - html.window.innerWidth! * EngineWindow.browserDevicePixelRatio; + html.window.innerWidth! * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicalScreenHeight = - html.window.innerHeight! * EngineWindow.browserDevicePixelRatio; + html.window.innerHeight! * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicsScreenPixelCount = physicalScreenWidth * physicalScreenHeight; final int totalDomNodeCount = scene.rootElement!.querySelectorAll('*').length; for (int i = 0; i < _surfaceStatsTimeline.length; i++) { - final Map statsMap = + final Map statsMap = _surfaceStatsTimeline[i]; - final _DebugSurfaceStats totals = _DebugSurfaceStats(null); + final DebugSurfaceStats totals = DebugSurfaceStats(null); int pixelCount = 0; - for (_DebugSurfaceStats oneSurfaceStats in statsMap.values) { + for (final DebugSurfaceStats oneSurfaceStats in statsMap.values) { totals.aggregate(oneSurfaceStats); if (oneSurfaceStats.surface is PersistedPicture) { - final PersistedPicture picture = oneSurfaceStats.surface as PersistedPicture; + final PersistedPicture picture = oneSurfaceStats.surface! as PersistedPicture; pixelCount += picture.bitmapPixelCount; } } @@ -211,7 +217,7 @@ void _debugRepaintSurfaceStatsOverlay(PersistedScene scene) { } /// Prints debug statistics for the current frame to the console. -void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { +void debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { int pictureCount = 0; int paintCount = 0; @@ -230,7 +236,7 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { int totalAllocatedDomNodeCount = 0; void countReusesRecursively(PersistedSurface surface) { - final _DebugSurfaceStats stats = _surfaceStatsFor(surface); + final DebugSurfaceStats stats = surfaceStatsFor(surface); assert(stats != null); // ignore: unnecessary_null_comparison surfaceRetainCount += stats.retainSurfaceCount; @@ -241,12 +247,12 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { pictureCount += 1; paintCount += stats.paintCount; - if (surface._canvas is DomCanvas) { + if (surface.canvas is DomCanvas) { domCanvasCount++; domPaintCount += stats.paintCount; } - if (surface._canvas is BitmapCanvas) { + if (surface.canvas is BitmapCanvas) { bitmapCanvasCount++; bitmapPaintCount += stats.paintCount; } @@ -280,7 +286,7 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { ..writeln(' Reused: $bitmapReuseCount') ..writeln(' Allocated: $bitmapAllocationCount') ..writeln(' Allocated pixels: $bitmapPixelsAllocated') - ..writeln(' Available for reuse: ${_recycledCanvases.length}'); + ..writeln(' Available for reuse: ${recycledCanvases.length}'); // A microtask will fire after the DOM is flushed, letting us probe into // actual tags. @@ -296,9 +302,9 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { return pixels; }).fold(0, (int total, int pixels) => total + pixels); final double physicalScreenWidth = - html.window.innerWidth! * EngineWindow.browserDevicePixelRatio; + html.window.innerWidth! * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicalScreenHeight = - html.window.innerHeight! * EngineWindow.browserDevicePixelRatio; + html.window.innerHeight! * EnginePlatformDispatcher.browserDevicePixelRatio; final double physicsScreenPixelCount = physicalScreenWidth * physicalScreenHeight; final double screenPixelRatio = pixelCount / physicsScreenPixelCount; @@ -312,7 +318,7 @@ void _debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { ..writeln(' Pixels: $canvasPixelDescription; $screenDescription)') ..writeln('-----------------------------------------------------------'); final bool screenPixelRatioTooHigh = - screenPixelRatio > _kScreenPixelRatioWarningThreshold; + screenPixelRatio > kScreenPixelRatioWarningThreshold; if (screenPixelRatioTooHigh) { print( 'WARNING: pixel/screen ratio too high (${screenPixelRatio.toStringAsFixed(2)}x)'); diff --git a/lib/web_ui/lib/src/engine/html/transform.dart b/lib/web_ui/lib/src/engine/html/transform.dart index f8ff2d3781bc8..44135a28cae61 100644 --- a/lib/web_ui/lib/src/engine/html/transform.dart +++ b/lib/web_ui/lib/src/engine/html/transform.dart @@ -2,53 +2,73 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../dom_renderer.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'surface.dart'; /// A surface that transforms its children using CSS transform. class PersistedTransform extends PersistedContainerSurface implements ui.TransformEngineLayer { - PersistedTransform(PersistedTransform? oldLayer, this.matrix4) + PersistedTransform(PersistedTransform? oldLayer, this._matrixStorage) : super(oldLayer); - final Float32List matrix4; + /// The storage representing the transform of this surface. + final Float32List _matrixStorage; + + /// The matrix representing the transform of this surface. + Matrix4 get matrix4 => _matrix4 ??= Matrix4.fromFloat32List(_matrixStorage); + Matrix4? _matrix4; @override void recomputeTransformAndClip() { - _transform = parent!._transform!.multiplied(Matrix4.fromFloat32List(matrix4)); - _localTransformInverse = null; - _projectedClip = null; + transform = parent!.transform!.multiplied(matrix4); + projectedClip = null; } + /// Cached inverse of transform on this node. Unlike [transform], this + /// Matrix only contains local transform (not chain multiplied since root). + Matrix4? _localTransformInverse; + @override Matrix4? get localTransformInverse { - _localTransformInverse ??= - Matrix4.tryInvert(Matrix4.fromFloat32List(matrix4)); + _localTransformInverse ??= Matrix4.tryInvert(matrix4); return _localTransformInverse; } @override html.Element createElement() { - return defaultCreateElement('flt-transform') - ..style.transformOrigin = '0 0 0'; + final html.Element element = domRenderer.createElement('flt-transform'); + DomRenderer.setElementStyle(element, 'position', 'absolute'); + DomRenderer.setElementStyle(element, 'transform-origin', '0 0 0'); + return element; } @override void apply() { - rootElement!.style.transform = float64ListToCssTransform(matrix4); + rootElement!.style.transform = float64ListToCssTransform(_matrixStorage); } @override void update(PersistedTransform oldSurface) { super.update(oldSurface); - if (identical(oldSurface.matrix4, matrix4)) { + if (identical(oldSurface._matrixStorage, _matrixStorage)) { + // The matrix storage is identical, so we can copy the matrices from the + // old surface to avoid recomputing them. + _matrix4 = oldSurface._matrix4; + _localTransformInverse = oldSurface._localTransformInverse; return; } bool matrixChanged = false; - for (int i = 0; i < matrix4.length; i++) { - if (matrix4[i] != oldSurface.matrix4[i]) { + for (int i = 0; i < _matrixStorage.length; i++) { + if (_matrixStorage[i] != oldSurface._matrixStorage[i]) { matrixChanged = true; break; } @@ -56,6 +76,11 @@ class PersistedTransform extends PersistedContainerSurface if (matrixChanged) { apply(); + } else { + // The matrix storage hasn't changed, so we can copy the matrices from the + // old surface to avoid recomputing them. + _matrix4 = oldSurface._matrix4; + _localTransformInverse = oldSurface._localTransformInverse; } } } diff --git a/lib/web_ui/lib/src/engine/html_image_codec.dart b/lib/web_ui/lib/src/engine/html_image_codec.dart index 3b434685f2bf6..01c394354d7c6 100644 --- a/lib/web_ui/lib/src/engine/html_image_codec.dart +++ b/lib/web_ui/lib/src/engine/html_image_codec.dart @@ -2,14 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:typed_data'; -final bool _supportsDecode = js_util.getProperty( - js_util.getProperty( - js_util.getProperty(html.window, 'Image'), 'prototype'), - 'decode') != - null; +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'util.dart'; + +Object? get _jsImageDecodeFunction => js_util.getProperty( + js_util.getProperty( + js_util.getProperty(html.window, 'Image') as Object, + 'prototype', + ) as Object, + 'decode', +); +final bool _supportsDecode = _jsImageDecodeFunction != null; typedef WebOnlyImageCodecChunkCallback = void Function( int cumulativeBytesLoaded, int expectedTotalBytes); @@ -39,10 +49,20 @@ class HtmlCodec implements ui.Codec { js_util.setProperty(imgElement, 'decoding', 'async'); imgElement.decode().then((dynamic _) { chunkCallback?.call(100, 100); + int naturalWidth = imgElement.naturalWidth; + int naturalHeight = imgElement.naturalHeight; + // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=700533. + if (naturalWidth == 0 && naturalHeight == 0 && ( + browserEngine == BrowserEngine.firefox || + browserEngine == BrowserEngine.ie11)) { + const int kDefaultImageSizeFallback = 300; + naturalWidth = kDefaultImageSizeFallback; + naturalHeight = kDefaultImageSizeFallback; + } final HtmlImage image = HtmlImage( imgElement, - imgElement.naturalWidth, - imgElement.naturalHeight, + naturalWidth, + naturalHeight, ); completer.complete(SingleFrameInfo(image)); }).catchError((dynamic e) { @@ -57,7 +77,7 @@ class HtmlCodec implements ui.Codec { return completer.future; } - void _decodeUsingOnLoad(Completer completer) { + void _decodeUsingOnLoad(Completer completer) { StreamSubscription? loadSubscription; late StreamSubscription errorSubscription; final html.ImageElement imgElement = html.ImageElement(); @@ -116,12 +136,25 @@ class HtmlImage implements ui.Image { bool _requiresClone = false; HtmlImage(this.imgElement, this.width, this.height); + bool _disposed = false; @override void dispose() { // Do nothing. The codec that owns this image should take care of // releasing the object url. + if (assertionsEnabled) { + _disposed = true; + } + } + + @override + bool get debugDisposed { + if (assertionsEnabled) { + return _disposed; + } + return throw StateError('Image.debugDisposed is only available when asserts are enabled.'); } + @override ui.Image clone() => this; @@ -146,13 +179,13 @@ class HtmlImage implements ui.Image { final html.CanvasRenderingContext2D ctx = canvas.context2D; ctx.drawImage(imgElement, 0, 0); final html.ImageData imageData = ctx.getImageData(0, 0, width, height); - return Future.value(imageData.data.buffer.asByteData()); + return Future.value(imageData.data.buffer.asByteData()); } if (imgElement.src?.startsWith('data:') == true) { - final data = UriData.fromUri(Uri.parse(imgElement.src!)); - return Future.value(data.contentAsBytes().buffer.asByteData()); + final UriData data = UriData.fromUri(Uri.parse(imgElement.src!)); + return Future.value(data.contentAsBytes().buffer.asByteData()); } else { - return Future.value(null); + return Future.value(null); } } diff --git a/lib/web_ui/lib/src/engine/key_map.dart b/lib/web_ui/lib/src/engine/key_map.dart new file mode 100644 index 0000000000000..5841e8adc5500 --- /dev/null +++ b/lib/web_ui/lib/src/engine/key_map.dart @@ -0,0 +1,628 @@ +// 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. + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/web_key_map_dart.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +/// Maps Web KeyboardEvent keys to the matching LogicalKeyboardKey id. +const Map kWebToLogicalKey = { + 'AVRInput': 0x00100000d08, + 'AVRPower': 0x00100000d09, + 'Accel': 0x00100000101, + 'Accept': 0x00100000501, + 'Again': 0x00100000502, + 'AllCandidates': 0x00100000701, + 'Alphanumeric': 0x00100000702, + 'AltGraph': 0x00100000103, + 'AppSwitch': 0x00100001001, + 'ArrowDown': 0x00100000301, + 'ArrowLeft': 0x00100000302, + 'ArrowRight': 0x00100000303, + 'ArrowUp': 0x00100000304, + 'Attn': 0x00100000503, + 'AudioBalanceLeft': 0x00100000d01, + 'AudioBalanceRight': 0x00100000d02, + 'AudioBassBoostDown': 0x00100000d03, + 'AudioBassBoostToggle': 0x00100000e02, + 'AudioBassBoostUp': 0x00100000d04, + 'AudioFaderFront': 0x00100000d05, + 'AudioFaderRear': 0x00100000d06, + 'AudioSurroundModeNext': 0x00100000d07, + 'AudioTrebleDown': 0x00100000e04, + 'AudioTrebleUp': 0x00100000e05, + 'AudioVolumeDown': 0x00100000a0f, + 'AudioVolumeMute': 0x00100000a11, + 'AudioVolumeUp': 0x00100000a10, + 'Backspace': 0x00100000008, + 'BrightnessDown': 0x00100000601, + 'BrightnessUp': 0x00100000602, + 'BrowserBack': 0x00100000c01, + 'BrowserFavorites': 0x00100000c02, + 'BrowserForward': 0x00100000c03, + 'BrowserHome': 0x00100000c04, + 'BrowserRefresh': 0x00100000c05, + 'BrowserSearch': 0x00100000c06, + 'BrowserStop': 0x00100000c07, + 'Call': 0x00100001002, + 'Camera': 0x00100000603, + 'CameraFocus': 0x00100001003, + 'Cancel': 0x00100000504, + 'CapsLock': 0x00100000104, + 'ChannelDown': 0x00100000d0a, + 'ChannelUp': 0x00100000d0b, + 'Clear': 0x00100000401, + 'Close': 0x00100000a01, + 'ClosedCaptionToggle': 0x00100000d12, + 'CodeInput': 0x00100000703, + 'ColorF0Red': 0x00100000d0c, + 'ColorF1Green': 0x00100000d0d, + 'ColorF2Yellow': 0x00100000d0e, + 'ColorF3Blue': 0x00100000d0f, + 'ColorF4Grey': 0x00100000d10, + 'ColorF5Brown': 0x00100000d11, + 'Compose': 0x00100000704, + 'ContextMenu': 0x00100000505, + 'Convert': 0x00100000705, + 'Copy': 0x00100000402, + 'CrSel': 0x00100000403, + 'Cut': 0x00100000404, + 'DVR': 0x00100000d4f, + 'Delete': 0x0010000007f, + 'Dimmer': 0x00100000d13, + 'DisplaySwap': 0x00100000d14, + 'Eisu': 0x00100000714, + 'Eject': 0x00100000604, + 'End': 0x00100000305, + 'EndCall': 0x00100001004, + 'Enter': 0x0010000000d, + 'EraseEof': 0x00100000405, + 'Escape': 0x0010000001b, + 'ExSel': 0x00100000406, + 'Execute': 0x00100000506, + 'Exit': 0x00100000d15, + 'F1': 0x00100000801, + 'F10': 0x0010000080a, + 'F11': 0x0010000080b, + 'F12': 0x0010000080c, + 'F13': 0x0010000080d, + 'F14': 0x0010000080e, + 'F15': 0x0010000080f, + 'F16': 0x00100000810, + 'F17': 0x00100000811, + 'F18': 0x00100000812, + 'F19': 0x00100000813, + 'F2': 0x00100000802, + 'F20': 0x00100000814, + 'F21': 0x00100000815, + 'F22': 0x00100000816, + 'F23': 0x00100000817, + 'F24': 0x00100000818, + 'F3': 0x00100000803, + 'F4': 0x00100000804, + 'F5': 0x00100000805, + 'F6': 0x00100000806, + 'F7': 0x00100000807, + 'F8': 0x00100000808, + 'F9': 0x00100000809, + 'FavoriteClear0': 0x00100000d16, + 'FavoriteClear1': 0x00100000d17, + 'FavoriteClear2': 0x00100000d18, + 'FavoriteClear3': 0x00100000d19, + 'FavoriteRecall0': 0x00100000d1a, + 'FavoriteRecall1': 0x00100000d1b, + 'FavoriteRecall2': 0x00100000d1c, + 'FavoriteRecall3': 0x00100000d1d, + 'FavoriteStore0': 0x00100000d1e, + 'FavoriteStore1': 0x00100000d1f, + 'FavoriteStore2': 0x00100000d20, + 'FavoriteStore3': 0x00100000d21, + 'FinalMode': 0x00100000706, + 'Find': 0x00100000507, + 'Fn': 0x00100000106, + 'FnLock': 0x00100000107, + 'GoBack': 0x00100001005, + 'GoHome': 0x00100001006, + 'GroupFirst': 0x00100000707, + 'GroupLast': 0x00100000708, + 'GroupNext': 0x00100000709, + 'GroupPrevious': 0x0010000070a, + 'Guide': 0x00100000d22, + 'GuideNextDay': 0x00100000d23, + 'GuidePreviousDay': 0x00100000d24, + 'HangulMode': 0x00100000711, + 'HanjaMode': 0x00100000712, + 'Hankaku': 0x00100000715, + 'HeadsetHook': 0x00100001007, + 'Help': 0x00100000508, + 'Hibernate': 0x00100000609, + 'Hiragana': 0x00100000716, + 'HiraganaKatakana': 0x00100000717, + 'Home': 0x00100000306, + 'Hyper': 0x00100000108, + 'Info': 0x00100000d25, + 'Insert': 0x00100000407, + 'InstantReplay': 0x00100000d26, + 'JunjaMode': 0x00100000713, + 'KanaMode': 0x00100000718, + 'KanjiMode': 0x00100000719, + 'Katakana': 0x0010000071a, + 'Key11': 0x00100001201, + 'Key12': 0x00100001202, + 'LastNumberRedial': 0x00100001008, + 'LaunchApplication1': 0x00100000b06, + 'LaunchApplication2': 0x00100000b01, + 'LaunchAssistant': 0x00100000b0e, + 'LaunchCalendar': 0x00100000b02, + 'LaunchContacts': 0x00100000b0c, + 'LaunchControlPanel': 0x00100000b0f, + 'LaunchMail': 0x00100000b03, + 'LaunchMediaPlayer': 0x00100000b04, + 'LaunchMusicPlayer': 0x00100000b05, + 'LaunchPhone': 0x00100000b0d, + 'LaunchScreenSaver': 0x00100000b07, + 'LaunchSpreadsheet': 0x00100000b08, + 'LaunchWebBrowser': 0x00100000b09, + 'LaunchWebCam': 0x00100000b0a, + 'LaunchWordProcessor': 0x00100000b0b, + 'Link': 0x00100000d27, + 'ListProgram': 0x00100000d28, + 'LiveContent': 0x00100000d29, + 'Lock': 0x00100000d2a, + 'LogOff': 0x00100000605, + 'MailForward': 0x00100000a02, + 'MailReply': 0x00100000a03, + 'MailSend': 0x00100000a04, + 'MannerMode': 0x0010000100a, + 'MediaApps': 0x00100000d2b, + 'MediaAudioTrack': 0x00100000d50, + 'MediaClose': 0x00100000d5b, + 'MediaFastForward': 0x00100000d2c, + 'MediaLast': 0x00100000d2d, + 'MediaPause': 0x00100000d2e, + 'MediaPlay': 0x00100000d2f, + 'MediaPlayPause': 0x00100000a05, + 'MediaRecord': 0x00100000d30, + 'MediaRewind': 0x00100000d31, + 'MediaSkip': 0x00100000d32, + 'MediaSkipBackward': 0x00100000d51, + 'MediaSkipForward': 0x00100000d52, + 'MediaStepBackward': 0x00100000d53, + 'MediaStepForward': 0x00100000d54, + 'MediaStop': 0x00100000a07, + 'MediaTopMenu': 0x00100000d55, + 'MediaTrackNext': 0x00100000a08, + 'MediaTrackPrevious': 0x00100000a09, + 'MicrophoneToggle': 0x00100000e06, + 'MicrophoneVolumeDown': 0x00100000e07, + 'MicrophoneVolumeMute': 0x00100000e09, + 'MicrophoneVolumeUp': 0x00100000e08, + 'ModeChange': 0x0010000070b, + 'NavigateIn': 0x00100000d56, + 'NavigateNext': 0x00100000d57, + 'NavigateOut': 0x00100000d58, + 'NavigatePrevious': 0x00100000d59, + 'New': 0x00100000a0a, + 'NextCandidate': 0x0010000070c, + 'NextFavoriteChannel': 0x00100000d33, + 'NextUserProfile': 0x00100000d34, + 'NonConvert': 0x0010000070d, + 'Notification': 0x00100001009, + 'NumLock': 0x0010000010a, + 'OnDemand': 0x00100000d35, + 'Open': 0x00100000a0b, + 'PageDown': 0x00100000307, + 'PageUp': 0x00100000308, + 'Pairing': 0x00100000d5a, + 'Paste': 0x00100000408, + 'Pause': 0x00100000509, + 'PinPDown': 0x00100000d36, + 'PinPMove': 0x00100000d37, + 'PinPToggle': 0x00100000d38, + 'PinPUp': 0x00100000d39, + 'Play': 0x0010000050a, + 'PlaySpeedDown': 0x00100000d3a, + 'PlaySpeedReset': 0x00100000d3b, + 'PlaySpeedUp': 0x00100000d3c, + 'Power': 0x00100000606, + 'PowerOff': 0x00100000607, + 'PreviousCandidate': 0x0010000070e, + 'Print': 0x00100000a0c, + 'PrintScreen': 0x00100000608, + 'Process': 0x0010000070f, + 'Props': 0x0010000050b, + 'RandomToggle': 0x00100000d3d, + 'RcLowBattery': 0x00100000d3e, + 'RecordSpeedNext': 0x00100000d3f, + 'Redo': 0x00100000409, + 'RfBypass': 0x00100000d40, + 'Romaji': 0x0010000071b, + 'STBInput': 0x00100000d45, + 'STBPower': 0x00100000d46, + 'Save': 0x00100000a0d, + 'ScanChannelsToggle': 0x00100000d41, + 'ScreenModeNext': 0x00100000d42, + 'ScrollLock': 0x0010000010c, + 'Select': 0x0010000050c, + 'Settings': 0x00100000d43, + 'ShiftLevel5': 0x00100000111, + 'SingleCandidate': 0x00100000710, + 'Soft1': 0x00100000901, + 'Soft2': 0x00100000902, + 'Soft3': 0x00100000903, + 'Soft4': 0x00100000904, + 'Soft5': 0x00100000905, + 'Soft6': 0x00100000906, + 'Soft7': 0x00100000907, + 'Soft8': 0x00100000908, + 'SpeechCorrectionList': 0x00100000f01, + 'SpeechInputToggle': 0x00100000f02, + 'SpellCheck': 0x00100000a0e, + 'SplitScreenToggle': 0x00100000d44, + 'Standby': 0x0010000060a, + 'Subtitle': 0x00100000d47, + 'Super': 0x0010000010e, + 'Symbol': 0x0010000010f, + 'SymbolLock': 0x00100000110, + 'TV': 0x00100000d49, + 'TV3DMode': 0x00100001101, + 'TVAntennaCable': 0x00100001102, + 'TVAudioDescription': 0x00100001103, + 'TVAudioDescriptionMixDown': 0x00100001104, + 'TVAudioDescriptionMixUp': 0x00100001105, + 'TVContentsMenu': 0x00100001106, + 'TVDataService': 0x00100001107, + 'TVInput': 0x00100000d4a, + 'TVInputComponent1': 0x00100001108, + 'TVInputComponent2': 0x00100001109, + 'TVInputComposite1': 0x0010000110a, + 'TVInputComposite2': 0x0010000110b, + 'TVInputHDMI1': 0x0010000110c, + 'TVInputHDMI2': 0x0010000110d, + 'TVInputHDMI3': 0x0010000110e, + 'TVInputHDMI4': 0x0010000110f, + 'TVInputVGA1': 0x00100001110, + 'TVMediaContext': 0x00100001111, + 'TVNetwork': 0x00100001112, + 'TVNumberEntry': 0x00100001113, + 'TVPower': 0x00100000d4b, + 'TVRadioService': 0x00100001114, + 'TVSatellite': 0x00100001115, + 'TVSatelliteBS': 0x00100001116, + 'TVSatelliteCS': 0x00100001117, + 'TVSatelliteToggle': 0x00100001118, + 'TVTerrestrialAnalog': 0x00100001119, + 'TVTerrestrialDigital': 0x0010000111a, + 'TVTimer': 0x0010000111b, + 'Tab': 0x00100000009, + 'Teletext': 0x00100000d48, + 'Undo': 0x0010000040a, + 'Unidentified': 0x00100000001, + 'VideoModeNext': 0x00100000d4c, + 'VoiceDial': 0x0010000100b, + 'WakeUp': 0x0010000060b, + 'Wink': 0x00100000d4d, + 'Zenkaku': 0x0010000071c, + 'ZenkakuHankaku': 0x0010000071d, + 'ZoomIn': 0x0010000050d, + 'ZoomOut': 0x0010000050e, + 'ZoomToggle': 0x00100000d4e, +}; + +/// Maps Web KeyboardEvent codes to the matching PhysicalKeyboardKey USB HID code. +const Map kWebToPhysicalKey = { + 'Abort': 0x0007009b, // abort + 'Again': 0x00070079, // again + 'AltLeft': 0x000700e2, // altLeft + 'AltRight': 0x000700e6, // altRight + 'ArrowDown': 0x00070051, // arrowDown + 'ArrowLeft': 0x00070050, // arrowLeft + 'ArrowRight': 0x0007004f, // arrowRight + 'ArrowUp': 0x00070052, // arrowUp + 'AudioVolumeDown': 0x00070081, // audioVolumeDown + 'AudioVolumeMute': 0x0007007f, // audioVolumeMute + 'AudioVolumeUp': 0x00070080, // audioVolumeUp + 'Backquote': 0x00070035, // backquote + 'Backslash': 0x00070031, // backslash + 'Backspace': 0x0007002a, // backspace + 'BassBoost': 0x000c00e5, // bassBoost + 'BracketLeft': 0x0007002f, // bracketLeft + 'BracketRight': 0x00070030, // bracketRight + 'BrightnessAuto': 0x000c0075, // brightnessAuto + 'BrightnessDown': 0x000c0070, // brightnessDown + 'BrightnessMaximum': 0x000c0074, // brightnessMaximum + 'BrightnessMinimum': 0x000c0073, // brightnessMinimum + 'BrightnessToggle': 0x000c0072, // brightnessToggle + 'BrightnessUp': 0x000c006f, // brightnessUp + 'BrowserBack': 0x000c0224, // browserBack + 'BrowserFavorites': 0x000c022a, // browserFavorites + 'BrowserForward': 0x000c0225, // browserForward + 'BrowserHome': 0x000c0223, // browserHome + 'BrowserRefresh': 0x000c0227, // browserRefresh + 'BrowserSearch': 0x000c0221, // browserSearch + 'BrowserStop': 0x000c0226, // browserStop + 'CapsLock': 0x00070039, // capsLock + 'ChannelDown': 0x000c009d, // channelDown + 'ChannelUp': 0x000c009c, // channelUp + 'Close': 0x000c0203, // close + 'ClosedCaptionToggle': 0x000c0061, // closedCaptionToggle + 'Comma': 0x00070036, // comma + 'ContextMenu': 0x00070065, // contextMenu + 'ControlLeft': 0x000700e0, // controlLeft + 'ControlRight': 0x000700e4, // controlRight + 'Convert': 0x0007008a, // convert + 'Copy': 0x0007007c, // copy + 'Cut': 0x0007007b, // cut + 'Delete': 0x0007004c, // delete + 'Digit0': 0x00070027, // digit0 + 'Digit1': 0x0007001e, // digit1 + 'Digit2': 0x0007001f, // digit2 + 'Digit3': 0x00070020, // digit3 + 'Digit4': 0x00070021, // digit4 + 'Digit5': 0x00070022, // digit5 + 'Digit6': 0x00070023, // digit6 + 'Digit7': 0x00070024, // digit7 + 'Digit8': 0x00070025, // digit8 + 'Digit9': 0x00070026, // digit9 + 'DisplayToggleIntExt': 0x000100b5, // displayToggleIntExt + 'Eject': 0x000c00b8, // eject + 'End': 0x0007004d, // end + 'Enter': 0x00070028, // enter + 'Equal': 0x0007002e, // equal + 'Escape': 0x00070029, // escape + 'Exit': 0x000c0094, // exit + 'F1': 0x0007003a, // f1 + 'F10': 0x00070043, // f10 + 'F11': 0x00070044, // f11 + 'F12': 0x00070045, // f12 + 'F13': 0x00070068, // f13 + 'F14': 0x00070069, // f14 + 'F15': 0x0007006a, // f15 + 'F16': 0x0007006b, // f16 + 'F17': 0x0007006c, // f17 + 'F18': 0x0007006d, // f18 + 'F19': 0x0007006e, // f19 + 'F2': 0x0007003b, // f2 + 'F20': 0x0007006f, // f20 + 'F21': 0x00070070, // f21 + 'F22': 0x00070071, // f22 + 'F23': 0x00070072, // f23 + 'F24': 0x00070073, // f24 + 'F3': 0x0007003c, // f3 + 'F4': 0x0007003d, // f4 + 'F5': 0x0007003e, // f5 + 'F6': 0x0007003f, // f6 + 'F7': 0x00070040, // f7 + 'F8': 0x00070041, // f8 + 'F9': 0x00070042, // f9 + 'Find': 0x0007007e, // find + 'Fn': 0x00000012, // fn + 'FnLock': 0x00000013, // fnLock + 'GameButton1': 0x0005ff01, // gameButton1 + 'GameButton10': 0x0005ff0a, // gameButton10 + 'GameButton11': 0x0005ff0b, // gameButton11 + 'GameButton12': 0x0005ff0c, // gameButton12 + 'GameButton13': 0x0005ff0d, // gameButton13 + 'GameButton14': 0x0005ff0e, // gameButton14 + 'GameButton15': 0x0005ff0f, // gameButton15 + 'GameButton16': 0x0005ff10, // gameButton16 + 'GameButton2': 0x0005ff02, // gameButton2 + 'GameButton3': 0x0005ff03, // gameButton3 + 'GameButton4': 0x0005ff04, // gameButton4 + 'GameButton5': 0x0005ff05, // gameButton5 + 'GameButton6': 0x0005ff06, // gameButton6 + 'GameButton7': 0x0005ff07, // gameButton7 + 'GameButton8': 0x0005ff08, // gameButton8 + 'GameButton9': 0x0005ff09, // gameButton9 + 'GameButtonA': 0x0005ff11, // gameButtonA + 'GameButtonB': 0x0005ff12, // gameButtonB + 'GameButtonC': 0x0005ff13, // gameButtonC + 'GameButtonLeft1': 0x0005ff14, // gameButtonLeft1 + 'GameButtonLeft2': 0x0005ff15, // gameButtonLeft2 + 'GameButtonMode': 0x0005ff16, // gameButtonMode + 'GameButtonRight1': 0x0005ff17, // gameButtonRight1 + 'GameButtonRight2': 0x0005ff18, // gameButtonRight2 + 'GameButtonSelect': 0x0005ff19, // gameButtonSelect + 'GameButtonStart': 0x0005ff1a, // gameButtonStart + 'GameButtonThumbLeft': 0x0005ff1b, // gameButtonThumbLeft + 'GameButtonThumbRight': 0x0005ff1c, // gameButtonThumbRight + 'GameButtonX': 0x0005ff1d, // gameButtonX + 'GameButtonY': 0x0005ff1e, // gameButtonY + 'GameButtonZ': 0x0005ff1f, // gameButtonZ + 'Help': 0x00070075, // help + 'Home': 0x0007004a, // home + 'Hyper': 0x00000010, // hyper + 'Info': 0x000c0060, // info + 'Insert': 0x00070049, // insert + 'IntlBackslash': 0x00070064, // intlBackslash + 'IntlRo': 0x00070087, // intlRo + 'IntlYen': 0x00070089, // intlYen + 'KanaMode': 0x00070088, // kanaMode + 'KbdIllumDown': 0x000c007a, // kbdIllumDown + 'KbdIllumUp': 0x000c0079, // kbdIllumUp + 'KeyA': 0x00070004, // keyA + 'KeyB': 0x00070005, // keyB + 'KeyC': 0x00070006, // keyC + 'KeyD': 0x00070007, // keyD + 'KeyE': 0x00070008, // keyE + 'KeyF': 0x00070009, // keyF + 'KeyG': 0x0007000a, // keyG + 'KeyH': 0x0007000b, // keyH + 'KeyI': 0x0007000c, // keyI + 'KeyJ': 0x0007000d, // keyJ + 'KeyK': 0x0007000e, // keyK + 'KeyL': 0x0007000f, // keyL + 'KeyM': 0x00070010, // keyM + 'KeyN': 0x00070011, // keyN + 'KeyO': 0x00070012, // keyO + 'KeyP': 0x00070013, // keyP + 'KeyQ': 0x00070014, // keyQ + 'KeyR': 0x00070015, // keyR + 'KeyS': 0x00070016, // keyS + 'KeyT': 0x00070017, // keyT + 'KeyU': 0x00070018, // keyU + 'KeyV': 0x00070019, // keyV + 'KeyW': 0x0007001a, // keyW + 'KeyX': 0x0007001b, // keyX + 'KeyY': 0x0007001c, // keyY + 'KeyZ': 0x0007001d, // keyZ + 'KeyboardLayoutSelect': 0x000c029d, // keyboardLayoutSelect + 'Lang1': 0x00070090, // lang1 + 'Lang2': 0x00070091, // lang2 + 'Lang3': 0x00070092, // lang3 + 'Lang4': 0x00070093, // lang4 + 'Lang5': 0x00070094, // lang5 + 'LaunchApp1': 0x000c0194, // launchApp1 + 'LaunchApp2': 0x000c0192, // launchApp2 + 'LaunchAssistant': 0x000c01cb, // launchAssistant + 'LaunchAudioBrowser': 0x000c01b7, // launchAudioBrowser + 'LaunchCalendar': 0x000c018e, // launchCalendar + 'LaunchContacts': 0x000c018d, // launchContacts + 'LaunchControlPanel': 0x000c019f, // launchControlPanel + 'LaunchDocuments': 0x000c01a7, // launchDocuments + 'LaunchInternetBrowser': 0x000c0196, // launchInternetBrowser + 'LaunchKeyboardLayout': 0x000c01ae, // launchKeyboardLayout + 'LaunchMail': 0x000c018a, // launchMail + 'LaunchPhone': 0x000c008c, // launchPhone + 'LaunchScreenSaver': 0x000c01b1, // launchScreenSaver + 'LaunchSpreadsheet': 0x000c0186, // launchSpreadsheet + 'LaunchWordProcessor': 0x000c0184, // launchWordProcessor + 'LockScreen': 0x000c019e, // lockScreen + 'LogOff': 0x000c019c, // logOff + 'MailForward': 0x000c028b, // mailForward + 'MailReply': 0x000c0289, // mailReply + 'MailSend': 0x000c028c, // mailSend + 'MediaFastForward': 0x000c00b3, // mediaFastForward + 'MediaLast': 0x000c0083, // mediaLast + 'MediaPause': 0x000c00b1, // mediaPause + 'MediaPlay': 0x000c00b0, // mediaPlay + 'MediaPlayPause': 0x000c00cd, // mediaPlayPause + 'MediaRecord': 0x000c00b2, // mediaRecord + 'MediaRewind': 0x000c00b4, // mediaRewind + 'MediaSelect': 0x000c0183, // mediaSelect + 'MediaStop': 0x000c00b7, // mediaStop + 'MediaTrackNext': 0x000c00b5, // mediaTrackNext + 'MediaTrackPrevious': 0x000c00b6, // mediaTrackPrevious + 'MetaLeft': 0x000700e3, // metaLeft + 'MetaRight': 0x000700e7, // metaRight + 'Minus': 0x0007002d, // minus + 'New': 0x000c0201, // newKey + 'NonConvert': 0x0007008b, // nonConvert + 'NumLock': 0x00070053, // numLock + 'Numpad0': 0x00070062, // numpad0 + 'Numpad1': 0x00070059, // numpad1 + 'Numpad2': 0x0007005a, // numpad2 + 'Numpad3': 0x0007005b, // numpad3 + 'Numpad4': 0x0007005c, // numpad4 + 'Numpad5': 0x0007005d, // numpad5 + 'Numpad6': 0x0007005e, // numpad6 + 'Numpad7': 0x0007005f, // numpad7 + 'Numpad8': 0x00070060, // numpad8 + 'Numpad9': 0x00070061, // numpad9 + 'NumpadAdd': 0x00070057, // numpadAdd + 'NumpadBackspace': 0x000700bb, // numpadBackspace + 'NumpadClear': 0x000700d8, // numpadClear + 'NumpadClearEntry': 0x000700d9, // numpadClearEntry + 'NumpadComma': 0x00070085, // numpadComma + 'NumpadDecimal': 0x00070063, // numpadDecimal + 'NumpadDivide': 0x00070054, // numpadDivide + 'NumpadEnter': 0x00070058, // numpadEnter + 'NumpadEqual': 0x00070067, // numpadEqual + 'NumpadMemoryAdd': 0x000700d3, // numpadMemoryAdd + 'NumpadMemoryClear': 0x000700d2, // numpadMemoryClear + 'NumpadMemoryRecall': 0x000700d1, // numpadMemoryRecall + 'NumpadMemoryStore': 0x000700d0, // numpadMemoryStore + 'NumpadMemorySubtract': 0x000700d4, // numpadMemorySubtract + 'NumpadMultiply': 0x00070055, // numpadMultiply + 'NumpadParenLeft': 0x000700b6, // numpadParenLeft + 'NumpadParenRight': 0x000700b7, // numpadParenRight + 'NumpadSignChange': 0x000700d7, // numpadSignChange + 'NumpadSubtract': 0x00070056, // numpadSubtract + 'Open': 0x00070074, // open + 'PageDown': 0x0007004e, // pageDown + 'PageUp': 0x0007004b, // pageUp + 'Paste': 0x0007007d, // paste + 'Pause': 0x00070048, // pause + 'Period': 0x00070037, // period + 'Power': 0x00070066, // power + 'Print': 0x000c0208, // print + 'PrintScreen': 0x00070046, // printScreen + 'PrivacyScreenToggle': 0x00000017, // privacyScreenToggle + 'ProgramGuide': 0x000c008d, // programGuide + 'Props': 0x000700a3, // props + 'Quote': 0x00070034, // quote + 'Redo': 0x000c0279, // redo + 'Resume': 0x00000015, // resume + 'Save': 0x000c0207, // save + 'ScrollLock': 0x00070047, // scrollLock + 'Select': 0x00070077, // select + 'SelectTask': 0x000c01a2, // selectTask + 'Semicolon': 0x00070033, // semicolon + 'ShiftLeft': 0x000700e1, // shiftLeft + 'ShiftRight': 0x000700e5, // shiftRight + 'ShowAllWindows': 0x000c029f, // showAllWindows + 'Slash': 0x00070038, // slash + 'Sleep': 0x00010082, // sleep + 'Space': 0x0007002c, // space + 'SpeechInputToggle': 0x000c00cf, // speechInputToggle + 'SpellCheck': 0x000c01ab, // spellCheck + 'Super': 0x00000011, // superKey + 'Suspend': 0x00000014, // suspend + 'Tab': 0x0007002b, // tab + 'Turbo': 0x00000016, // turbo + 'Undo': 0x0007007a, // undo + 'UsbErrorRollOver': 0x00070001, // usbErrorRollOver + 'UsbErrorUndefined': 0x00070003, // usbErrorUndefined + 'UsbPostFail': 0x00070002, // usbPostFail + 'UsbReserved': 0x00070000, // usbReserved + 'WakeUp': 0x00010083, // wakeUp + 'ZoomIn': 0x000c022d, // zoomIn + 'ZoomOut': 0x000c022e, // zoomOut + 'ZoomToggle': 0x000c0232, // zoomToggle +}; + +/// Maps Web KeyboardEvent keys to Flutter logical IDs that depend on locations. +/// +/// `KeyboardEvent.location` is defined as: +/// +/// * 0: Standard +/// * 1: Left +/// * 2: Right +/// * 3: Numpad +const Map> kWebLogicalLocationMap = >{ + '*': [0x0000000002a, null, null, 0x0020000022a], // asterisk, null, null, numpadMultiply + '+': [0x0000000002b, null, null, 0x0020000022b], // add, null, null, numpadAdd + '-': [0x0000000002d, null, null, 0x0020000022d], // minus, null, null, numpadSubtract + '.': [0x0000000002e, null, null, 0x0020000022e], // period, null, null, numpadDecimal + '/': [0x0000000002f, null, null, 0x0020000022f], // slash, null, null, numpadDivide + '0': [0x00000000030, null, null, 0x00200000230], // digit0, null, null, numpad0 + '1': [0x00000000031, null, null, 0x00200000231], // digit1, null, null, numpad1 + '2': [0x00000000032, null, null, 0x00200000232], // digit2, null, null, numpad2 + '3': [0x00000000033, null, null, 0x00200000233], // digit3, null, null, numpad3 + '4': [0x00000000034, null, null, 0x00200000234], // digit4, null, null, numpad4 + '5': [0x00000000035, null, null, 0x00200000235], // digit5, null, null, numpad5 + '6': [0x00000000036, null, null, 0x00200000236], // digit6, null, null, numpad6 + '7': [0x00000000037, null, null, 0x00200000237], // digit7, null, null, numpad7 + '8': [0x00000000038, null, null, 0x00200000238], // digit8, null, null, numpad8 + '9': [0x00000000039, null, null, 0x00200000239], // digit9, null, null, numpad9 + 'Alt': [null, 0x00200000104, 0x00200000105, null], // null, altLeft, altRight, null + 'ArrowDown': [0x00100000301, null, null, 0x00200000232], // arrowDown, null, null, numpad2 + 'ArrowLeft': [0x00100000302, null, null, 0x00200000234], // arrowLeft, null, null, numpad4 + 'ArrowRight': [0x00100000303, null, null, 0x00200000236], // arrowRight, null, null, numpad6 + 'ArrowUp': [0x00100000304, null, null, 0x00200000238], // arrowUp, null, null, numpad8 + 'Clear': [0x00100000401, null, null, 0x00200000235], // clear, null, null, numpad5 + 'Control': [null, 0x00200000100, 0x00200000101, null], // null, controlLeft, controlRight, null + 'Delete': [0x0010000007f, null, null, 0x0020000022e], // delete, null, null, numpadDecimal + 'End': [0x00100000305, null, null, 0x00200000231], // end, null, null, numpad1 + 'Enter': [0x0010000000d, null, null, 0x0020000020d], // enter, null, null, numpadEnter + 'Home': [0x00100000306, null, null, 0x00200000237], // home, null, null, numpad7 + 'Insert': [0x00100000407, null, null, 0x00200000230], // insert, null, null, numpad0 + 'Meta': [null, 0x00200000106, 0x00200000107, null], // null, metaLeft, metaRight, null + 'PageDown': [0x00100000307, null, null, 0x00200000233], // pageDown, null, null, numpad3 + 'PageUp': [0x00100000308, null, null, 0x00200000239], // pageUp, null, null, numpad9 + 'Shift': [null, 0x00200000102, 0x00200000103, null], // null, shiftLeft, shiftRight, null +}; diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index c003dcfce6045..895172d5d7e28 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import '../engine.dart' show registerHotRestartListener; +import 'platform_dispatcher.dart'; +import 'services.dart'; /// After a keydown is received, this is the duration we wait for a repeat event /// before we decide to synthesize a keyup event. @@ -80,15 +85,6 @@ class Keyboard { } final html.KeyboardEvent keyboardEvent = event; - - if (window._onPlatformMessage == null) { - return; - } - - if (_shouldPreventDefault(event)) { - event.preventDefault(); - } - final String timerKey = keyboardEvent.code!; // Don't handle synthesizing a keyup event for modifier keys @@ -116,26 +112,38 @@ class Keyboard { } _lastMetaState = _getMetaState(event); + if (event.type == 'keydown') { + // For lock modifiers _getMetaState won't report a metaState at keydown. + // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState. + if (event.key == 'CapsLock') { + _lastMetaState |= modifierCapsLock; + } else if (event.code == 'NumLock') { + _lastMetaState |= modifierNumLock; + } else if (event.key == 'ScrollLock') { + _lastMetaState |= modifierScrollLock; + } + } final Map eventData = { 'type': event.type, 'keymap': 'web', 'code': keyboardEvent.code, 'key': keyboardEvent.key, + 'location': keyboardEvent.location, 'metaState': _lastMetaState, }; - window.invokeOnPlatformMessage('flutter/keyevent', - _messageCodec.encodeMessage(eventData), _noopCallback); - } - - bool _shouldPreventDefault(html.KeyboardEvent event) { - switch (event.key) { - case 'Tab': - return true; - - default: - return false; - } + EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', + _messageCodec.encodeMessage(eventData), (ByteData? data) { + if (data == null) { + return; + } + final Map jsonResponse = _messageCodec.decodeMessage(data) as Map; + if (jsonResponse['handled'] as bool) { + // If the framework handled it, then don't propagate it any further. + event.preventDefault(); + } + }, + ); } void _synthesizeKeyup(html.KeyboardEvent event) { @@ -144,10 +152,11 @@ class Keyboard { 'keymap': 'web', 'code': event.code, 'key': event.key, + 'location': event.location, 'metaState': _lastMetaState, }; - window.invokeOnPlatformMessage('flutter/keyevent', + EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', _messageCodec.encodeMessage(eventData), _noopCallback); } } @@ -157,6 +166,9 @@ const int _modifierShift = 0x01; const int _modifierAlt = 0x02; const int _modifierControl = 0x04; const int _modifierMeta = 0x08; +const int modifierNumLock = 0x10; +const int modifierCapsLock = 0x20; +const int modifierScrollLock = 0x40; /// Creates a bitmask representing the meta state of the [event]. int _getMetaState(html.KeyboardEvent event) { @@ -164,7 +176,7 @@ int _getMetaState(html.KeyboardEvent event) { if (event.getModifierState('Shift')) { metaState |= _modifierShift; } - if (event.getModifierState('Alt')) { + if (event.getModifierState('Alt') || event.getModifierState('AltGraph')) { metaState |= _modifierAlt; } if (event.getModifierState('Control')) { @@ -173,8 +185,17 @@ int _getMetaState(html.KeyboardEvent event) { if (event.getModifierState('Meta')) { metaState |= _modifierMeta; } - // TODO: Re-enable lock key modifiers once there is support on Flutter - // Framework. https://github.com/flutter/flutter/issues/46718 + // See https://github.com/flutter/flutter/issues/66601 for why we don't + // set the ones below based on persistent state. + // if (event.getModifierState("CapsLock")) { + // metaState |= modifierCapsLock; + // } + // if (event.getModifierState("NumLock")) { + // metaState |= modifierNumLock; + // } + // if (event.getModifierState("ScrollLock")) { + // metaState |= modifierScrollLock; + // } return metaState; } diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart new file mode 100644 index 0000000000000..471752dc0c4d7 --- /dev/null +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -0,0 +1,505 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../engine.dart' show registerHotRestartListener; +import 'browser_detection.dart'; +import 'key_map.dart'; +import 'platform_dispatcher.dart'; +import 'semantics.dart'; + +typedef _VoidCallback = void Function(); +typedef ValueGetter = T Function(); +typedef _ModifierGetter = bool Function(FlutterHtmlKeyboardEvent event); + +// Set this flag to true to see all the fired events in the console. +const bool _debugLogKeyEvents = false; + +const int _kLocationLeft = 1; +const int _kLocationRight = 2; + +final int _kLogicalAltLeft = kWebLogicalLocationMap['Alt']![_kLocationLeft]!; +final int _kLogicalAltRight = kWebLogicalLocationMap['Alt']![_kLocationRight]!; +final int _kLogicalControlLeft = kWebLogicalLocationMap['Control']![_kLocationLeft]!; +final int _kLogicalControlRight = kWebLogicalLocationMap['Control']![_kLocationRight]!; +final int _kLogicalShiftLeft = kWebLogicalLocationMap['Shift']![_kLocationLeft]!; +final int _kLogicalShiftRight = kWebLogicalLocationMap['Shift']![_kLocationRight]!; +final int _kLogicalMetaLeft = kWebLogicalLocationMap['Meta']![_kLocationLeft]!; +final int _kLogicalMetaRight = kWebLogicalLocationMap['Meta']![_kLocationRight]!; +// Map logical keys for modifier keys to the functions that can get their +// modifier flag out of an event. +final Map _kLogicalKeyToModifierGetter = { + _kLogicalAltLeft: (FlutterHtmlKeyboardEvent event) => event.altKey, + _kLogicalAltRight: (FlutterHtmlKeyboardEvent event) => event.altKey, + _kLogicalControlLeft: (FlutterHtmlKeyboardEvent event) => event.ctrlKey, + _kLogicalControlRight: (FlutterHtmlKeyboardEvent event) => event.ctrlKey, + _kLogicalShiftLeft: (FlutterHtmlKeyboardEvent event) => event.shiftKey, + _kLogicalShiftRight: (FlutterHtmlKeyboardEvent event) => event.shiftKey, + _kLogicalMetaLeft: (FlutterHtmlKeyboardEvent event) => event.metaKey, + _kLogicalMetaRight: (FlutterHtmlKeyboardEvent event) => event.metaKey, +}; + +// After a keydown is received, this is the duration we wait for a repeat event +// before we decide to synthesize a keyup event. +// +// On Linux and Windows, the typical ranges for keyboard repeat delay go up to +// 1000ms. On Mac, the range goes up to 2000ms. +const Duration _kKeydownCancelDurationNormal = Duration(milliseconds: 1000); +const Duration _kKeydownCancelDurationMacOs = Duration(milliseconds: 2000); + +// ASCII for a, z, A, and Z +const int _kCharLowerA = 0x61; +const int _kCharLowerZ = 0x7a; +const int _kCharUpperA = 0x41; +const int _kCharUpperZ = 0x5a; +bool isAlphabet(int charCode) { + return (charCode >= _kCharLowerA && charCode <= _kCharLowerZ) + || (charCode >= _kCharUpperA && charCode <= _kCharUpperZ); +} + +const String _kPhysicalCapsLock = 'CapsLock'; + +const String _kLogicalDead = 'Dead'; + +const int _kWebKeyIdPlane = 0x1700000000; + +// Bits in a Flutter logical event to generate the logical key for dead keys. +// +// Logical keys for dead keys are generated by annotating physical keys with +// modifiers (see `_getLogicalCode`). +const int _kDeadKeyCtrl = 0x10000000; +const int _kDeadKeyShift = 0x20000000; +const int _kDeadKeyAlt = 0x40000000; +const int _kDeadKeyMeta = 0x80000000; + +const ui.KeyData _emptyKeyData = ui.KeyData( + type: ui.KeyEventType.down, + timeStamp: Duration.zero, + logical: 0, + physical: 0, + character: null, + synthesized: false, +); + +typedef DispatchKeyData = bool Function(ui.KeyData data); + +/// Converts a floating number timestamp (in milliseconds) to a [Duration] by +/// splitting it into two integer components: milliseconds + microseconds. +Duration _eventTimeStampToDuration(num milliseconds) { + final int ms = milliseconds.toInt(); + final int micro = ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); + return Duration(milliseconds: ms, microseconds: micro); +} + +class KeyboardBinding { + /// The singleton instance of this object. + static KeyboardBinding? get instance => _instance; + static KeyboardBinding? _instance; + + static void initInstance(html.Element glassPaneElement) { + if (_instance == null) { + _instance = KeyboardBinding._(glassPaneElement); + assert(() { + registerHotRestartListener(_instance!._reset); + return true; + }()); + } + } + + KeyboardBinding._(this.glassPaneElement) { + _setup(); + } + + final html.Element glassPaneElement; + late KeyboardConverter _converter; + final Map _listeners = {}; + + void _addEventListener(String eventName, html.EventListener handler) { + dynamic loggedHandler(html.Event event) { + if (_debugLogKeyEvents) { + print(event.type); + } + if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { + return handler(event); + } + return null; + } + + assert(!_listeners.containsKey(eventName)); + _listeners[eventName] = loggedHandler; + html.window.addEventListener(eventName, loggedHandler, true); + } + + /// Remove all active event listeners. + void _clearListeners() { + _listeners.forEach((String eventName, html.EventListener listener) { + html.window.removeEventListener(eventName, listener, true); + }); + _listeners.clear(); + } + bool _onKeyData(ui.KeyData data) { + bool? result; + // This callback is designed to be invoked synchronously. This is enforced + // by `result`, which starts null and is asserted non-null when returned. + EnginePlatformDispatcher.instance.invokeOnKeyData(data, + (bool handled) { result = handled; }); + return result!; + } + + void _setup() { + _addEventListener('keydown', (html.Event event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); + }); + _addEventListener('keyup', (html.Event event) { + return _converter.handleEvent(FlutterHtmlKeyboardEvent(event as html.KeyboardEvent)); + }); + _converter = KeyboardConverter(_onKeyData, onMacOs: operatingSystem == OperatingSystem.macOs); + } + + void _reset() { + _clearListeners(); + _converter.dispose(); + } +} + +class AsyncKeyboardDispatching { + AsyncKeyboardDispatching({ + required this.keyData, + this.callback, + }); + + final ui.KeyData keyData; + final _VoidCallback? callback; +} + +// A wrapper of [html.KeyboardEvent] with reduced methods delegated to the event +// for the convenience of testing. +class FlutterHtmlKeyboardEvent { + FlutterHtmlKeyboardEvent(this._event); + + final html.KeyboardEvent _event; + + String get type => _event.type; + String? get code => _event.code; + String? get key => _event.key; + bool? get repeat => _event.repeat; + int? get location => _event.location; + num? get timeStamp => _event.timeStamp; + bool get altKey => _event.altKey; + bool get ctrlKey => _event.ctrlKey; + bool get shiftKey => _event.shiftKey; + bool get metaKey => _event.metaKey; + + bool getModifierState(String key) => _event.getModifierState(key); + void preventDefault() => _event.preventDefault(); +} + +// Reads [html.KeyboardEvent], then [dispatches ui.KeyData] accordingly. +// +// The events are read through [handleEvent], and dispatched through the +// [dispatchKeyData] as given in the constructor. Some key data might be +// dispatched asynchronously. +class KeyboardConverter { + KeyboardConverter(this.dispatchKeyData, {this.onMacOs = false}); + + final DispatchKeyData dispatchKeyData; + final bool onMacOs; + + bool _disposed = false; + void dispose() { + _disposed = true; + } + + // On macOS, CapsLock behaves differently in that, a keydown event occurs when + // the key is pressed and the light turns on, while a keyup event occurs when the + // key is pressed and the light turns off. Flutter considers both events as + // key down, and synthesizes immediate cancel events following them. The state + // of "whether CapsLock is on" should be accessed by "activeLocks". + bool _shouldSynthesizeCapsLockUp() { + return onMacOs; + } + + Duration get _keydownCancelDuration => onMacOs ? _kKeydownCancelDurationMacOs : _kKeydownCancelDurationNormal; + + static int _getPhysicalCode(String code) { + return kWebToPhysicalKey[code] ?? (code.hashCode + _kWebKeyIdPlane); + } + + static int _getModifierMask(FlutterHtmlKeyboardEvent event) { + final bool altDown = event.altKey; + final bool ctrlDown = event.ctrlKey; + final bool shiftDown = event.shiftKey; + final bool metaDown = event.metaKey; + return (altDown ? _kDeadKeyAlt : 0) + + (ctrlDown ? _kDeadKeyCtrl : 0) + + (shiftDown ? _kDeadKeyShift : 0) + + (metaDown ? _kDeadKeyMeta : 0); + } + + // Whether `event.key` should be considered a key name. + // + // The `event.key` can either be a key name or the printable character. If the + // first character is an alphabet, it must be either 'A' to 'Z' ( and return + // true), or be a key name (and return false). Otherwise, return true. + static bool _eventKeyIsKeyname(String key) { + assert(key.isNotEmpty); + return isAlphabet(key.codeUnitAt(0)) && key.length > 1; + } + + static int _characterToLogicalKey(String key) { + // Assume the length being <= 2 to be sufficient in all cases. If not, + // extend the algorithm. + assert(key.length <= 2); + int result = key.codeUnitAt(0) & 0xffff; + if (key.length == 2) { + result += key.codeUnitAt(1) << 16; + } + // Convert upper letters to lower letters + if (result >= _kCharUpperA && result <= _kCharUpperZ) { + result = result + _kCharLowerA - _kCharUpperA; + } + return result; + } + + static int _deadKeyToLogicalKey(int physicalKey, FlutterHtmlKeyboardEvent event) { + // 'Dead' is used to represent dead keys, such as a diacritic to the + // following base letter (such as Option-e results in ´). + // + // Assume they can be told apart with the physical key and the modifiers + // pressed. + return physicalKey + _getModifierMask(event) + _kWebKeyIdPlane; + } + + static int _otherLogicalKey(String key) { + return kWebToLogicalKey[key] ?? (key.hashCode + _kWebKeyIdPlane); + } + + // Map from pressed physical key to corresponding pressed logical key. + // + // Multiple physical keys can be mapped to the same logical key, usually due + // to positioned keys (left/right/numpad) or multiple keyboards. + final Map _pressingRecords = {}; + + // Schedule the dispatching of an event in the future. The `callback` will + // invoked before that. + // + // Returns a callback that cancels the schedule. Disposal of + // `KeyBoardConverter` also cancels the shedule automatically. + _VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, _VoidCallback callback) { + bool canceled = false; + Future.delayed(duration).then((_) { + if (!canceled && !_disposed) { + callback(); + dispatchKeyData(getData()); + } + }); + return () { canceled = true; }; + } + + // ## About Key guards + // + // When the user enters a browser/system shortcut (e.g. `cmd+alt+i`) the + // browser doesn't send a keyup for it. This puts the framework in a corrupt + // state because it thinks the key was never released. + // + // To avoid this, we rely on the fact that browsers send repeat events + // while the key is held down by the user. If we don't receive a repeat + // event within a specific duration ([_keydownCancelDuration]) we assume + // the user has released the key and we synthesize a keyup event. + final Map _keyGuards = {}; + // Call this method on the down or repeated event of a non-modifier key. + void _startGuardingKey(int physicalKey, int logicalKey, Duration currentTimeStamp) { + final _VoidCallback cancelingCallback = _scheduleAsyncEvent( + _keydownCancelDuration, + () => ui.KeyData( + timeStamp: currentTimeStamp + _keydownCancelDuration, + type: ui.KeyEventType.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + ), + () { + _pressingRecords.remove(physicalKey); + } + ); + _keyGuards.remove(physicalKey)?.call(); + _keyGuards[physicalKey] = cancelingCallback; + } + // Call this method on an up event event of a non-modifier key. + void _stopGuardingKey(int physicalKey) { + _keyGuards.remove(physicalKey)?.call(); + } + + // Parse the HTML event, update states, and dispatch Flutter key data through + // [dispatchKeyData]. + // + // * The method might dispatch some synthesized key data first to update states, + // results discarded. + // * Then it dispatches exactly one non-synthesized key data that corresponds + // to the `event`, i.e. the primary key data. If this dispatching returns + // true, then this event will be invoked `preventDefault`. + // * Some key data might be synthesized to update states after the main key + // data. They are always scheduled asynchronously with results discarded. + void handleEvent(FlutterHtmlKeyboardEvent event) { + final Duration timeStamp = _eventTimeStampToDuration(event.timeStamp!); + + final String eventKey = event.key!; + + final int physicalKey = _getPhysicalCode(event.code!); + final bool logicalKeyIsCharacter = !_eventKeyIsKeyname(eventKey); + final String? character = logicalKeyIsCharacter ? eventKey : null; + final int logicalKey = () { + if (kWebLogicalLocationMap.containsKey(event.key!)) { + final int? result = kWebLogicalLocationMap[event.key!]?[event.location!]; + assert(result != null, 'Invalid modifier location: ${event.key}, ${event.location}'); + return result!; + } + if (character != null) + return _characterToLogicalKey(character); + if (eventKey == _kLogicalDead) + return _deadKeyToLogicalKey(physicalKey, event); + return _otherLogicalKey(eventKey); + }(); + + assert(event.type == 'keydown' || event.type == 'keyup'); + final bool isPhysicalDown = event.type == 'keydown' || + // On macOS, both keydown and keyup events of CapsLock should be considered keydown, + // followed by an immediate cancel event. + (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock); + + final int? lastLogicalRecord = _pressingRecords[physicalKey]; + + ui.KeyEventType type; + + if (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock) { + // Case 1: Handle CapsLock on macOS + // + // On macOS, both keydown and keyup events of CapsLock are considered + // keydown, followed by an immediate synchronized up event. + + _scheduleAsyncEvent( + Duration.zero, + () => ui.KeyData( + timeStamp: timeStamp, + type: ui.KeyEventType.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + ), + () { + _pressingRecords.remove(physicalKey); + } + ); + type = ui.KeyEventType.down; + + } else if (isPhysicalDown) { + // Case 2: Handle key down of normal keys + type = ui.KeyEventType.down; + if (lastLogicalRecord != null) { + // This physical key is being pressed according to the record. + if (event.repeat ?? false) { + // A normal repeated key. + type = ui.KeyEventType.repeat; + } else { + // A non-repeated key has been pressed that has the exact physical key as + // a currently pressed one, usually indicating multiple keyboards are + // pressing keys with the same physical key, or the up event was lost + // during a loss of focus. The down event is ignored. + dispatchKeyData(_emptyKeyData); + event.preventDefault(); + return; + } + } else { + // This physical key is not being pressed according to the record. It's a + // normal down event, whether the system event is a repeat or not. + } + + } else { // isPhysicalDown is false and not CapsLock + // Case 2: Handle key up of normal keys + if (lastLogicalRecord == null) { + // The physical key has been released before. It indicates multiple + // keyboards pressed keys with the same physical key. Ignore the up event. + dispatchKeyData(_emptyKeyData); + event.preventDefault(); + return; + } + + type = ui.KeyEventType.up; + } + + final int? nextLogicalRecord; + switch (type) { + case ui.KeyEventType.down: + assert(lastLogicalRecord == null); + nextLogicalRecord = logicalKey; + break; + case ui.KeyEventType.up: + assert(lastLogicalRecord != null); + nextLogicalRecord = null; + break; + case ui.KeyEventType.repeat: + assert(lastLogicalRecord != null); + nextLogicalRecord = lastLogicalRecord; + break; + } + if (nextLogicalRecord == null) { + _pressingRecords.remove(physicalKey); + } else { + _pressingRecords[physicalKey] = nextLogicalRecord; + } + + // After updating _pressingRecords, synchronize modifier states. The + // `event.***Key` fields can be used to reduce some omitted modifier key + // events. We can deduce key cancel events if they are false. Key sync + // events can not be deduced since we don't know which physical key they + // represent. + _kLogicalKeyToModifierGetter.forEach((int logicalKey, _ModifierGetter getModifier) { + if (_pressingRecords.containsValue(logicalKey) && !getModifier(event)) { + _pressingRecords.removeWhere((int physicalKey, int logicalRecord) { + if (logicalRecord != logicalKey) + return false; + + dispatchKeyData(ui.KeyData( + timeStamp: timeStamp, + type: ui.KeyEventType.up, + physical: physicalKey, + logical: logicalKey, + character: null, + synthesized: true, + )); + + return true; + }); + } + }); + + // Update key guards + if (logicalKeyIsCharacter) { + if (nextLogicalRecord != null) { + _startGuardingKey(physicalKey, logicalKey, timeStamp); + } else { + _stopGuardingKey(physicalKey); + } + } + + final ui.KeyData keyData = ui.KeyData( + timeStamp: timeStamp, + type: type, + physical: physicalKey, + logical: lastLogicalRecord ?? logicalKey, + character: type == ui.KeyEventType.up ? null : character, + synthesized: false, + ); + + final bool primaryHandled = dispatchKeyData(keyData); + if (primaryHandled) { + event.preventDefault(); + } + } +} diff --git a/lib/web_ui/lib/src/engine/mouse_cursor.dart b/lib/web_ui/lib/src/engine/mouse_cursor.dart index 7db0e00a47ba9..cc67b9d08f0cb 100644 --- a/lib/web_ui/lib/src/engine/mouse_cursor.dart +++ b/lib/web_ui/lib/src/engine/mouse_cursor.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dom_renderer.dart'; /// Provides mouse cursor bindings, such as the `flutter/mousecursor` channel. class MouseCursor { @@ -18,7 +17,7 @@ class MouseCursor { static MouseCursor? get instance => _instance; static MouseCursor? _instance; - MouseCursor._() {} + MouseCursor._(); // Map from Flutter's kind values to CSS's cursor values. // @@ -66,7 +65,7 @@ class MouseCursor { } void activateSystemCursor(String? kind) { - domRenderer.setElementStyle( + DomRenderer.setElementStyle( domRenderer.glassPaneElement!, 'cursor', _mapKindToCssValue(kind), diff --git a/lib/web_ui/lib/src/engine/navigation.dart b/lib/web_ui/lib/src/engine/navigation.dart new file mode 100644 index 0000000000000..fae9027fa3cb2 --- /dev/null +++ b/lib/web_ui/lib/src/engine/navigation.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'navigation/history.dart'; +export 'navigation/js_url_strategy.dart'; +export 'navigation/url_strategy.dart'; \ No newline at end of file diff --git a/lib/web_ui/lib/src/engine/navigation/history.dart b/lib/web_ui/lib/src/engine/navigation/history.dart new file mode 100644 index 0000000000000..9c6de07e95edf --- /dev/null +++ b/lib/web_ui/lib/src/engine/navigation/history.dart @@ -0,0 +1,382 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../platform_dispatcher.dart'; +import '../services/message_codec.dart'; +import '../services/message_codecs.dart'; +import 'url_strategy.dart'; + +/// Infers the history mode from the existing browser history state, then +/// creates the appropriate instance of [BrowserHistory] for it. +/// +/// If it can't infer, it creates a [MultiEntriesBrowserHistory] by default. +BrowserHistory createHistoryForExistingState(UrlStrategy? urlStrategy) { + if (urlStrategy != null) { + final Object? state = urlStrategy.getState(); + if (SingleEntryBrowserHistory._isOriginEntry(state) || SingleEntryBrowserHistory._isFlutterEntry(state)) { + return SingleEntryBrowserHistory(urlStrategy: urlStrategy); + } + } + return MultiEntriesBrowserHistory(urlStrategy: urlStrategy); +} + +/// An abstract class that provides the API for [EngineWindow] to delegate its +/// navigating events. +/// +/// Subclasses will have access to [BrowserHistory.locationStrategy] to +/// interact with the html browser history and should come up with their own +/// ways to manage the states in the browser history. +/// +/// There should only be one global instance among all all subclasses. +/// +/// See also: +/// +/// * [SingleEntryBrowserHistory]: which creates a single fake browser history +/// entry and delegates all browser navigating events to the flutter +/// framework. +/// * [MultiEntriesBrowserHistory]: which creates a set of states that records +/// the navigating events happened in the framework. +abstract class BrowserHistory { + late ui.VoidCallback _unsubscribe; + + /// The strategy to interact with html browser history. + UrlStrategy? get urlStrategy; + + bool _isDisposed = false; + + void _setupStrategy(UrlStrategy strategy) { + _unsubscribe = strategy.addPopStateListener( + onPopState as html.EventListener, + ); + } + + /// Exit this application and return to the previous page. + Future exit() async { + if (urlStrategy != null) { + await tearDown(); + // Now the history should be in the original state, back one more time to + // exit the application. + await urlStrategy!.go(-1); + } + } + + /// This method does the same thing as the browser back button. + Future back() async { + return urlStrategy?.go(-1); + } + + /// The path of the current location of the user's browser. + String get currentPath => urlStrategy?.getPath() ?? '/'; + + /// The state of the current location of the user's browser. + Object? get currentState => urlStrategy?.getState(); + + /// Update the url with the given `routeName` and `state`. + /// + /// If `replace` is false, the caller wants to push a new `routeName` and + /// `state` on top of the existing ones; otherwise, the caller wants to replace + /// the current `routeName` and `state` with the new ones. + void setRouteName(String? routeName, {Object? state, bool replace = false}); + + /// A callback method to handle browser backward or forward buttons. + /// + /// Subclasses should send appropriate system messages to update the flutter + /// applications accordingly. + void onPopState(covariant html.PopStateEvent event); + + /// Restore any modifications to the html browser history during the lifetime + /// of this class. + Future tearDown(); +} + +/// A browser history class that creates a set of browser history entries to +/// support browser backward and forward button natively. +/// +/// This class pushes a browser history entry every time the framework reports +/// a route change and sends a `pushRouteInformation` method call to the +/// framework when the browser jumps to a specific browser history entry. +/// +/// The web engine uses this class to manage its browser history when the +/// framework uses a Router for routing. +/// +/// See also: +/// +/// * [SingleEntryBrowserHistory], which is used when the framework does not use +/// a Router for routing. +class MultiEntriesBrowserHistory extends BrowserHistory { + MultiEntriesBrowserHistory({required this.urlStrategy}) { + final UrlStrategy? strategy = urlStrategy; + if (strategy == null) { + return; + } + + _setupStrategy(strategy); + if (!_hasSerialCount(currentState)) { + strategy.replaceState( + _tagWithSerialCount(currentState, 0), 'flutter', currentPath); + } + // If we restore from a page refresh, the _currentSerialCount may not be 0. + _lastSeenSerialCount = _currentSerialCount; + } + + @override + final UrlStrategy? urlStrategy; + + late int _lastSeenSerialCount; + int get _currentSerialCount { + if (_hasSerialCount(currentState)) { + final Map stateMap = + currentState! as Map; + return stateMap['serialCount'] as int; + } + return 0; + } + + Object _tagWithSerialCount(Object? originialState, int count) { + return { + 'serialCount': count, + 'state': originialState, + }; + } + + bool _hasSerialCount(Object? state) { + return state is Map && state['serialCount'] != null; + } + + @override + void setRouteName(String? routeName, {Object? state, bool replace = false}) { + if (urlStrategy != null) { + assert(routeName != null); + if (replace) { + urlStrategy!.replaceState( + _tagWithSerialCount(state, _lastSeenSerialCount), + 'flutter', + routeName!, + ); + } else { + _lastSeenSerialCount += 1; + urlStrategy!.pushState( + _tagWithSerialCount(state, _lastSeenSerialCount), + 'flutter', + routeName!, + ); + } + } + } + + @override + void onPopState(covariant html.PopStateEvent event) { + assert(urlStrategy != null); + // May be a result of direct url access while the flutter application is + // already running. + if (!_hasSerialCount(event.state)) { + // In this case we assume this will be the next history entry from the + // last seen entry. + urlStrategy!.replaceState( + _tagWithSerialCount(event.state, _lastSeenSerialCount + 1), + 'flutter', + currentPath); + } + _lastSeenSerialCount = _currentSerialCount; + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/navigation', + const JSONMethodCodec().encodeMethodCall( + MethodCall('pushRouteInformation', { + 'location': currentPath, + 'state': event.state?['state'], + })), + (_) {}, + ); + } + + @override + Future tearDown() async { + if (_isDisposed || urlStrategy == null) { + return; + } + _isDisposed = true; + _unsubscribe(); + + // Restores the html browser history. + assert(_hasSerialCount(currentState)); + final int backCount = _currentSerialCount; + if (backCount > 0) { + await urlStrategy!.go(-backCount); + } + // Unwrap state. + assert(_hasSerialCount(currentState) && _currentSerialCount == 0); + final Map stateMap = + currentState! as Map; + urlStrategy!.replaceState( + stateMap['state'], + 'flutter', + currentPath, + ); + } +} + +/// The browser history class is responsible for integrating Flutter Web apps +/// with the browser history so that the back button works as expected. +/// +/// It does that by always keeping a single entry (conventionally called the +/// "flutter" entry) at the top of the browser history. That way, the browser's +/// back button always triggers a `popstate` event and never closes the app (we +/// close the app programmatically by calling [SystemNavigator.pop] when there +/// are no more app routes to be popped). +/// +/// The web engine uses this class when the framework does not use Router for +/// routing, and it does not support browser forward button. +/// +/// See also: +/// +/// * [MultiEntriesBrowserHistory], which is used when the framework uses a +/// Router for routing. +class SingleEntryBrowserHistory extends BrowserHistory { + SingleEntryBrowserHistory({required this.urlStrategy}) { + final UrlStrategy? strategy = urlStrategy; + if (strategy == null) { + return; + } + + _setupStrategy(strategy); + + final String path = currentPath; + if (!_isFlutterEntry(html.window.history.state)) { + // An entry may not have come from Flutter, for example, when the user + // refreshes the page. They land directly on the "flutter" entry, so + // there's no need to set up the "origin" and "flutter" entries, we can + // safely assume they are already set up. + _setupOriginEntry(strategy); + _setupFlutterEntry(strategy, replace: false, path: path); + } + } + + @override + final UrlStrategy? urlStrategy; + + static const MethodCall _popRouteMethodCall = MethodCall('popRoute'); + static const String _kFlutterTag = 'flutter'; + static const String _kOriginTag = 'origin'; + + Map _wrapOriginState(Object? state) { + return {_kOriginTag: true, 'state': state}; + } + + Object? _unwrapOriginState(Object? state) { + assert(_isOriginEntry(state)); + final Map originState = state! as Map; + return originState['state']; + } + + Map _flutterState = {_kFlutterTag: true}; + + /// The origin entry is the history entry that the Flutter app landed on. It's + /// created by the browser when the user navigates to the url of the app. + static bool _isOriginEntry(Object? state) { + return state is Map && state[_kOriginTag] == true; + } + + /// The flutter entry is a history entry that we maintain on top of the origin + /// entry. It allows us to catch popstate events when the user hits the back + /// button. + static bool _isFlutterEntry(Object? state) { + return state is Map && state[_kFlutterTag] == true; + } + + @override + void setRouteName(String? routeName, {Object? state, bool replace = false}) { + if (urlStrategy != null) { + _setupFlutterEntry(urlStrategy!, replace: true, path: routeName); + } + } + + String? _userProvidedRouteName; + @override + void onPopState(covariant html.PopStateEvent event) { + if (_isOriginEntry(event.state)) { + _setupFlutterEntry(urlStrategy!); + + // 2. Send a 'popRoute' platform message so the app can handle it accordingly. + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/navigation', + const JSONMethodCodec().encodeMethodCall(_popRouteMethodCall), + (_) {}, + ); + } else if (_isFlutterEntry(event.state)) { + // We get into this scenario when the user changes the url manually. It + // causes a new entry to be pushed on top of our "flutter" one. When this + // happens it first goes to the "else" section below where we capture the + // path into `_userProvidedRouteName` then trigger a history back which + // brings us here. + assert(_userProvidedRouteName != null); + + final String newRouteName = _userProvidedRouteName!; + _userProvidedRouteName = null; + + // Send a 'pushRoute' platform message so the app handles it accordingly. + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/navigation', + const JSONMethodCodec().encodeMethodCall( + MethodCall('pushRoute', newRouteName), + ), + (_) {}, + ); + } else { + // The user has pushed a new entry on top of our flutter entry. This could + // happen when the user modifies the hash part of the url directly, for + // example. + + // 1. We first capture the user's desired path. + _userProvidedRouteName = currentPath; + + // 2. Then we remove the new entry. + // This will take us back to our "flutter" entry and it causes a new + // popstate event that will be handled in the "else if" section above. + urlStrategy!.go(-1); + } + } + + /// This method should be called when the Origin Entry is active. It just + /// replaces the state of the entry so that we can recognize it later using + /// [_isOriginEntry] inside [_popStateListener]. + void _setupOriginEntry(UrlStrategy strategy) { + assert(strategy != null); // ignore: unnecessary_null_comparison + strategy.replaceState(_wrapOriginState(currentState), 'origin', ''); + } + + /// This method is used manipulate the Flutter Entry which is always the + /// active entry while the Flutter app is running. + void _setupFlutterEntry( + UrlStrategy strategy, { + bool replace = false, + String? path, + }) { + assert(strategy != null); // ignore: unnecessary_null_comparison + path ??= currentPath; + if (replace) { + strategy.replaceState(_flutterState, 'flutter', path); + } else { + strategy.pushState(_flutterState, 'flutter', path); + } + } + + @override + Future tearDown() async { + if (_isDisposed || urlStrategy == null) { + return; + } + _isDisposed = true; + _unsubscribe(); + + // We need to remove the flutter entry that we pushed in setup. + await urlStrategy!.go(-1); + // Restores original state. + urlStrategy! + .replaceState(_unwrapOriginState(currentState), 'flutter', currentPath); + } +} diff --git a/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart b/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart new file mode 100644 index 0000000000000..560b73f09121e --- /dev/null +++ b/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart @@ -0,0 +1,83 @@ +// 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. + +@JS() +library js_url_strategy; + +import 'dart:html' as html; + +import 'package:js/js.dart'; +import 'package:ui/ui.dart' as ui; + +typedef _PathGetter = String Function(); + +typedef _StateGetter = Object? Function(); + +typedef _AddPopStateListener = ui.VoidCallback Function(html.EventListener); + +typedef _StringToString = String Function(String); + +typedef _StateOperation = void Function( + Object? state, String title, String url); + +typedef _HistoryMove = Future Function(int count); + +/// The JavaScript representation of a URL strategy. +/// +/// This is used to pass URL strategy implementations across a JS-interop +/// bridge from the app to the engine. +@JS() +@anonymous +abstract class JsUrlStrategy { + /// Creates an instance of [JsUrlStrategy] from a bag of URL strategy + /// functions. + external factory JsUrlStrategy({ + required _PathGetter getPath, + required _StateGetter getState, + required _AddPopStateListener addPopStateListener, + required _StringToString prepareExternalUrl, + required _StateOperation pushState, + required _StateOperation replaceState, + required _HistoryMove go, + }); + + /// Adds a listener to the `popstate` event and returns a function that, when + /// invoked, removes the listener. + external ui.VoidCallback addPopStateListener(html.EventListener fn); + + /// Returns the active path in the browser. + external String getPath(); + + /// Returns the history state in the browser. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state + external Object? getState(); + + /// Given a path that's internal to the app, create the external url that + /// will be used in the browser. + external String prepareExternalUrl(String internalUrl); + + /// Push a new history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + external void pushState(Object? state, String title, String url); + + /// Replace the currently active history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState + external void replaceState(Object? state, String title, String url); + + /// Moves forwards or backwards through the history stack. + /// + /// A negative [count] value causes a backward move in the history stack. And + /// a positive [count] value causs a forward move. + /// + /// Examples: + /// + /// * `go(-2)` moves back 2 steps in history. + /// * `go(3)` moves forward 3 steps in hisotry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go + external Future go(int count); +} diff --git a/lib/web_ui/lib/src/engine/navigation/url_strategy.dart b/lib/web_ui/lib/src/engine/navigation/url_strategy.dart new file mode 100644 index 0000000000000..3da031a022bcd --- /dev/null +++ b/lib/web_ui/lib/src/engine/navigation/url_strategy.dart @@ -0,0 +1,300 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import 'js_url_strategy.dart'; + +/// Represents and reads route state from the browser's URL. +/// +/// By default, the [HashUrlStrategy] subclass is used if the app doesn't +/// specify one. +abstract class UrlStrategy { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const UrlStrategy(); + + /// Adds a listener to the `popstate` event and returns a function that, when + /// invoked, removes the listener. + ui.VoidCallback addPopStateListener(html.EventListener fn); + + /// Returns the active path in the browser. + String getPath(); + + /// The state of the current browser history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state + Object? getState(); + + /// Given a path that's internal to the app, create the external url that + /// will be used in the browser. + String prepareExternalUrl(String internalUrl); + + /// Push a new history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + void pushState(Object? state, String title, String url); + + /// Replace the currently active history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState + void replaceState(Object? state, String title, String url); + + /// Moves forwards or backwards through the history stack. + /// + /// A negative [count] value causes a backward move in the history stack. And + /// a positive [count] value causs a forward move. + /// + /// Examples: + /// + /// * `go(-2)` moves back 2 steps in history. + /// * `go(3)` moves forward 3 steps in hisotry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go + Future go(int count); +} + +/// This is an implementation of [UrlStrategy] that uses the browser URL's +/// [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) +/// to represent its state. +/// +/// In order to use this [UrlStrategy] for an app, it needs to be set like this: +/// +/// ```dart +/// import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +/// +/// // Somewhere before calling `runApp()` do: +/// setUrlStrategy(const HashUrlStrategy()); +/// ``` +class HashUrlStrategy extends UrlStrategy { + /// Creates an instance of [HashUrlStrategy]. + /// + /// The [PlatformLocation] parameter is useful for testing to mock out browser + /// interations. + const HashUrlStrategy( + [this._platformLocation = const BrowserPlatformLocation()]); + + final PlatformLocation _platformLocation; + + @override + ui.VoidCallback addPopStateListener(html.EventListener fn) { + _platformLocation.addPopStateListener(fn); + return () => _platformLocation.removePopStateListener(fn); + } + + @override + String getPath() { + // the hash value is always prefixed with a `#` + // and if it is empty then it will stay empty + final String path = _platformLocation.hash ?? ''; + assert(path.isEmpty || path.startsWith('#')); + + // We don't want to return an empty string as a path. Instead we default to "/". + if (path.isEmpty || path == '#') { + return '/'; + } + // At this point, we know [path] starts with "#" and isn't empty. + return path.substring(1); + } + + @override + Object? getState() => _platformLocation.state; + + @override + String prepareExternalUrl(String internalUrl) { + // It's convention that if the hash path is empty, we omit the `#`; however, + // if the empty URL is pushed it won't replace any existing fragment. So + // when the hash path is empty, we instead return the location's path and + // query. + return internalUrl.isEmpty + ? '${_platformLocation.pathname}${_platformLocation.search}' + : '#$internalUrl'; + } + + @override + void pushState(Object? state, String title, String url) { + _platformLocation.pushState(state, title, prepareExternalUrl(url)); + } + + @override + void replaceState(Object? state, String title, String url) { + _platformLocation.replaceState(state, title, prepareExternalUrl(url)); + } + + @override + Future go(int count) { + _platformLocation.go(count); + return _waitForPopState(); + } + + /// Waits until the next popstate event is fired. + /// + /// This is useful, for example, to wait until the browser has handled the + /// `history.back` transition. + Future _waitForPopState() { + final Completer completer = Completer(); + late ui.VoidCallback unsubscribe; + unsubscribe = addPopStateListener((_) { + unsubscribe(); + completer.complete(); + }); + return completer.future; + } +} + +/// Wraps a custom implementation of [UrlStrategy] that was previously converted +/// to a [JsUrlStrategy]. +class CustomUrlStrategy extends UrlStrategy { + /// Wraps the [delegate] in a [CustomUrlStrategy] instance. + CustomUrlStrategy.fromJs(this.delegate); + + final JsUrlStrategy delegate; + + @override + ui.VoidCallback addPopStateListener(html.EventListener fn) => + delegate.addPopStateListener(fn); + + @override + String getPath() => delegate.getPath(); + + @override + Object? getState() => delegate.getState(); + + @override + String prepareExternalUrl(String internalUrl) => + delegate.prepareExternalUrl(internalUrl); + + @override + void pushState(Object? state, String title, String url) => + delegate.pushState(state, title, url); + + @override + void replaceState(Object? state, String title, String url) => + delegate.replaceState(state, title, url); + + @override + Future go(int count) => delegate.go(count); +} + +/// Encapsulates all calls to DOM apis, which allows the [UrlStrategy] classes +/// to be platform agnostic and testable. +/// +/// For convenience, the [PlatformLocation] class can be used by implementations +/// of [UrlStrategy] to interact with DOM apis like pushState, popState, etc. +abstract class PlatformLocation { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const PlatformLocation(); + + /// Registers an event listener for the `popstate` event. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate + void addPopStateListener(html.EventListener fn); + + /// Unregisters the given listener (added by [addPopStateListener]) from the + /// `popstate` event. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate + void removePopStateListener(html.EventListener fn); + + /// The `pathname` part of the URL in the browser address bar. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname + String get pathname; + + /// The `query` part of the URL in the browser address bar. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/search + String get search; + + /// The `hash]` part of the URL in the browser address bar. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/hash + String? get hash; + + /// The `state` in the current history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state + Object? get state; + + /// Adds a new entry to the browser history stack. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + void pushState(Object? state, String title, String url); + + /// Replaces the current entry in the browser history stack. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState + void replaceState(Object? state, String title, String url); + + /// Moves forwards or backwards through the history stack. + /// + /// A negative [count] value causes a backward move in the history stack. And + /// a positive [count] value causs a forward move. + /// + /// Examples: + /// + /// * `go(-2)` moves back 2 steps in history. + /// * `go(3)` moves forward 3 steps in hisotry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go + void go(int count); + + /// The base href where the Flutter app is being served. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base + String? getBaseHref(); +} + +/// Delegates to real browser APIs to provide platform location functionality. +class BrowserPlatformLocation extends PlatformLocation { + /// Default constructor for [BrowserPlatformLocation]. + const BrowserPlatformLocation(); + + html.Location get _location => html.window.location; + html.History get _history => html.window.history; + + @override + void addPopStateListener(html.EventListener fn) { + html.window.addEventListener('popstate', fn); + } + + @override + void removePopStateListener(html.EventListener fn) { + html.window.removeEventListener('popstate', fn); + } + + @override + String get pathname => _location.pathname!; + + @override + String get search => _location.search!; + + @override + String get hash => _location.hash; + + @override + Object? get state => _history.state; + + @override + void pushState(Object? state, String title, String url) { + _history.pushState(state, title, url); + } + + @override + void replaceState(Object? state, String title, String url) { + _history.replaceState(state, title, url); + } + + @override + void go(int count) { + _history.go(count); + } + + @override + String? getBaseHref() => html.document.baseUri; +} diff --git a/lib/web_ui/lib/src/engine/onscreen_logging.dart b/lib/web_ui/lib/src/engine/onscreen_logging.dart index c52580191f3f8..c3eeb38b80b3b 100644 --- a/lib/web_ui/lib/src/engine/onscreen_logging.dart +++ b/lib/web_ui/lib/src/engine/onscreen_logging.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; html.Element? _logElement; late html.Element _logContainer; @@ -116,7 +115,7 @@ Iterable defaultStackFilter(Iterable frames) { final RegExp packageParser = RegExp(r'^([^:]+):(.+)$'); final List result = []; final List skipped = []; - for (String line in frames) { + for (final String line in frames) { final Match? match = stackParser.firstMatch(line); if (match != null) { assert(match.groupCount == 2); diff --git a/lib/web_ui/lib/src/engine/picture.dart b/lib/web_ui/lib/src/engine/picture.dart index 946f348a66a3e..889862b402d2e 100644 --- a/lib/web_ui/lib/src/engine/picture.dart +++ b/lib/web_ui/lib/src/engine/picture.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import 'html/bitmap_canvas.dart'; +import 'html/recording_canvas.dart'; +import 'html_image_codec.dart'; /// An implementation of [ui.PictureRecorder] backed by a [RecordingCanvas]. class EnginePictureRecorder implements ui.PictureRecorder { @@ -24,7 +30,7 @@ class EnginePictureRecorder implements ui.PictureRecorder { bool get isRecording => _isRecording; @override - ui.Picture endRecording() { + EnginePicture endRecording() { if (!_isRecording) { // The mobile version returns an empty picture in this case. To match the // behavior we produce a blank picture too. @@ -47,7 +53,7 @@ class EnginePicture implements ui.Picture { @override Future toImage(int width, int height) async { final ui.Rect imageRect = ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble()); - final BitmapCanvas canvas = BitmapCanvas(imageRect); + final BitmapCanvas canvas = BitmapCanvas.imageData(imageRect); recordingCanvas!.apply(canvas, imageRect); final String imageDataUrl = canvas.toDataUrl(); final html.ImageElement imageElement = html.ImageElement() diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart new file mode 100644 index 0000000000000..6957a192e6af7 --- /dev/null +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -0,0 +1,1078 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import '../engine.dart' show platformViewManager, registerHotRestartListener; +import 'canvaskit/initialization.dart'; +import 'canvaskit/layer_scene_builder.dart'; +import 'canvaskit/rasterizer.dart'; +import 'clipboard.dart'; +import 'dom_renderer.dart'; +import 'html/scene.dart'; +import 'mouse_cursor.dart'; +import 'platform_views/message_handler.dart'; +import 'plugins.dart'; +import 'profiler.dart'; +import 'semantics.dart'; +import 'services.dart'; +import 'text_editing/text_editing.dart'; +import 'util.dart'; +import 'window.dart'; + +/// Requests that the browser schedule a frame. +/// +/// This may be overridden in tests, for example, to pump fake frames. +ui.VoidCallback? scheduleFrameCallback; + +typedef _KeyDataResponseCallback = void Function(bool handled); + +/// Platform event dispatcher. +/// +/// This is the central entry point for platform messages and configuration +/// events from the platform. +class EnginePlatformDispatcher extends ui.PlatformDispatcher { + /// Private constructor, since only dart:ui is supposed to create one of + /// these. + EnginePlatformDispatcher._() { + _addBrightnessMediaQueryListener(); + } + + /// The [EnginePlatformDispatcher] singleton. + static EnginePlatformDispatcher get instance => _instance; + static final EnginePlatformDispatcher _instance = + EnginePlatformDispatcher._(); + + /// The current platform configuration. + @override + ui.PlatformConfiguration get configuration => _configuration; + ui.PlatformConfiguration _configuration = + ui.PlatformConfiguration(locales: parseBrowserLanguages()); + + /// Receives all events related to platform configuration changes. + @override + ui.VoidCallback? get onPlatformConfigurationChanged => + _onPlatformConfigurationChanged; + ui.VoidCallback? _onPlatformConfigurationChanged; + Zone? _onPlatformConfigurationChangedZone; + @override + set onPlatformConfigurationChanged(ui.VoidCallback? callback) { + _onPlatformConfigurationChanged = callback; + _onPlatformConfigurationChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformConfigurationChanged() { + invoke( + _onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + } + + /// The current list of windows, + @override + Iterable get views => _windows.values; + Map get windows => _windows; + Map _windows = {}; + + /// A map of opaque platform window identifiers to window configurations. + /// + /// This should be considered a protected member, only to be used by + /// [PlatformDispatcher] subclasses. + Map get windowConfigurations => _windowConfigurations; + Map _windowConfigurations = + {}; + + /// A callback that is invoked whenever the platform's [devicePixelRatio], + /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] + /// values change, for example when the device is rotated or when the + /// application is resized (e.g. when showing applications side-by-side + /// on Android). + /// + /// The engine invokes this callback in the same zone in which the callback + /// was set. + /// + /// The framework registers with this callback and updates the layout + /// appropriately. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// register for notifications when this is called. + /// * [MediaQuery.of], a simpler mechanism for the same. + @override + ui.VoidCallback? get onMetricsChanged => _onMetricsChanged; + ui.VoidCallback? _onMetricsChanged; + Zone? _onMetricsChangedZone; + @override + set onMetricsChanged(ui.VoidCallback? callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnMetricsChanged() { + if (_onMetricsChanged != null) { + invoke(_onMetricsChanged, _onMetricsChangedZone); + } + } + + /// Returns device pixel ratio returned by browser. + static double get browserDevicePixelRatio { + final double? ratio = html.window.devicePixelRatio as double?; + // Guard against WebOS returning 0 and other browsers returning null. + return (ratio == null || ratio == 0.0) ? 1.0 : ratio; + } + + /// A callback invoked when any window begins a frame. + /// + /// A callback that is invoked to notify the application that it is an + /// appropriate time to provide a scene using the [SceneBuilder] API and the + /// [PlatformWindow.render] method. + /// When possible, this is driven by the hardware VSync signal of the attached + /// screen with the highest VSync rate. This is only called if + /// [PlatformWindow.scheduleFrame] has been called since the last time this + /// callback was invoked. + @override + ui.FrameCallback? get onBeginFrame => _onBeginFrame; + ui.FrameCallback? _onBeginFrame; + Zone? _onBeginFrameZone; + @override + set onBeginFrame(ui.FrameCallback? callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnBeginFrame(Duration duration) { + invoke1(_onBeginFrame, _onBeginFrameZone, duration); + } + + /// A callback that is invoked for each frame after [onBeginFrame] has + /// completed and after the microtask queue has been drained. + /// + /// This can be used to implement a second phase of frame rendering that + /// happens after any deferred work queued by the [onBeginFrame] phase. + @override + ui.VoidCallback? get onDrawFrame => _onDrawFrame; + ui.VoidCallback? _onDrawFrame; + Zone? _onDrawFrameZone; + @override + set onDrawFrame(ui.VoidCallback? callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnDrawFrame() { + invoke(_onDrawFrame, _onDrawFrameZone); + } + + /// A callback that is invoked when pointer data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + @override + ui.PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; + ui.PointerDataPacketCallback? _onPointerDataPacket; + Zone? _onPointerDataPacketZone; + @override + set onPointerDataPacket(ui.PointerDataPacketCallback? callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPointerDataPacket(ui.PointerDataPacket dataPacket) { + invoke1( + _onPointerDataPacket, _onPointerDataPacketZone, dataPacket); + } + + /// A callback that is invoked when key data is available. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [GestureBinding], the Flutter framework class which manages pointer + /// events. + @override + ui.KeyDataCallback? get onKeyData => _onKeyData; + ui.KeyDataCallback? _onKeyData; + Zone? _onKeyDataZone; + @override + set onKeyData(ui.KeyDataCallback? callback) { + _onKeyData = callback; + _onKeyDataZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnKeyData(ui.KeyData data, _KeyDataResponseCallback callback) { + final ui.KeyDataCallback? onKeyData = _onKeyData; + if (onKeyData != null) { + invoke( + () => callback(onKeyData(data)), + _onKeyDataZone, + ); + } else { + callback(false); + } + } + + /// A callback that is invoked to report the [FrameTiming] of recently + /// rasterized frames. + /// + /// It's preferred to use [SchedulerBinding.addTimingsCallback] than to use + /// [PlatformDispatcher.onReportTimings] directly because + /// [SchedulerBinding.addTimingsCallback] allows multiple callbacks. + /// + /// This can be used to see if the application has missed frames (through + /// [FrameTiming.buildDuration] and [FrameTiming.rasterDuration]), or high + /// latencies (through [FrameTiming.totalSpan]). + /// + /// Unlike [Timeline], the timing information here is available in the release + /// mode (additional to the profile and the debug mode). Hence this can be + /// used to monitor the application's performance in the wild. + /// + /// {@macro dart.ui.TimingsCallback.list} + /// + /// If this is null, no additional work will be done. If this is not null, + /// Flutter spends less than 0.1ms every 1 second to report the timings + /// (measured on iPhone6S). The 0.1ms is about 0.6% of 16ms (frame budget for + /// 60fps), or 0.01% CPU usage per second. + @override + ui.TimingsCallback? get onReportTimings => _onReportTimings; + ui.TimingsCallback? _onReportTimings; + Zone? _onReportTimingsZone; + @override + set onReportTimings(ui.TimingsCallback? callback) { + _onReportTimings = callback; + _onReportTimingsZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnReportTimings(List timings) { + invoke1>( + _onReportTimings, _onReportTimingsZone, timings); + } + + @override + void sendPlatformMessage( + String name, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + _sendPlatformMessage( + name, data, _zonedPlatformMessageResponseCallback(callback)); + } + + // TODO(ianh): Deprecate onPlatformMessage once the framework is moved over + // to using channel buffers exclusively. + @override + ui.PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; + ui.PlatformMessageCallback? _onPlatformMessage; + Zone? _onPlatformMessageZone; + @override + set onPlatformMessage(ui.PlatformMessageCallback? callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformMessage( + String name, + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) { + if (name == ui.ChannelBuffers.kControlChannelName) { + // TODO(ianh): move this logic into ChannelBuffers once we remove onPlatformMessage + try { + ui.channelBuffers.handleMessage(data!); + } finally { + callback(null); + } + } else if (_onPlatformMessage != null) { + invoke3( + _onPlatformMessage, + _onPlatformMessageZone, + name, + data, + callback, + ); + } else { + ui.channelBuffers.push(name, data, callback); + } + } + + /// Wraps the given [callback] in another callback that ensures that the + /// original callback is called in the zone it was registered in. + static ui.PlatformMessageResponseCallback? + _zonedPlatformMessageResponseCallback( + ui.PlatformMessageResponseCallback? callback) { + if (callback == null) { + return null; + } + + // Store the zone in which the callback is being registered. + final Zone registrationZone = Zone.current; + + return (ByteData? data) { + registrationZone.runUnaryGuarded(callback, data); + }; + } + + PlatformViewMessageHandler? _platformViewMessageHandler; + + void _sendPlatformMessage( + String name, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + // In widget tests we want to bypass processing of platform messages. + if (assertionsEnabled && ui.debugEmulateFlutterTesterEnvironment) { + return; + } + + if (debugPrintPlatformMessages) { + print('Sent platform message on channel: "$name"'); + } + + if (assertionsEnabled && name == 'flutter/debug-echo') { + // Echoes back the data unchanged. Used for testing purposes. + replyToPlatformMessage(callback, data); + return; + } + + switch (name) { + + /// This should be in sync with shell/common/shell.cc + case 'flutter/skia': + const MethodCodec codec = JSONMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + switch (decoded.method) { + case 'Skia.setResourceCacheMaxBytes': + if (useCanvasKit) { + // If we're in CanvasKit mode, we must also have a rasterizer. + assert(rasterizer != null); + assert( + decoded.arguments is int, + 'Argument to Skia.setResourceCacheMaxBytes must be an int, but was ${decoded.arguments.runtimeType}', + ); + final int cacheSizeInBytes = decoded.arguments as int; + rasterizer!.setSkiaResourceCacheMaxBytes(cacheSizeInBytes); + } + + // Also respond in HTML mode. Otherwise, apps would have to detect + // CanvasKit vs HTML before invoking this method. + replyToPlatformMessage( + callback, codec.encodeSuccessEnvelope([true])); + break; + } + return; + + case 'flutter/assets': + final String url = utf8.decode(data!.buffer.asUint8List()); + ui.webOnlyAssetManager.load(url).then((ByteData assetData) { + replyToPlatformMessage(callback, assetData); + }, onError: (dynamic error) { + printWarning('Error while trying to load an asset: $error'); + replyToPlatformMessage(callback, null); + }); + return; + + case 'flutter/platform': + const MethodCodec codec = JSONMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + switch (decoded.method) { + case 'SystemNavigator.pop': + // TODO(gspencergoog): As multi-window support expands, the pop call + // will need to include the window ID. Right now only one window is + // supported. + (_windows[0]! as EngineFlutterWindow) + .browserHistory + .exit() + .then((_) { + replyToPlatformMessage( + callback, codec.encodeSuccessEnvelope(true)); + }); + return; + case 'HapticFeedback.vibrate': + final String? type = decoded.arguments as String?; + domRenderer.vibrate(_getHapticFeedbackDuration(type)); + replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'SystemChrome.setApplicationSwitcherDescription': + final Map arguments = decoded.arguments as Map; + // TODO(ferhat): Find more appropriate defaults? Or noop when values are null? + final String label = arguments['label'] as String? ?? ''; + final int primaryColor = arguments['primaryColor'] as int? ?? 0xFF000000; + domRenderer.setTitle(label); + domRenderer.setThemeColor(ui.Color(primaryColor)); + replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'SystemChrome.setPreferredOrientations': + final List arguments = decoded.arguments as List; + domRenderer.setPreferredOrientation(arguments).then((bool success) { + replyToPlatformMessage( + callback, codec.encodeSuccessEnvelope(success)); + }); + return; + case 'SystemSound.play': + // There are no default system sounds on web. + replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + return; + case 'Clipboard.setData': + ClipboardMessageHandler().setDataMethodCall(decoded, callback); + return; + case 'Clipboard.getData': + ClipboardMessageHandler().getDataMethodCall(callback); + return; + } + break; + + // Dispatched by the bindings to delay service worker initialization. + case 'flutter/service_worker': + html.window.dispatchEvent(html.Event('flutter-first-frame')); + return; + + case 'flutter/textinput': + textEditing.channel.handleTextInput(data, callback); + return; + + case 'flutter/mousecursor': + const MethodCodec codec = StandardMethodCodec(); + final MethodCall decoded = codec.decodeMethodCall(data); + final Map arguments = decoded.arguments as Map; + switch (decoded.method) { + case 'activateSystemCursor': + MouseCursor.instance!.activateSystemCursor(arguments.tryString('kind')); + } + return; + + case 'flutter/web_test_e2e': + const MethodCodec codec = JSONMethodCodec(); + replyToPlatformMessage( + callback, + codec.encodeSuccessEnvelope( + _handleWebTestEnd2EndMessage(codec, data))); + return; + + case 'flutter/platform_views': + _platformViewMessageHandler ??= PlatformViewMessageHandler( + contentManager: platformViewManager, + contentHandler: (html.Element content) { + domRenderer.glassPaneElement!.append(content); + }, + ); + _platformViewMessageHandler!.handlePlatformViewCall(data, callback!); + return; + + case 'flutter/accessibility': + // In widget tests we want to bypass processing of platform messages. + const StandardMessageCodec codec = StandardMessageCodec(); + accessibilityAnnouncements.handleMessage(codec, data); + replyToPlatformMessage(callback, codec.encodeMessage(true)); + return; + + case 'flutter/navigation': + // TODO(gspencergoog): As multi-window support expands, the navigation call + // will need to include the window ID. Right now only one window is + // supported. + (_windows[0]! as EngineFlutterWindow) + .handleNavigationMessage(data) + .then((bool handled) { + if (handled) { + const MethodCodec codec = JSONMethodCodec(); + replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + } else { + callback?.call(null); + } + }); + + // As soon as Flutter starts taking control of the app navigation, we + // should reset _defaultRouteName to "/" so it doesn't have any + // further effect after this point. + _defaultRouteName = '/'; + return; + } + + if (pluginMessageCallHandler != null) { + pluginMessageCallHandler!(name, data, callback); + return; + } + + // Passing [null] to [callback] indicates that the platform message isn't + // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is + // handled. + replyToPlatformMessage(callback, null); + } + + int _getHapticFeedbackDuration(String? type) { + switch (type) { + case 'HapticFeedbackType.lightImpact': + return DomRenderer.vibrateLightImpact; + case 'HapticFeedbackType.mediumImpact': + return DomRenderer.vibrateMediumImpact; + case 'HapticFeedbackType.heavyImpact': + return DomRenderer.vibrateHeavyImpact; + case 'HapticFeedbackType.selectionClick': + return DomRenderer.vibrateSelectionClick; + default: + return DomRenderer.vibrateLongPress; + } + } + + /// Requests that, at the next appropriate opportunity, the [onBeginFrame] + /// and [onDrawFrame] callbacks be invoked. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + @override + void scheduleFrame() { + if (scheduleFrameCallback == null) { + throw Exception('scheduleFrameCallback must be initialized first.'); + } + scheduleFrameCallback!(); + } + + /// Updates the application's rendering on the GPU with the newly provided + /// [Scene]. This function must be called within the scope of the + /// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function + /// is called a second time during a single [onBeginFrame]/[onDrawFrame] + /// callback sequence or called outside the scope of those callbacks, the call + /// will be ignored. + /// + /// To record graphical operations, first create a [PictureRecorder], then + /// construct a [Canvas], passing that [PictureRecorder] to its constructor. + /// After issuing all the graphical operations, call the + /// [PictureRecorder.endRecording] function on the [PictureRecorder] to obtain + /// the final [Picture] that represents the issued graphical operations. + /// + /// Next, create a [SceneBuilder], and add the [Picture] to it using + /// [SceneBuilder.addPicture]. With the [SceneBuilder.build] method you can + /// then obtain a [Scene] object, which you can display to the user via this + /// [render] function. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + @override + void render(ui.Scene scene, [ui.FlutterView? view]) { + if (useCanvasKit) { + // "Build finish" and "raster start" happen back-to-back because we + // render on the same thread, so there's no overhead from hopping to + // another thread. + // + // CanvasKit works differently from the HTML renderer in that in HTML + // we update the DOM in SceneBuilder.build, which is these function calls + // here are CanvasKit-only. + frameTimingsOnBuildFinish(); + frameTimingsOnRasterStart(); + + final LayerScene layerScene = scene as LayerScene; + rasterizer!.draw(layerScene.layerTree); + } else { + final SurfaceScene surfaceScene = scene as SurfaceScene; + domRenderer.renderScene(surfaceScene.webOnlyRootElement); + } + frameTimingsOnRasterFinish(); + } + + /// Additional accessibility features that may be enabled by the platform. + @override + ui.AccessibilityFeatures get accessibilityFeatures => + configuration.accessibilityFeatures; + + /// A callback that is invoked when the value of [accessibilityFeatures] changes. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + @override + ui.VoidCallback? get onAccessibilityFeaturesChanged => + _onAccessibilityFeaturesChanged; + ui.VoidCallback? _onAccessibilityFeaturesChanged; + Zone? _onAccessibilityFeaturesChangedZone; + @override + set onAccessibilityFeaturesChanged(ui.VoidCallback? callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFeaturesChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnAccessibilityFeaturesChanged() { + invoke( + _onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); + } + + /// Change the retained semantics data about this window. + /// + /// If [semanticsEnabled] is true, the user has requested that this function + /// be called whenever the semantic content of this window changes. + /// + /// In either case, this function disposes the given update, which means the + /// semantics update cannot be used further. + @override + void updateSemantics(ui.SemanticsUpdate update) { + EngineSemanticsOwner.instance.updateSemantics(update); + } + + /// This is equivalent to `locales.first`, except that it will provide an + /// undefined (using the language tag "und") non-null locale if the [locales] + /// list has not been set or is empty. + /// + /// We use the first locale in the [locales] list instead of the browser's + /// built-in `navigator.language` because browsers do not agree on the + /// implementation. + /// + /// See also: + /// + /// * https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages, + /// which explains browser quirks in the implementation notes. + @override + ui.Locale get locale => + locales.isEmpty ? const ui.Locale.fromSubtags() : locales.first; + + /// The full system-reported supported locales of the device. + /// + /// This establishes the language and formatting conventions that application + /// should, if possible, use to render their user interface. + /// + /// The list is ordered in order of priority, with lower-indexed locales being + /// preferred over higher-indexed ones. The first element is the primary [locale]. + /// + /// The [onLocaleChanged] callback is called whenever this value changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + @override + List get locales => configuration.locales; + + /// Performs the platform-native locale resolution. + /// + /// Each platform may return different results. + /// + /// If the platform fails to resolve a locale, then this will return null. + /// + /// This method returns synchronously and is a direct call to + /// platform specific APIs without invoking method channels. + @override + ui.Locale? computePlatformResolvedLocale(List supportedLocales) { + // TODO(garyq): Implement on web. + return null; + } + + /// A callback that is invoked whenever [locale] changes value. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + @override + ui.VoidCallback? get onLocaleChanged => _onLocaleChanged; + ui.VoidCallback? _onLocaleChanged; + Zone? _onLocaleChangedZone; + @override + set onLocaleChanged(ui.VoidCallback? callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + /// The locale used when we fail to get the list from the browser. + static const ui.Locale _defaultLocale = ui.Locale('en', 'US'); + + /// Sets locales to an empty list. + /// + /// The empty list is not a valid value for locales. This is only used for + /// testing locale update logic. + void debugResetLocales() { + _configuration = _configuration.copyWith(locales: const []); + } + + // Called by DomRenderer when browser languages change. + void updateLocales() { + _configuration = _configuration.copyWith(locales: parseBrowserLanguages()); + } + + static List parseBrowserLanguages() { + // TODO(yjbanov): find a solution for IE + final List? languages = html.window.navigator.languages; + if (languages == null || languages.isEmpty) { + // To make it easier for the app code, let's not leave the locales list + // empty. This way there's fewer corner cases for apps to handle. + return const [_defaultLocale]; + } + + final List locales = []; + for (final String language in languages) { + final List parts = language.split('-'); + if (parts.length > 1) { + locales.add(ui.Locale(parts.first, parts.last)); + } else { + locales.add(ui.Locale(language)); + } + } + + assert(locales.isNotEmpty); + return locales; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnLocaleChanged() { + invoke(_onLocaleChanged, _onLocaleChangedZone); + } + + /// The system-reported text scale. + /// + /// This establishes the text scaling factor to use when rendering text, + /// according to the user's platform preferences. + /// + /// The [onTextScaleFactorChanged] callback is called whenever this value + /// changes. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + @override + double get textScaleFactor => configuration.textScaleFactor; + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + /// + /// This option is used by [showTimePicker]. + @override + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + + /// A callback that is invoked whenever [textScaleFactor] changes value. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + @override + ui.VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; + ui.VoidCallback? _onTextScaleFactorChanged; + Zone? _onTextScaleFactorChangedZone; + @override + set onTextScaleFactorChanged(ui.VoidCallback? callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnTextScaleFactorChanged() { + invoke(_onTextScaleFactorChanged, _onTextScaleFactorChangedZone); + } + + void updateSemanticsEnabled(bool semanticsEnabled) { + if (semanticsEnabled != this.semanticsEnabled) { + _configuration = _configuration.copyWith(semanticsEnabled: semanticsEnabled); + if (_onSemanticsEnabledChanged != null) { + invokeOnSemanticsEnabledChanged(); + } + } + } + + /// The setting indicating the current brightness mode of the host platform. + /// If the platform has no preference, [platformBrightness] defaults to [Brightness.light]. + @override + ui.Brightness get platformBrightness => configuration.platformBrightness; + + /// Updates [_platformBrightness] and invokes [onPlatformBrightnessChanged] + /// callback if [_platformBrightness] changed. + void _updatePlatformBrightness(ui.Brightness value) { + if (configuration.platformBrightness != value) { + _configuration = configuration.copyWith(platformBrightness: value); + invokeOnPlatformConfigurationChanged(); + invokeOnPlatformBrightnessChanged(); + } + } + + /// Reference to css media query that indicates the user theme preference on the web. + final html.MediaQueryList _brightnessMediaQuery = + html.window.matchMedia('(prefers-color-scheme: dark)'); + + /// A callback that is invoked whenever [_brightnessMediaQuery] changes value. + /// + /// Updates the [_platformBrightness] with the new user preference. + html.EventListener? _brightnessMediaQueryListener; + + /// Set the callback function for listening changes in [_brightnessMediaQuery] value. + void _addBrightnessMediaQueryListener() { + _updatePlatformBrightness(_brightnessMediaQuery.matches + ? ui.Brightness.dark + : ui.Brightness.light); + + _brightnessMediaQueryListener = (html.Event event) { + final html.MediaQueryListEvent mqEvent = + event as html.MediaQueryListEvent; + _updatePlatformBrightness( + mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light); + }; + _brightnessMediaQuery.addListener(_brightnessMediaQueryListener); + registerHotRestartListener(() { + _removeBrightnessMediaQueryListener(); + }); + } + + /// Remove the callback function for listening changes in [_brightnessMediaQuery] value. + void _removeBrightnessMediaQueryListener() { + _brightnessMediaQuery.removeListener(_brightnessMediaQueryListener); + _brightnessMediaQueryListener = null; + } + + /// A callback that is invoked whenever [platformBrightness] changes value. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this callback is invoked. + @override + ui.VoidCallback? get onPlatformBrightnessChanged => + _onPlatformBrightnessChanged; + ui.VoidCallback? _onPlatformBrightnessChanged; + Zone? _onPlatformBrightnessChangedZone; + @override + set onPlatformBrightnessChanged(ui.VoidCallback? callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformBrightnessChanged() { + invoke(_onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); + } + + /// Whether the user has requested that [updateSemantics] be called when + /// the semantic contents of window changes. + /// + /// The [onSemanticsEnabledChanged] callback is called whenever this value + /// changes. + @override + bool get semanticsEnabled => configuration.semanticsEnabled; + + /// A callback that is invoked when the value of [semanticsEnabled] changes. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + @override + ui.VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + ui.VoidCallback? _onSemanticsEnabledChanged; + Zone? _onSemanticsEnabledChangedZone; + @override + set onSemanticsEnabledChanged(ui.VoidCallback? callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsEnabledChanged() { + invoke(_onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); + } + + /// A callback that is invoked whenever the user requests an action to be + /// performed. + /// + /// This callback is used when the user expresses the action they wish to + /// perform based on the semantics supplied by [updateSemantics]. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + @override + ui.SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction; + ui.SemanticsActionCallback? _onSemanticsAction; + Zone? _onSemanticsActionZone; + @override + set onSemanticsAction(ui.SemanticsActionCallback? callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsAction( + int id, ui.SemanticsAction action, ByteData? args) { + invoke3( + _onSemanticsAction, _onSemanticsActionZone, id, action, args); + } + + /// The route or path that the embedder requested when the application was + /// launched. + /// + /// This will be the string "`/`" if no particular route was requested. + /// + /// ## Android + /// + /// On Android, calling + /// [`FlutterView.setInitialRoute`](/javadoc/io/flutter/view/FlutterView.html#setInitialRoute-java.lang.String-) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `createFlutterView` method in your `FlutterActivity` + /// subclass is a suitable time to set the value. The application's + /// `AndroidManifest.xml` file must also be updated to have a suitable + /// [``](https://developer.android.com/guide/topics/manifest/intent-filter-element.html). + /// + /// ## iOS + /// + /// On iOS, calling + /// [`FlutterViewController.setInitialRoute`](/objcdoc/Classes/FlutterViewController.html#/c:objc%28cs%29FlutterViewController%28im%29setInitialRoute:) + /// will set this value. The value must be set sufficiently early, i.e. before + /// the [runApp] call is executed in Dart, for this to have any effect on the + /// framework. The `application:didFinishLaunchingWithOptions:` method is a + /// suitable time to set this value. + /// + /// See also: + /// + /// * [Navigator], a widget that handles routing. + /// * [SystemChannels.navigation], which handles subsequent navigation + /// requests from the embedder. + @override + String get defaultRouteName { + return _defaultRouteName ??= + (_windows[0]! as EngineFlutterWindow).browserHistory.currentPath; + } + + /// Lazily initialized when the `defaultRouteName` getter is invoked. + /// + /// The reason for the lazy initialization is to give enough time for the app + /// to set [locationStrategy] in `lib/src/ui/initialization.dart`. + String? _defaultRouteName; + + @visibleForTesting + late Rasterizer? rasterizer = useCanvasKit ? Rasterizer() : null; + + /// In Flutter, platform messages are exchanged between threads so the + /// messages and responses have to be exchanged asynchronously. We simulate + /// that by adding a zero-length delay to the reply. + void replyToPlatformMessage( + ui.PlatformMessageResponseCallback? callback, + ByteData? data, + ) { + Future.delayed(Duration.zero).then((_) { + if (callback != null) { + callback(data); + } + }); + } + + @override + ui.FrameData get frameData => const ui.FrameData.webOnly(); +} + +bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData? data) { + final MethodCall decoded = codec.decodeMethodCall(data); + final double ratio = double.parse(decoded.arguments as String); + switch (decoded.method) { + case 'setDevicePixelRatio': + window.debugOverrideDevicePixelRatio(ratio); + EnginePlatformDispatcher.instance.onMetricsChanged!(); + return true; + } + return false; +} + +/// Invokes [callback] inside the given [zone]. +void invoke(void Function()? callback, Zone? zone) { + if (callback == null) { + return; + } + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(); + } else { + zone!.runGuarded(callback); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg]. +void invoke1(void Function(A a)? callback, Zone? zone, A arg) { + if (callback == null) { + return; + } + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg); + } else { + zone!.runUnaryGuarded(callback, arg); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. +void invoke2( + void Function(A1 a1, A2 a2)? callback, Zone? zone, A1 arg1, A2 arg2) { + if (callback == null) { + return; + } + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2); + } else { + zone!.runGuarded(() { + callback(arg1, arg2); + }); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. +void invoke3(void Function(A1 a1, A2 a2, A3 a3)? callback, + Zone? zone, A1 arg1, A2 arg2, A3 arg3) { + if (callback == null) { + return; + } + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2, arg3); + } else { + zone!.runGuarded(() { + callback(arg1, arg2, arg3); + }); + } +} diff --git a/lib/web_ui/lib/src/engine/platform_views.dart b/lib/web_ui/lib/src/engine/platform_views.dart index cc6e904c9d76c..956e19d8e04f8 100644 --- a/lib/web_ui/lib/src/engine/platform_views.dart +++ b/lib/web_ui/lib/src/engine/platform_views.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - // TODO(yjbanov): The code in this file was temporarily moved to lib/web_ui/lib/ui.dart // during the NNBD migration so that `dart:ui` does not have to export // `dart:_engine`. NNBD does not allow exported non-migrated libraries diff --git a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart new file mode 100644 index 0000000000000..064bed679ac29 --- /dev/null +++ b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart @@ -0,0 +1,199 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../util.dart'; +import 'slots.dart'; + +/// A function which takes a unique `id` and some `params` and creates an HTML element. +/// +/// This is made available to end-users through dart:ui in web. +typedef ParameterizedPlatformViewFactory = html.Element Function( + int viewId, { + Object? params, +}); + +/// A function which takes a unique `id` and creates an HTML element. +/// +/// This is made available to end-users through dart:ui in web. +typedef PlatformViewFactory = html.Element Function(int viewId); + +/// This class handles the lifecycle of Platform Views in the DOM of a Flutter Web App. +/// +/// There are three important parts of Platform Views. This class manages two of +/// them: +/// +/// * `factories`: The functions used to render the contents of any given Platform +/// View by its `viewType`. +/// * `contents`: The result [html.Element] of calling a `factory` function. +/// +/// The third part is `slots`, which are created on demand by the +/// [createPlatformViewSlot] function. +/// +/// This class keeps a registry of `factories`, `contents` so the framework can +/// CRUD Platform Views as needed, regardless of the rendering backend. +class PlatformViewManager { + // The factory functions, indexed by the viewType + final Map _factories = {}; + + // The references to content tags, indexed by their framework-given ID. + final Map _contents = {}; + + /// Returns `true` if the passed in `viewType` has been registered before. + /// + /// See [registerViewFactory] to understand how factories are registered. + bool knowsViewType(String viewType) { + return _factories.containsKey(viewType); + } + + /// Returns `true` if the passed in `viewId` has been rendered (and not disposed) before. + /// + /// See [renderContent] and [createPlatformViewSlot] to understand how platform views are + /// rendered. + bool knowsViewId(int viewId) { + return _contents.containsKey(viewId); + } + + /// Registers a `factoryFunction` that knows how to render a Platform View of `viewType`. + /// + /// `viewType` is selected by the programmer, but it can't be overridden once + /// it's been set. + /// + /// `factoryFunction` needs to be a [PlatformViewFactory]. + bool registerFactory(String viewType, Function factoryFunction) { + assert(factoryFunction is PlatformViewFactory || + factoryFunction is ParameterizedPlatformViewFactory); + + if (_factories.containsKey(viewType)) { + return false; + } + _factories[viewType] = factoryFunction; + return true; + } + + /// Creates the HTML markup for the `contents` of a Platform View. + /// + /// The result of this call is cached in the `_contents` Map. This is only + /// cached so it can be disposed of later by [clearPlatformView]. _Note that + /// there's no `getContents` function in this class._ + /// + /// The resulting DOM for the `contents` of a Platform View looks like this: + /// + /// ```html + /// + /// + /// + /// ``` + /// + /// The `arbitrary-html-elements` are the result of the call to the user-supplied + /// `factory` function for this Platform View (see [registerFactory]). + /// + /// The outer `flt-platform-view` tag is a simple wrapper that we add to have + /// a place where to attach the `slot` property, that will tell the browser + /// what `slot` tag will reveal this `contents`, **without modifying the returned + /// html from the `factory` function**. + html.Element renderContent( + String viewType, + int viewId, + Object? params, + ) { + assert(knowsViewType(viewType), + 'Attempted to render contents of unregistered viewType: $viewType'); + + final String slotName = getPlatformViewSlotName(viewId); + + return _contents.putIfAbsent(viewId, () { + final html.Element wrapper = html.document + .createElement('flt-platform-view') + ..setAttribute('slot', slotName); + + final Function factoryFunction = _factories[viewType]!; + late html.Element content; + + if (factoryFunction is ParameterizedPlatformViewFactory) { + content = factoryFunction(viewId, params: params); + } else { + content = (factoryFunction as PlatformViewFactory).call(viewId); + } + + _ensureContentCorrectlySized(content, viewType); + + return wrapper..append(content); + }); + } + + /// Removes a PlatformView by its `viewId` from the manager, and from the DOM. + /// + /// Once a view has been cleared, calls [knowsViewId] will fail, as if it had + /// never been rendered before. + void clearPlatformView(int viewId) { + // Remove from our cache, and then from the DOM... + final html.Element? element = _contents.remove(viewId); + _safelyRemoveSlottedElement(element); + } + + // We need to remove slotted elements like this because of a Safari bug that + // gets triggered when a slotted element is removed in a JS event different + // than its slot (after the slot is removed). + // + // TODO(web): Cleanup https://github.com/flutter/flutter/issues/85816 + void _safelyRemoveSlottedElement(html.Element? element) { + if (element == null) { + return; + } + if (browserEngine != BrowserEngine.webkit) { + element.remove(); + return; + } + final String tombstoneName = "tombstone-${element.getAttribute('slot')}"; + // Create and inject a new slot in the shadow root + final html.Element slot = html.document.createElement('slot') + ..style.display = 'none' + ..setAttribute('name', tombstoneName); + domRenderer.glassPaneShadow!.append(slot); + // Link the element to the new slot + element.setAttribute('slot', tombstoneName); + // Delete both the element, and the new slot + element.remove(); + slot.remove(); + } + + /// Attempt to ensure that the contents of the user-supplied DOM element will + /// fill the space allocated for this platform view by the framework. + void _ensureContentCorrectlySized(html.Element content, String viewType) { + // Scrutinize closely any other modifications to `content`. + // We shouldn't modify users' returned `content` if at all possible. + // Note there's also no getContent(viewId) function anymore, to prevent + // from later modifications too. + if (content.style.height.isEmpty) { + printWarning('Height of Platform View type: [$viewType] may not be set.' + ' Defaulting to `height: 100%`.\n' + 'Set `style.height` to any appropriate value to stop this message.'); + + content.style.height = '100%'; + } + + if (content.style.width.isEmpty) { + printWarning('Width of Platform View type: [$viewType] may not be set.' + ' Defaulting to `width: 100%`.\n' + 'Set `style.width` to any appropriate value to stop this message.'); + + content.style.width = '100%'; + } + } + + /// Clears the state. Used in tests. + /// + /// Returns the set of know view ids, so they can be cleaned up. + Set debugClear() { + final Set result = _contents.keys.toSet(); + result.forEach(clearPlatformView); + _factories.clear(); + _contents.clear(); + return result; + } +} diff --git a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart new file mode 100644 index 0000000000000..a46fff244a0ce --- /dev/null +++ b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart @@ -0,0 +1,149 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:typed_data'; + +import '../services.dart'; +import '../util.dart'; +import 'content_manager.dart'; + +/// The signature for a callback for a Platform Message. From the `ui` package. +/// Copied here so there's no circular dependencies. +typedef _PlatformMessageResponseCallback = void Function(ByteData? data); + +/// A function that handle a newly created [html.Element] with the contents of a +/// platform view with a unique [int] id. +typedef PlatformViewContentHandler = void Function(html.Element); + +/// This class handles incoming framework messages to create/dispose Platform Views. +/// +/// (An instance of this class is connected to the `flutter/platform_views` +/// Platform Channel in the [EnginePlatformDispatcher] class.) +/// +/// It uses a [PlatformViewManager] to handle the CRUD of the DOM of Platform Views. +/// This `contentManager` is shared across the engine, to perform +/// all operations related to platform views (registration, rendering, etc...), +/// regardless of the rendering backend. +/// +/// When the `contents` of a Platform View are created, a [PlatformViewContentHandler] +/// function (passed from the outside) will decide where in the DOM to inject +/// said content. +/// +/// The rendering/compositing of Platform Views can create the other "half" of a +/// Platform View: the `slot`, through the [createPlatformViewSlot] method. +/// +/// When a Platform View is disposed of, it is removed from the cache (and DOM) +/// directly by the `contentManager`. The canvaskit rendering backend needs to do +/// some extra cleanup of its internal state, but it can do it automatically. See +/// [HtmlViewEmbedder.disposeViews] +class PlatformViewMessageHandler { + final MethodCodec _codec = const StandardMethodCodec(); + + final PlatformViewManager _contentManager; + final PlatformViewContentHandler? _contentHandler; + + PlatformViewMessageHandler({ + required PlatformViewManager contentManager, + PlatformViewContentHandler? contentHandler, + }) : _contentManager = contentManager, + _contentHandler = contentHandler; + + /// Handle a `create` Platform View message. + /// + /// This will attempt to render the `contents` and of a Platform View, if its + /// `viewType` has been registered previously. + /// + /// (See [PlatformViewContentManager.registerFactory] for more details.) + /// + /// The `contents` are delegated to a [_contentHandler] function, so the + /// active rendering backend can inject them in the right place of the DOM. + /// + /// If all goes well, this function will `callback` with an empty success envelope. + /// In case of error, this will `callback` with an error envelope describing the error. + void _createPlatformView( + MethodCall methodCall, + _PlatformMessageResponseCallback callback, + ) { + final Map args = methodCall.arguments as Map; + final int viewId = args.readInt('id'); + final String viewType = args.readString('viewType'); + + if (!_contentManager.knowsViewType(viewType)) { + callback(_codec.encodeErrorEnvelope( + code: 'unregistered_view_type', + message: 'trying to create a view with an unregistered type', + details: 'unregistered view type: $viewType', + )); + return; + } + + if (_contentManager.knowsViewId(viewId)) { + callback(_codec.encodeErrorEnvelope( + code: 'recreating_view', + message: 'trying to create an already created view', + details: 'view id: $viewId', + )); + return; + } + + // TODO(hterkelsen): How can users add extra `args` from the HtmlElementView widget? + final html.Element content = _contentManager.renderContent( + viewType, + viewId, + args, + ); + + // For now, we don't need anything fancier. If needed, this can be converted + // to a PlatformViewStrategy class for each web-renderer backend? + if (_contentHandler != null) { + _contentHandler!(content); + } + callback(_codec.encodeSuccessEnvelope(null)); + } + + /// Handle a `dispose` Platform View message. + /// + /// This will clear the cached information that the framework has about a given + /// `viewId`, through the [_contentManager]. + /// + /// Once that's done, the dispose call is delegated to the [_disposeHandler] + /// function, so the active rendering backend can dispose of whatever resources + /// it needed to get ahold of. + /// + /// This function should always `callback` with an empty success envelope. + void _disposePlatformView( + MethodCall methodCall, + _PlatformMessageResponseCallback callback, + ) { + final int viewId = methodCall.arguments as int; + + // The contentManager removes the slot and the contents from its internal + // cache, and the DOM. + _contentManager.clearPlatformView(viewId); + + callback(_codec.encodeSuccessEnvelope(null)); + } + + /// Handles a PlatformViewCall to the `flutter/platform_views` channel. + /// + /// This method handles two possible messages: + /// * `create`: See [_createPlatformView] + /// * `dispose`: See [_disposePlatformView] + void handlePlatformViewCall( + ByteData? data, + _PlatformMessageResponseCallback callback, + ) { + final MethodCall decoded = _codec.decodeMethodCall(data); + switch (decoded.method) { + case 'create': + _createPlatformView(decoded, callback); + return; + case 'dispose': + _disposePlatformView(decoded, callback); + return; + } + callback(null); + } +} diff --git a/lib/web_ui/lib/src/engine/platform_views/slots.dart b/lib/web_ui/lib/src/engine/platform_views/slots.dart new file mode 100644 index 0000000000000..3b1a08b47b79f --- /dev/null +++ b/lib/web_ui/lib/src/engine/platform_views/slots.dart @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +/// Returns the name of a slot from its `viewId`. +/// +/// This is used by the [renderContent] function of the [PlatformViewManager] +/// class, and the [createPlatformViewSlot] method below, to keep the slot name +/// attribute consistent across the framework. +String getPlatformViewSlotName(int viewId) { + return 'flt-pv-slot-$viewId'; +} + +/// Creates the HTML markup for the `slot` of a Platform View. +/// +/// The resulting DOM for a `slot` looks like this: +/// +/// ```html +/// +/// +/// +/// ``` +/// +/// The inner `SLOT` tag is standard HTML to reveal an element that is rendered +/// elsewhere in the DOM. Its `name` attribute must match the value of the `slot` +/// attribute of the contents being revealed (see [getPlatformViewSlotName].) +/// +/// The outer `flt-platform-view-slot` tag is a simple wrapper that the framework +/// can position/style as needed. +/// +/// (When the framework accesses a `slot`, it's really accessing its wrapper +/// `flt-platform-view-slot` tag) +html.Element createPlatformViewSlot(int viewId) { + final String slotName = getPlatformViewSlotName(viewId); + + final html.Element wrapper = html.document + .createElement('flt-platform-view-slot') + ..style.pointerEvents = 'auto'; + + final html.Element slot = html.document.createElement('slot') + ..setAttribute('name', slotName); + + return wrapper..append(slot); +} diff --git a/lib/web_ui/lib/src/engine/plugins.dart b/lib/web_ui/lib/src/engine/plugins.dart index ec39df944124f..083816b1c29d3 100644 --- a/lib/web_ui/lib/src/engine/plugins.dart +++ b/lib/web_ui/lib/src/engine/plugins.dart @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; Future Function(String, ByteData?, ui.PlatformMessageResponseCallback?)? pluginMessageCallHandler; diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index f6bb53110491b..a70e644daf82d 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -2,8 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:js' as js; +import 'dart:js_util' as js_util; +import 'dart:math' as math; + +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import '../engine.dart' show registerHotRestartListener; +import 'browser_detection.dart'; +import 'platform_dispatcher.dart'; +import 'pointer_converter.dart'; +import 'semantics.dart'; /// Set this flag to true to see all the fired events in the console. const bool _debugLogPointerEvents = false; @@ -125,9 +136,7 @@ class PointerBinding { void _onPointerData(Iterable data) { final ui.PointerDataPacket packet = ui.PointerDataPacket(data: data.toList()); - if (window._onPointerDataPacket != null) { - window.invokeOnPointerDataPacket(packet); - } + EnginePlatformDispatcher.instance.invokeOnPointerDataPacket(packet); } } @@ -195,13 +204,19 @@ abstract class _BaseAdapter { html.EventListener handler, { bool acceptOutsideGlasspane = false, }) { - final html.EventListener loggedHandler = (html.Event event) { + dynamic loggedHandler(html.Event event) { if (!acceptOutsideGlasspane && !glassPaneElement.contains(event.target as html.Node?)) { - return; + return null; } if (_debugLogPointerEvents) { - print(event.type); + if (event is html.PointerEvent) { + print('${event.type} ' + '${event.client.x.toStringAsFixed(1)},' + '${event.client.y.toStringAsFixed(1)}'); + } else { + print(event.type); + } } // Report the event to semantics. This information is used to debounce // browser gestures. Semantics tells us whether it is safe to forward @@ -209,7 +224,7 @@ abstract class _BaseAdapter { if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) { handler(event); } - }; + } _listeners[eventName] = loggedHandler; // We have to attach the event listener on the window instead of the // glasspane element. That's because "up" events that occur outside the @@ -229,6 +244,8 @@ abstract class _BaseAdapter { } mixin _WheelEventListenerMixin on _BaseAdapter { + static double? _defaultScrollLineHeight; + List _convertWheelEventToPointerData( html.WheelEvent event ) { @@ -242,8 +259,9 @@ mixin _WheelEventListenerMixin on _BaseAdapter { double deltaY = event.deltaY as double; switch (event.deltaMode) { case domDeltaLine: - deltaX *= 32.0; - deltaY *= 32.0; + _defaultScrollLineHeight ??= _computeDefaultScrollLineHeight(); + deltaX *= _defaultScrollLineHeight!; + deltaY *= _defaultScrollLineHeight!; break; case domDeltaPage: deltaX *= ui.window.physicalSize.width; @@ -253,6 +271,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter { default: break; } + final List data = []; _pointerDataConverter.convert( data, @@ -274,7 +293,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter { } void _addWheelEventListener(html.EventListener handler) { - final dynamic eventOptions = js_util.newObject(); + final Object eventOptions = js_util.newObject() as Object; final html.EventListener jsHandler = js.allowInterop((html.Event event) => handler(event)); _BaseAdapter._nativeListeners['wheel'] = jsHandler; js_util.setProperty(eventOptions, 'passive', false); @@ -287,6 +306,48 @@ mixin _WheelEventListenerMixin on _BaseAdapter { ] ); } + + void _handleWheelEvent(html.Event e) { + assert(e is html.WheelEvent); + final html.WheelEvent event = e as html.WheelEvent; + if (_debugLogPointerEvents) { + print(event.type); + } + _callback(_convertWheelEventToPointerData(event)); + if (event.getModifierState('Control') && + operatingSystem != OperatingSystem.macOs && + operatingSystem != OperatingSystem.iOs) { + // Ignore Control+wheel events since the default handler + // will change browser zoom level instead of scrolling. + // The exception is MacOs where Control+wheel will still scroll and zoom. + return; + } + // Prevent default so mouse wheel event doesn't get converted to + // a scroll event that semantic nodes would process. + // + event.preventDefault(); + } + + /// For browsers that report delta line instead of pixels such as FireFox + /// compute line height using the default font size. + /// + /// Use Firefox to test this code path. + double _computeDefaultScrollLineHeight() { + const double kFallbackFontHeight = 16.0; + final html.DivElement probe = html.DivElement(); + probe.style + ..fontSize = 'initial' + ..display = 'none'; + html.document.body!.append(probe); + String fontSize = probe.getComputedStyle().fontSize; + double? res; + if (fontSize.contains('px')) { + fontSize = fontSize.replaceAll('px', ''); + res = double.tryParse(fontSize); + } + probe.remove(); + return res == null ? kFallbackFontHeight : res / 4.0; + } } @immutable @@ -336,6 +397,7 @@ class _ButtonSanitizer { } _pressedButtons = _inferDownFlutterButtons(button, buttons); + return _SanitizedDetails( change: ui.PointerChange.down, buttons: _pressedButtons, @@ -344,18 +406,6 @@ class _ButtonSanitizer { _SanitizedDetails sanitizeMoveEvent({required int buttons}) { final int newPressedButtons = _htmlButtonsToFlutterButtons(buttons); - // This could happen when the context menu is active and the user clicks - // RMB somewhere else. The browser sends a down event with `buttons:0`. - // - // In this case, we keep the old `buttons` value so we don't confuse the - // framework. - if (_pressedButtons != 0 && newPressedButtons == 0) { - return _SanitizedDetails( - change: ui.PointerChange.move, - buttons: _pressedButtons, - ); - } - // This could happen when the user clicks RMB then moves the mouse quickly. // The brower sends a move event with `buttons:2` even though there's no // buttons down yet. @@ -367,6 +417,7 @@ class _ButtonSanitizer { } _pressedButtons = newPressedButtons; + return _SanitizedDetails( change: _pressedButtons == 0 ? ui.PointerChange.hover @@ -375,17 +426,43 @@ class _ButtonSanitizer { ); } - _SanitizedDetails? sanitizeUpEvent() { + _SanitizedDetails? sanitizeMissingRightClickUp({required int buttons}) { + final int newPressedButtons = _htmlButtonsToFlutterButtons(buttons); + // This could happen when RMB is clicked and released but no pointerup + // event was received because context menu was shown. + if (_pressedButtons != 0 && newPressedButtons == 0) { + _pressedButtons = 0; + return _SanitizedDetails( + change: ui.PointerChange.up, + buttons: _pressedButtons, + ); + } + return null; + } + + _SanitizedDetails? sanitizeUpEvent({required int? buttons}) { // The pointer could have been released by a `pointerout` event, in which // case `pointerup` should have no effect. if (_pressedButtons == 0) { return null; } - _pressedButtons = 0; - return _SanitizedDetails( - change: ui.PointerChange.up, - buttons: _pressedButtons, - ); + + _pressedButtons = _htmlButtonsToFlutterButtons(buttons ?? 0); + + if (_pressedButtons == 0) { + // All buttons have been released. + return _SanitizedDetails( + change: ui.PointerChange.up, + buttons: _pressedButtons, + ); + } else { + // There are still some unreleased buttons, we shouldn't send an up event + // yet. Instead we send a move event to update the position of the pointer. + return _SanitizedDetails( + change: ui.PointerChange.move, + buttons: _pressedButtons, + ); + } } _SanitizedDetails sanitizeCancelEvent() { @@ -444,45 +521,54 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { @override void setup() { _addPointerEventListener('pointerdown', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final List pointerData = []; - final _SanitizedDetails details = - _ensureSanitizer(device).sanitizeDownEvent( + final _ButtonSanitizer sanitizer = _ensureSanitizer(device); + final _SanitizedDetails? up = + sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!); + if (up != null) { + _convertEventsToPointerData(data: pointerData, event: event, details: up); + } + final _SanitizedDetails down = + sanitizer.sanitizeDownEvent( button: event.button, buttons: event.buttons!, ); - _convertEventsToPointerData(data: pointerData, event: event, details: details); + _convertEventsToPointerData(data: pointerData, event: event, details: down); _callback(pointerData); }); _addPointerEventListener('pointermove', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final _ButtonSanitizer sanitizer = _ensureSanitizer(device); final List pointerData = []; - final Iterable<_SanitizedDetails> detailsList = _expandEvents(event).map( - (html.PointerEvent expandedEvent) => sanitizer.sanitizeMoveEvent(buttons: expandedEvent.buttons!), - ); - for (_SanitizedDetails details in detailsList) { - _convertEventsToPointerData(data: pointerData, event: event, details: details); + final List expandedEvents = _expandEvents(event); + for (final html.PointerEvent event in expandedEvents) { + final _SanitizedDetails? up = sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!); + if (up != null) { + _convertEventsToPointerData(data: pointerData, event: event, details: up); + } + final _SanitizedDetails move = sanitizer.sanitizeMoveEvent(buttons: event.buttons!); + _convertEventsToPointerData(data: pointerData, event: event, details: move); } _callback(pointerData); }, acceptOutsideGlasspane: true); _addPointerEventListener('pointerup', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final List pointerData = []; - final _SanitizedDetails? details = _getSanitizer(device).sanitizeUpEvent(); + final _SanitizedDetails? details = _getSanitizer(device).sanitizeUpEvent(buttons: event.buttons); _removePointerIfUnhoverable(event); if (details != null) { _convertEventsToPointerData(data: pointerData, event: event, details: details); + _callback(pointerData); } - _callback(pointerData); }, acceptOutsideGlasspane: true); // A browser fires cancel event if it concludes the pointer will no longer // be able to generate events (example: device is deactivated) _addPointerEventListener('pointercancel', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final List pointerData = []; final _SanitizedDetails details = _getSanitizer(device).sanitizeCancelEvent(); _removePointerIfUnhoverable(event); @@ -491,14 +577,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { }); _addWheelEventListener((html.Event event) { - assert(event is html.WheelEvent); - if (_debugLogPointerEvents) { - print(event.type); - } - _callback(_convertWheelEventToPointerData(event as html.WheelEvent)); - // Prevent default so mouse wheel event doesn't get converted to - // a scroll event that semantic nodes would process. - event.preventDefault(); + _handleWheelEvent(event); }); } @@ -513,23 +592,20 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { assert(event != null); // ignore: unnecessary_null_comparison assert(details != null); // ignore: unnecessary_null_comparison final ui.PointerDeviceKind kind = _pointerTypeToDeviceKind(event.pointerType!); - // We force `device: _mouseDeviceId` on mouse pointers because Wheel events - // might come before any PointerEvents, and since wheel events don't contain - // pointerId we always assign `device: _mouseDeviceId` to them. - final int device = kind == ui.PointerDeviceKind.mouse ? _mouseDeviceId : event.pointerId!; final double tilt = _computeHighestTilt(event); final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!); + final num? pressure = event.pressure; _pointerDataConverter.convert( data, change: details.change, timeStamp: timeStamp, kind: kind, signalKind: ui.PointerSignalKind.none, - device: device, + device: _getPointerId(event), physicalX: event.client.x.toDouble() * ui.window.devicePixelRatio, physicalY: event.client.y.toDouble() * ui.window.devicePixelRatio, buttons: details.buttons, - pressure: event.pressure as double, + pressure: pressure == null ? 0.0 : pressure.toDouble(), pressureMin: 0.0, pressureMax: 1.0, tilt: tilt, @@ -564,6 +640,14 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { } } + int _getPointerId(html.PointerEvent event) { + // We force `device: _mouseDeviceId` on mouse pointers because Wheel events + // might come before any PointerEvents, and since wheel events don't contain + // pointerId we always assign `device: _mouseDeviceId` to them. + final ui.PointerDeviceKind kind = _pointerTypeToDeviceKind(event.pointerType!); + return kind == ui.PointerDeviceKind.mouse ? _mouseDeviceId : event.pointerId!; + } + /// Tilt angle is -90 to + 90. Take maximum deflection and convert to radians. double _computeHighestTilt(html.PointerEvent e) => (e.tiltX!.abs() > e.tiltY!.abs() ? e.tiltX : e.tiltY)!.toDouble() / @@ -598,8 +682,8 @@ class _TouchAdapter extends _BaseAdapter { _addTouchEventListener('touchstart', (html.TouchEvent event) { final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!); final List pointerData = []; - for (html.Touch touch in event.changedTouches!) { - final nowPressed = _isTouchPressed(touch.identifier!); + for (final html.Touch touch in event.changedTouches!) { + final bool nowPressed = _isTouchPressed(touch.identifier!); if (!nowPressed) { _pressTouch(touch.identifier!); _convertEventToPointerData( @@ -618,8 +702,8 @@ class _TouchAdapter extends _BaseAdapter { event.preventDefault(); // Prevents standard overscroll on iOS/Webkit. final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!); final List pointerData = []; - for (html.Touch touch in event.changedTouches!) { - final nowPressed = _isTouchPressed(touch.identifier!); + for (final html.Touch touch in event.changedTouches!) { + final bool nowPressed = _isTouchPressed(touch.identifier!); if (nowPressed) { _convertEventToPointerData( data: pointerData, @@ -639,8 +723,8 @@ class _TouchAdapter extends _BaseAdapter { event.preventDefault(); final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!); final List pointerData = []; - for (html.Touch touch in event.changedTouches!) { - final nowPressed = _isTouchPressed(touch.identifier!); + for (final html.Touch touch in event.changedTouches!) { + final bool nowPressed = _isTouchPressed(touch.identifier!); if (nowPressed) { _unpressTouch(touch.identifier!); _convertEventToPointerData( @@ -658,8 +742,8 @@ class _TouchAdapter extends _BaseAdapter { _addTouchEventListener('touchcancel', (html.TouchEvent event) { final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!); final List pointerData = []; - for (html.Touch touch in event.changedTouches!) { - final nowPressed = _isTouchPressed(touch.identifier!); + for (final html.Touch touch in event.changedTouches!) { + final bool nowPressed = _isTouchPressed(touch.identifier!); if (nowPressed) { _unpressTouch(touch.identifier!); _convertEventToPointerData( @@ -743,6 +827,11 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { void setup() { _addMouseEventListener('mousedown', (html.MouseEvent event) { final List pointerData = []; + final _SanitizedDetails? up = + _sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!); + if (up != null) { + _convertEventsToPointerData(data: pointerData, event: event, details: up); + } final _SanitizedDetails sanitizedDetails = _sanitizer.sanitizeDownEvent( button: event.button, @@ -754,30 +843,26 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { _addMouseEventListener('mousemove', (html.MouseEvent event) { final List pointerData = []; - final _SanitizedDetails sanitizedDetails = _sanitizer.sanitizeMoveEvent(buttons: event.buttons!); - _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); + final _SanitizedDetails? up = _sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!); + if (up != null) { + _convertEventsToPointerData(data: pointerData, event: event, details: up); + } + final _SanitizedDetails move = _sanitizer.sanitizeMoveEvent(buttons: event.buttons!); + _convertEventsToPointerData(data: pointerData, event: event, details: move); _callback(pointerData); }, acceptOutsideGlasspane: true); _addMouseEventListener('mouseup', (html.MouseEvent event) { final List pointerData = []; - final bool isEndOfDrag = event.buttons == 0; - final _SanitizedDetails sanitizedDetails = isEndOfDrag ? - _sanitizer.sanitizeUpEvent()! : - _sanitizer.sanitizeMoveEvent(buttons: event.buttons!); - _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); - _callback(pointerData); + final _SanitizedDetails? sanitizedDetails = _sanitizer.sanitizeUpEvent(buttons: event.buttons); + if (sanitizedDetails != null) { + _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); + _callback(pointerData); + } }, acceptOutsideGlasspane: true); _addWheelEventListener((html.Event event) { - assert(event is html.WheelEvent); - if (_debugLogPointerEvents) { - print(event.type); - } - _callback(_convertWheelEventToPointerData(event as html.WheelEvent)); - // Prevent default so mouse wheel event doesn't get converted to - // a scroll event that semantic nodes would process. - event.preventDefault(); + _handleWheelEvent(event); }); } diff --git a/lib/web_ui/lib/src/engine/pointer_converter.dart b/lib/web_ui/lib/src/engine/pointer_converter.dart index 0140663a2bb5d..5b944f0767652 100644 --- a/lib/web_ui/lib/src/engine/pointer_converter.dart +++ b/lib/web_ui/lib/src/engine/pointer_converter.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +const bool _debugLogPointerConverter = false; class _PointerState { _PointerState(this.x, this.y); @@ -17,8 +18,6 @@ class _PointerState { _pointer = _pointerCount; } - bool down = false; - double x; double y; } @@ -48,6 +47,14 @@ class PointerDataConverter { // Map from browser pointer identifiers to PointerEvent pointer identifiers. final Map _pointers = {}; + /// This field is used to keep track of button state. + /// + /// To normalize pointer events, when we receive pointer down followed by + /// pointer up, we synthesize a move event. To make sure that button state + /// is correct for move regardless of button state at the time of up event + /// we store it on down,hover and move events. + int _activeButtons = 0; + /// Clears the existing pointer states. /// /// This method is invoked during hot reload to make sure we have a clean @@ -55,6 +62,7 @@ class PointerDataConverter { void clearPointerState() { _pointers.clear(); _PointerState._pointerCount = 0; + _activeButtons = 0; } _PointerState _ensureStateForPointer(int device, double x, double y) { @@ -228,6 +236,10 @@ class PointerDataConverter { double scrollDeltaX = 0.0, double scrollDeltaY = 0.0, }) { + if (_debugLogPointerConverter) { + print('>> device=$device change=$change buttons=$buttons'); + } + final bool isDown = buttons != 0; assert(change != null); // ignore: unnecessary_null_comparison if (signalKind == null || signalKind == ui.PointerSignalKind.none) { @@ -267,9 +279,8 @@ class PointerDataConverter { break; case ui.PointerChange.hover: final bool alreadyAdded = _pointers.containsKey(device); - final _PointerState state = _ensureStateForPointer( - device, physicalX, physicalY); - assert(!state.down); + _ensureStateForPointer(device, physicalX, physicalY); + assert(!isDown); if (!alreadyAdded) { // Synthesizes an add pointer data. result.add( @@ -328,12 +339,13 @@ class PointerDataConverter { scrollDeltaY: scrollDeltaY, ) ); + _activeButtons = buttons; break; case ui.PointerChange.down: final bool alreadyAdded = _pointers.containsKey(device); final _PointerState state = _ensureStateForPointer( device, physicalX, physicalY); - assert(!state.down); + assert(isDown); state.startNewPointer(); if (!alreadyAdded) { // Synthesizes an add pointer data. @@ -397,7 +409,6 @@ class PointerDataConverter { ) ); } - state.down = true; result.add( _generateCompletePointerData( timeStamp: timeStamp, @@ -426,11 +437,11 @@ class PointerDataConverter { scrollDeltaY: scrollDeltaY, ) ); + _activeButtons = buttons; break; case ui.PointerChange.move: assert(_pointers.containsKey(device)); - final _PointerState state = _pointers[device]!; - assert(state.down); + assert(isDown); result.add( _generateCompletePointerData( timeStamp: timeStamp, @@ -459,12 +470,13 @@ class PointerDataConverter { scrollDeltaY: scrollDeltaY, ) ); + _activeButtons = buttons; break; case ui.PointerChange.up: case ui.PointerChange.cancel: assert(_pointers.containsKey(device)); final _PointerState state = _pointers[device]!; - assert(state.down); + assert(!isDown); // Cancel events can have different coordinates due to various // reasons (window lost focus which is accompanied by window // movement, or PointerEvent simply always gives 0). Instead of @@ -485,7 +497,7 @@ class PointerDataConverter { device: device, physicalX: physicalX, physicalY: physicalY, - buttons: buttons, + buttons: _activeButtons, obscured: obscured, pressure: pressure, pressureMin: pressureMin, @@ -505,7 +517,6 @@ class PointerDataConverter { ) ); } - state.down = false; result.add( _generateCompletePointerData( timeStamp: timeStamp, @@ -571,7 +582,7 @@ class PointerDataConverter { case ui.PointerChange.remove: assert(_pointers.containsKey(device)); final _PointerState state = _pointers[device]!; - assert(!state.down); + assert(!isDown); result.add( _generateCompletePointerData( timeStamp: timeStamp, @@ -607,8 +618,7 @@ class PointerDataConverter { switch (signalKind) { case ui.PointerSignalKind.scroll: final bool alreadyAdded = _pointers.containsKey(device); - final _PointerState state = _ensureStateForPointer( - device, physicalX, physicalY); + _ensureStateForPointer(device, physicalX, physicalY); if (!alreadyAdded) { // Synthesizes an add pointer data. result.add( @@ -644,7 +654,7 @@ class PointerDataConverter { // before sending the scroll event, if necessary, so that clients // don't have to worry about native ordering of hover and scroll // events. - if (state.down) { + if (isDown) { result.add( _synthesizePointerData( timeStamp: timeStamp, diff --git a/lib/web_ui/lib/src/engine/profiler.dart b/lib/web_ui/lib/src/engine/profiler.dart index c0071fcffefe7..b5f14383d3811 100644 --- a/lib/web_ui/lib/src/engine/profiler.dart +++ b/lib/web_ui/lib/src/engine/profiler.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:ui/ui.dart' as ui; + +import 'platform_dispatcher.dart'; /// A function that receives a benchmark [value] labeleb by [name]. typedef OnBenchmark = void Function(String name, double value); @@ -101,9 +106,204 @@ class Profiler { _checkBenchmarkMode(); final OnBenchmark? onBenchmark = - js_util.getProperty(html.window, '_flutter_internal_on_benchmark'); + js_util.getProperty(html.window, '_flutter_internal_on_benchmark') as OnBenchmark?; if (onBenchmark != null) { onBenchmark(name, value); } } } + +/// Whether we are collecting [ui.FrameTiming]s. +bool get _frameTimingsEnabled { + return EnginePlatformDispatcher.instance.onReportTimings != null; +} + +/// Collects frame timings from frames. +/// +/// This list is periodically reported to the framework (see +/// [_kFrameTimingsSubmitInterval]). +List _frameTimings = []; + +/// The amount of time in microseconds we wait between submitting +/// frame timings. +const int _kFrameTimingsSubmitInterval = 100000; // 100 milliseconds + +/// The last time (in microseconds) we submitted frame timings. +int _frameTimingsLastSubmitTime = _nowMicros(); + +// These variables store individual [ui.FrameTiming] properties. +int _vsyncStartMicros = -1; +int _buildStartMicros = -1; +int _buildFinishMicros = -1; +int _rasterStartMicros = -1; +int _rasterFinishMicros = -1; + +/// Records the vsync timestamp for this frame. +void frameTimingsOnVsync() { + if (!_frameTimingsEnabled) { + return; + } + _vsyncStartMicros = _nowMicros(); +} + +/// Records the time when the framework started building the frame. +void frameTimingsOnBuildStart() { + if (!_frameTimingsEnabled) { + return; + } + _buildStartMicros = _nowMicros(); +} + +/// Records the time when the framework finished building the frame. +void frameTimingsOnBuildFinish() { + if (!_frameTimingsEnabled) { + return; + } + _buildFinishMicros = _nowMicros(); +} + +/// Records the time when the framework started rasterizing the frame. +/// +/// On the web, this value is almost always the same as [_buildFinishMicros] +/// because it's single-threaded so there's no delay between building +/// and rasterization. +/// +/// This also means different things between HTML and CanvasKit renderers. +/// +/// In HTML "rasterization" only captures DOM updates, but not the work that +/// the browser performs after the DOM updates are committed. The browser +/// does not report that information. +/// +/// CanvasKit captures everything because we control the rasterization +/// process, so we know exactly when rasterization starts and ends. +void frameTimingsOnRasterStart() { + if (!_frameTimingsEnabled) { + return; + } + _rasterStartMicros = _nowMicros(); +} + +/// Records the time when the framework started rasterizing the frame. +/// +/// See [_frameTimingsOnRasterStart] for more details on what rasterization +/// timings mean on the web. +void frameTimingsOnRasterFinish() { + if (!_frameTimingsEnabled) { + return; + } + final int now = _nowMicros(); + _rasterFinishMicros = now; + _frameTimings.add(ui.FrameTiming( + vsyncStart: _vsyncStartMicros, + buildStart: _buildStartMicros, + buildFinish: _buildFinishMicros, + rasterStart: _rasterStartMicros, + rasterFinish: _rasterFinishMicros, + rasterFinishWallTime: _rasterFinishMicros, + )); + _vsyncStartMicros = -1; + _buildStartMicros = -1; + _buildFinishMicros = -1; + _rasterStartMicros = -1; + _rasterFinishMicros = -1; + if (now - _frameTimingsLastSubmitTime > _kFrameTimingsSubmitInterval) { + _frameTimingsLastSubmitTime = now; + EnginePlatformDispatcher.instance.invokeOnReportTimings(_frameTimings); + _frameTimings = []; + } +} + +/// Current timestamp in microseconds taken from the high-precision +/// monotonically increasing timer. +/// +/// See also: +/// +/// * https://developer.mozilla.org/en-US/docs/Web/API/Performance/now, +/// particularly notes about Firefox rounding to 1ms for security reasons, +/// which can be bypassed in tests by setting certain browser options. +int _nowMicros() { + return (html.window.performance.now() * 1000).toInt(); +} + +/// Counts various events that take place while the app is running. +/// +/// This class will slow down the app, and therefore should be disabled while +/// benchmarking. For example, avoid using it in conjunction with [Profiler]. +class Instrumentation { + Instrumentation._() { + _checkInstrumentationEnabled(); + } + + /// Whether instrumentation is enabled. + /// + /// Check this value before calling any other methods in this class. + static bool get enabled => _enabled; + static set enabled(bool value) { + if (_enabled == value) { + return; + } + + if (!value) { + _instance._counters.clear(); + _instance._printTimer = null; + } + + _enabled = value; + } + static bool _enabled = const bool.fromEnvironment( + 'FLUTTER_WEB_ENABLE_INSTRUMENTATION', + defaultValue: false, + ); + + /// Returns the singleton that provides instrumentation API. + static Instrumentation get instance { + _checkInstrumentationEnabled(); + return _instance; + } + + static late final Instrumentation _instance = Instrumentation._(); + + static void _checkInstrumentationEnabled() { + if (!enabled) { + throw StateError( + 'Cannot use Instrumentation unless it is enabled. ' + 'You can enable it by setting the `FLUTTER_WEB_ENABLE_INSTRUMENTATION` ' + 'environment variable to true, or by passing ' + '--dart-define=FLUTTER_WEB_ENABLE_INSTRUMENTATION=true to the flutter ' + 'tool.', + ); + } + } + + Map get debugCounters => _counters; + final Map _counters = {}; + + Timer? get debugPrintTimer => _printTimer; + Timer? _printTimer; + + /// Increments the count of a particular event by one. + void incrementCounter(String event) { + _checkInstrumentationEnabled(); + final int currentCount = _counters[event] ?? 0; + _counters[event] = currentCount + 1; + _printTimer ??= Timer( + const Duration(seconds: 2), + () { + if (_printTimer == null || !_enabled) { + return; + } + final StringBuffer message = StringBuffer('Engine counters:\n'); + // Entries are sorted for readability and testability. + final List> entries = _counters.entries.toList() + ..sort((MapEntry a, MapEntry b) { + return a.key.compareTo(b.key); + }); + for (final MapEntry entry in entries) { + message.writeln(' ${entry.key}: ${entry.value}'); + } + print(message); + _printTimer = null; + }, + ); + } +} diff --git a/lib/web_ui/lib/src/engine/rrect_renderer.dart b/lib/web_ui/lib/src/engine/rrect_renderer.dart index 3ccc054331d41..c23514b6d7ab2 100644 --- a/lib/web_ui/lib/src/engine/rrect_renderer.dart +++ b/lib/web_ui/lib/src/engine/rrect_renderer.dart @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:ui/ui.dart' as ui; + +import 'dom_renderer.dart'; /// Renders an RRect using path primitives. -abstract class _RRectRenderer { +abstract class RRectRenderer { // To draw the rounded rectangle, perform the following steps: // 0. Ensure border radius don't overlap // 1. Flip left,right top,bottom since web doesn't support flipped @@ -178,21 +182,25 @@ abstract class _RRectRenderer { } /// Renders RRect to a 2d canvas. -class _RRectToCanvasRenderer extends _RRectRenderer { +class RRectToCanvasRenderer extends RRectRenderer { final html.CanvasRenderingContext2D context; - _RRectToCanvasRenderer(this.context); + RRectToCanvasRenderer(this.context); + @override void beginPath() { context.beginPath(); } + @override void moveTo(double x, double y) { context.moveTo(x, y); } + @override void lineTo(double x, double y) { context.lineTo(x, y); } + @override void ellipse(double centerX, double centerY, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool antiClockwise) { DomRenderer.ellipse(context, centerX, centerY, radiusX, radiusY, rotation, startAngle, @@ -201,18 +209,23 @@ class _RRectToCanvasRenderer extends _RRectRenderer { } /// Renders RRect to a path. -class _RRectToPathRenderer extends _RRectRenderer { +class RRectToPathRenderer extends RRectRenderer { final ui.Path path; - _RRectToPathRenderer(this.path); + RRectToPathRenderer(this.path); + @override void beginPath() {} + + @override void moveTo(double x, double y) { path.moveTo(x, y); } + @override void lineTo(double x, double y) { path.lineTo(x, y); } + @override void ellipse(double centerX, double centerY, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool antiClockwise) { path.addArc( @@ -227,7 +240,7 @@ typedef RRectRendererEllipseCallback = void Function(double centerX, double cent typedef RRectRendererCallback = void Function(double x, double y); /// Converts RRect to path primitives with callbacks. -class RRectMetricsRenderer extends _RRectRenderer { +class RRectMetricsRenderer extends RRectRenderer { RRectMetricsRenderer({this.moveToCallback, this.lineToCallback, this.ellipseCallback}); final RRectRendererEllipseCallback? ellipseCallback; diff --git a/lib/web_ui/lib/src/engine/semantics.dart b/lib/web_ui/lib/src/engine/semantics.dart new file mode 100644 index 0000000000000..224fd988cd5a0 --- /dev/null +++ b/lib/web_ui/lib/src/engine/semantics.dart @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'semantics/accessibility.dart'; +export 'semantics/checkable.dart'; +export 'semantics/image.dart'; +export 'semantics/incrementable.dart'; +export 'semantics/label_and_value.dart'; +export 'semantics/live_region.dart'; +export 'semantics/scrollable.dart'; +export 'semantics/semantics.dart'; +export 'semantics/semantics_helper.dart'; +export 'semantics/tappable.dart'; +export 'semantics/text_field.dart'; diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index 101ae7bded7f1..2a63357d53255 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import '../../engine.dart' show registerHotRestartListener; +import '../services.dart'; +import '../util.dart'; /// Singleton for accessing accessibility announcements from the platform. final AccessibilityAnnouncements accessibilityAnnouncements = @@ -54,9 +59,9 @@ class AccessibilityAnnouncements { /// Decodes the message coming from the 'flutter/accessibility' channel. void handleMessage(StandardMessageCodec codec, ByteData? data) { final Map inputMap = - codec.decodeMessage(data); - final Map dataMap = inputMap['data']; - final String? message = dataMap['message']; + codec.decodeMessage(data) as Map; + final Map dataMap = inputMap.readDynamicJson('data'); + final String? message = dataMap.tryString('message'); if (message != null && message.isNotEmpty) { _initLiveRegion(message); _removeElementTimer = Timer(durationA11yMessageIsOnDom, () { diff --git a/lib/web_ui/lib/src/engine/semantics/checkable.dart b/lib/web_ui/lib/src/engine/semantics/checkable.dart index 9c7c2c101d834..348e31224cca8 100644 --- a/lib/web_ui/lib/src/engine/semantics/checkable.dart +++ b/lib/web_ui/lib/src/engine/semantics/checkable.dart @@ -11,8 +11,11 @@ // framework. Currently the framework does not report the // grouping of radio buttons. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import 'semantics.dart'; /// The specific type of checkable control. enum _CheckableKind { diff --git a/lib/web_ui/lib/src/engine/semantics/image.dart b/lib/web_ui/lib/src/engine/semantics/image.dart index af469fcfd3547..e05787780c2ad 100644 --- a/lib/web_ui/lib/src/engine/semantics/image.dart +++ b/lib/web_ui/lib/src/engine/semantics/image.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'semantics.dart'; /// Represents semantic objects that deliver information in a visual manner. /// diff --git a/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/lib/web_ui/lib/src/engine/semantics/incrementable.dart index 77fbb6daf5e47..71c068cdf8004 100644 --- a/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../platform_dispatcher.dart'; +import 'semantics.dart'; /// Adds increment/decrement event handling to a semantics object. /// @@ -53,11 +57,11 @@ class Incrementable extends RoleManager { final int newInputValue = int.parse(_element.value!); if (newInputValue > _currentSurrogateValue) { _currentSurrogateValue += 1; - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.increase, null); } else if (newInputValue < _currentSurrogateValue) { _currentSurrogateValue -= 1; - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.decrease, null); } }); diff --git a/lib/web_ui/lib/src/engine/semantics/label_and_value.dart b/lib/web_ui/lib/src/engine/semantics/label_and_value.dart index 1e4d94060db8c..a9fab80ce7820 100644 --- a/lib/web_ui/lib/src/engine/semantics/label_and_value.dart +++ b/lib/web_ui/lib/src/engine/semantics/label_and_value.dart @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import 'semantics.dart'; /// Renders [_label] and [_value] to the semantics DOM. /// @@ -50,12 +53,9 @@ class LabelAndValue extends RoleManager { final bool hasValue = semanticsObject.hasValue; final bool hasLabel = semanticsObject.hasLabel; - // If the node is incrementable or a text field the value is reported to the - // browser via the respective role managers. We do not need to also render - // it again here. - final bool shouldDisplayValue = hasValue && - !semanticsObject.isIncrementable && - !semanticsObject.isTextField; + // If the node is incrementable the value is reported to the browser via + // the respective role manager. We do not need to also render it again here. + final bool shouldDisplayValue = hasValue && !semanticsObject.isIncrementable; if (!hasLabel && !shouldDisplayValue) { _cleanUpDom(); @@ -96,7 +96,11 @@ class LabelAndValue extends RoleManager { ..width = '${semanticsObject.rect!.width}px' ..height = '${semanticsObject.rect!.height}px'; } - _auxiliaryValueElement!.style.fontSize = '6px'; + + // Normally use a small font size so that text doesn't leave the scope + // of the semantics node. When debugging semantics, use a font size + // that's reasonably visible. + _auxiliaryValueElement!.style.fontSize = debugShowSemanticsNodes ? '12px' : '6px'; semanticsObject.element.append(_auxiliaryValueElement!); } _auxiliaryValueElement!.text = combinedValue.toString(); diff --git a/lib/web_ui/lib/src/engine/semantics/live_region.dart b/lib/web_ui/lib/src/engine/semantics/live_region.dart index a34c86c75624e..bd6353f6f8070 100644 --- a/lib/web_ui/lib/src/engine/semantics/live_region.dart +++ b/lib/web_ui/lib/src/engine/semantics/live_region.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'semantics.dart'; /// Manages semantics configurations that represent live regions. /// diff --git a/lib/web_ui/lib/src/engine/semantics/scrollable.dart b/lib/web_ui/lib/src/engine/semantics/scrollable.dart index 50624ddd4ba09..8fe6fb0d9dc9a 100644 --- a/lib/web_ui/lib/src/engine/semantics/scrollable.dart +++ b/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../platform_dispatcher.dart'; +import 'semantics.dart'; /// Implements vertical and horizontal scrolling functionality for semantics /// objects. @@ -53,20 +57,20 @@ class Scrollable extends RoleManager { final int semanticsId = semanticsObject.id; if (doScrollForward) { if (semanticsObject.isVerticalScrollContainer) { - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollUp, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollLeft, null); } } else { if (semanticsObject.isVerticalScrollContainer) { - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollDown, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollRight, null); } } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 57c02b5bf22db..4e544c5fe1219 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -2,14 +2,44 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import '../../engine.dart' show registerHotRestartListener; +import '../alarm_clock.dart'; +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../platform_dispatcher.dart'; +import '../util.dart'; +import '../vector_math.dart'; +import 'checkable.dart'; +import 'image.dart'; +import 'incrementable.dart'; +import 'label_and_value.dart'; +import 'live_region.dart'; +import 'scrollable.dart'; +import 'semantics_helper.dart'; +import 'tappable.dart'; +import 'text_field.dart'; /// Set this flag to `true` to cause the engine to visualize the semantics tree -/// on the screen. +/// on the screen for debugging. /// -/// This is useful for debugging. -const bool _debugShowSemanticsNodes = false; +/// This only works in profile and release modes. Debug mode does not support +/// passing compile-time constants. +/// +/// Example: +/// +/// ``` +/// flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true +/// ``` +const bool debugShowSemanticsNodes = bool.fromEnvironment( + 'FLUTTER_WEB_DEBUG_SHOW_SEMANTICS', + defaultValue: false, +); /// Contains updates for the semantics tree. /// @@ -48,10 +78,15 @@ class SemanticsNodeUpdate { required this.scrollExtentMin, required this.rect, required this.label, + this.labelAttributes, required this.hint, + this.hintAttributes, required this.value, + this.valueAttributes, required this.increasedValue, + this.increasedValueAttributes, required this.decreasedValue, + this.decreasedValueAttributes, this.textDirection, required this.transform, required this.elevation, @@ -106,18 +141,33 @@ class SemanticsNodeUpdate { /// See [ui.SemanticsUpdateBuilder.updateNode]. final String label; + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final List? labelAttributes; + /// See [ui.SemanticsUpdateBuilder.updateNode]. final String hint; + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final List? hintAttributes; + /// See [ui.SemanticsUpdateBuilder.updateNode]. final String value; + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final List? valueAttributes; + /// See [ui.SemanticsUpdateBuilder.updateNode]. final String increasedValue; + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final List? increasedValueAttributes; + /// See [ui.SemanticsUpdateBuilder.updateNode]. final String decreasedValue; + /// See [ui.SemanticsUpdateBuilder.updateNode]. + final List? decreasedValueAttributes; + /// See [ui.SemanticsUpdateBuilder.updateNode]. final ui.TextDirection? textDirection; @@ -233,15 +283,17 @@ class SemanticsObject { /// Creates a semantics tree node with the given [id] and [owner]. SemanticsObject(this.id, this.owner) { // DOM nodes created for semantics objects are positioned absolutely using - // transforms. We use a transparent color instead of "visibility:hidden" or - // "display:none" so that a screen reader does not ignore these elements. + // transforms. element.style.position = 'absolute'; // The root node has some properties that other nodes do not. - if (id == 0) { + if (id == 0 && !debugShowSemanticsNodes) { // Make all semantics transparent. We use `filter` instead of `opacity` // attribute because `filter` is stronger. `opacity` does not apply to // some elements, particularly on iOS, such as the slider thumb and track. + // + // We use transparency instead of "visibility:hidden" or "display:none" + // so that a screen reader does not ignore these elements. element.style.filter = 'opacity(0%)'; // Make text explicitly transparent to signal to the browser that no @@ -249,17 +301,17 @@ class SemanticsObject { element.style.color = 'rgba(0,0,0,0)'; } - if (_debugShowSemanticsNodes) { - element.style - ..filter = 'opacity(90%)' - ..outline = '1px solid green' - ..color = 'purple'; + // Make semantic elements visible for debugging by outlining them using a + // green border. We do not use `border` attribute because it affects layout + // (`outline` does not). + if (debugShowSemanticsNodes) { + element.style.outline = '1px solid green'; } } /// See [ui.SemanticsUpdateBuilder.updateNode]. - int? get flags => _flags; - int? _flags; + int get flags => _flags; + int _flags = 0; /// Whether the [flags] field has been updated but has not been applied to the /// DOM yet. @@ -390,6 +442,10 @@ class SemanticsObject { String? get label => _label; String? _label; + /// See [ui.SemanticsUpdateBuilder.updateNode] + List? get labelAttributes => _labelAttributes; + List? _labelAttributes; + /// Whether this object contains a non-empty label. bool get hasLabel => _label != null && _label!.isNotEmpty; @@ -406,6 +462,10 @@ class SemanticsObject { String? get hint => _hint; String? _hint; + /// See [ui.SemanticsUpdateBuilder.updateNode] + List? get hintAttributes => _hintAttributes; + List? _hintAttributes; + static const int _hintIndex = 1 << 11; /// Whether the [hint] field has been updated but has not been @@ -419,6 +479,10 @@ class SemanticsObject { String? get value => _value; String? _value; + /// See [ui.SemanticsUpdateBuilder.updateNode] + List? get valueAttributes => _valueAttributes; + List? _valueAttributes; + /// Whether this object contains a non-empty value. bool get hasValue => _value != null && _value!.isNotEmpty; @@ -435,6 +499,10 @@ class SemanticsObject { String? get increasedValue => _increasedValue; String? _increasedValue; + /// See [ui.SemanticsUpdateBuilder.updateNode] + List? get increasedValueAttributes => _increasedValueAttributes; + List? _increasedValueAttributes; + static const int _increasedValueIndex = 1 << 13; /// Whether the [increasedValue] field has been updated but has not been @@ -448,6 +516,10 @@ class SemanticsObject { String? get decreasedValue => _decreasedValue; String? _decreasedValue; + /// See [ui.SemanticsUpdateBuilder.updateNode] + List? get decreasedValueAttributes => _decreasedValueAttributes; + List? _decreasedValueAttributes; + static const int _decreasedValueIndex = 1 << 14; /// Whether the [decreasedValue] field has been updated but has not been @@ -572,7 +644,7 @@ class SemanticsObject { SemanticsObject? _parent; /// Whether this node currently has a given [SemanticsFlag]. - bool hasFlag(ui.SemanticsFlag flag) => _flags! & flag.index != 0; + bool hasFlag(ui.SemanticsFlag flag) => _flags & flag.index != 0; /// Whether [actions] contains the given action. bool hasAction(ui.SemanticsAction action) => (_actions! & action.index) != 0; @@ -582,6 +654,8 @@ class SemanticsObject { hasAction(ui.SemanticsAction.scrollDown) || hasAction(ui.SemanticsAction.scrollUp); + bool get hasFocus => hasFlag(ui.SemanticsFlag.isFocused); + /// Whether this object represents a hotizontally scrollable area. bool get isHorizontalScrollContainer => hasAction(ui.SemanticsAction.scrollLeft) || @@ -639,11 +713,21 @@ class SemanticsObject { _markValueDirty(); } + if (_valueAttributes != update.valueAttributes) { + _valueAttributes = update.valueAttributes; + _markValueDirty(); + } + if (_label != update.label) { _label = update.label; _markLabelDirty(); } + if (_labelAttributes != update.labelAttributes) { + _labelAttributes = update.labelAttributes; + _markLabelDirty(); + } + if (_rect != update.rect) { _rect = update.rect; _markRectDirty(); @@ -699,16 +783,31 @@ class SemanticsObject { _markHintDirty(); } + if (_hintAttributes != update.hintAttributes) { + _hintAttributes = update.hintAttributes; + _markHintDirty(); + } + if (_increasedValue != update.increasedValue) { _increasedValue = update.increasedValue; _markIncreasedValueDirty(); } + if (_increasedValueAttributes != update.increasedValueAttributes) { + _increasedValueAttributes = update.increasedValueAttributes; + _markIncreasedValueDirty(); + } + if (_decreasedValue != update.decreasedValue) { _decreasedValue = update.decreasedValue; _markDecreasedValueDirty(); } + if (_decreasedValueAttributes != update.decreasedValueAttributes) { + _decreasedValueAttributes = update.decreasedValueAttributes; + _markDecreasedValueDirty(); + } + if (_textDirection != update.textDirection) { _textDirection = update.textDirection; _markTextDirectionDirty(); @@ -771,15 +870,24 @@ class SemanticsObject { /// > A map literal is ordered: iterating over the keys and/or values of the maps always happens in the order the keys appeared in the source code. final Map _roleManagers = {}; + /// Returns the role manager for the given [role]. + /// + /// If a role manager does not exist for the given role, returns null. + RoleManager? debugRoleManagerFor(Role role) => _roleManagers[role]; + /// Detects the roles that this semantics object corresponds to and manages /// the lifecycles of [SemanticsObjectRole] objects. void _updateRoles() { - _updateRole(Role.labelAndValue, (hasLabel || hasValue) && !isVisualOnly); + _updateRole(Role.labelAndValue, (hasLabel || hasValue) && !isTextField && !isVisualOnly); _updateRole(Role.textField, isTextField); - _updateRole( - Role.tappable, - hasAction(ui.SemanticsAction.tap) || - hasFlag(ui.SemanticsFlag.isButton)); + + final bool shouldUseTappableRole = + (hasAction(ui.SemanticsAction.tap) || hasFlag(ui.SemanticsFlag.isButton)) && + // Text fields manage their own focus/tap interactions. We don't need the + // tappable role manager. It only confuses AT. + !isTextField; + + _updateRole(Role.tappable, shouldUseTappableRole); _updateRole(Role.incrementable, isIncrementable); _updateRole(Role.scrollable, isVerticalScrollContainer || isHorizontalScrollContainer); @@ -851,13 +959,9 @@ class SemanticsObject { hasIdentityTransform && verticalContainerAdjustment == 0.0 && horizontalContainerAdjustment == 0.0) { - element.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); + _clearSemanticElementTransform(element); if (containerElement != null) { - containerElement.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); + _clearSemanticElementTransform(containerElement); } return; } @@ -877,7 +981,7 @@ class SemanticsObject { effectiveTransformIsIdentity = effectiveTransform.isIdentity(); } } else if (!hasIdentityTransform) { - effectiveTransform = Matrix4.fromFloat32List(transform!); + effectiveTransform = Matrix4.fromFloat32List(transform); effectiveTransformIsIdentity = false; } @@ -886,9 +990,7 @@ class SemanticsObject { ..transformOrigin = '0 0 0' ..transform = matrix4ToCssTransform(effectiveTransform); } else { - element.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); + _clearSemanticElementTransform(element); } if (containerElement != null) { @@ -898,16 +1000,36 @@ class SemanticsObject { final double translateX = -_rect!.left + horizontalContainerAdjustment; final double translateY = -_rect!.top + verticalContainerAdjustment; containerElement.style - ..transformOrigin = '0 0 0' - ..transform = 'translate(${translateX}px, ${translateY}px)'; + ..top = '${translateY}px' + ..left = '${translateX}px'; } else { - containerElement.style - ..removeProperty('transform-origin') - ..removeProperty('transform'); + _clearSemanticElementTransform(containerElement); } } } + /// Clears the transform on a semantic element as if an identity transform is + /// applied. + /// + /// On macOS and iOS, VoiceOver requires `left=0; top=0` value to correctly + /// handle traversal order. + /// + /// See https://github.com/flutter/flutter/issues/73347. + static void _clearSemanticElementTransform(html.Element element) { + element.style + ..removeProperty('transform-origin') + ..removeProperty('transform'); + if (isMacOrIOS) { + element.style + ..top = '0px' + ..left = '0px'; + } else { + element.style + ..removeProperty('top') + ..removeProperty('left'); + } + } + Int32List? _previousChildrenInTraversalOrder; /// Updates the traversal child list of [object] from the given [update]. @@ -948,7 +1070,7 @@ class SemanticsObject { if (_previousChildrenInTraversalOrder == null || _previousChildrenInTraversalOrder!.isEmpty) { _previousChildrenInTraversalOrder = _childrenInTraversalOrder; - for (int id in _previousChildrenInTraversalOrder!) { + for (final int id in _previousChildrenInTraversalOrder!) { final SemanticsObject child = owner.getOrCreateObject(id); containerElement!.append(child.element); owner._attachObject(parent: this, child: child); @@ -1117,7 +1239,7 @@ class EngineSemanticsOwner { _instance = null; } - final Map _semanticsTree = {}; + final Map _semanticsTree = {}; /// Map [SemanticsObject.id] to parent [SemanticsObject] it was attached to /// this frame. @@ -1125,7 +1247,7 @@ class EngineSemanticsOwner { /// Declares that the [child] must be attached to the [parent]. /// - /// Attachments take precendence over detachments (see [_detachObject]). This + /// Attachments take precedence over detachments (see [_detachObject]). This /// allows the same node to be detached from one parent in the tree and /// reattached to another parent. void _attachObject({required SemanticsObject parent, required SemanticsObject child}) { @@ -1168,7 +1290,7 @@ class EngineSemanticsOwner { /// the one-time callbacks scheduled via the [addOneTimePostUpdateCallback] /// method. void _finalizeTree() { - for (SemanticsObject? object in _detachments) { + for (final SemanticsObject? object in _detachments) { final SemanticsObject? parent = _attachments[object!.id]; if (parent == null) { // Was not reparented and is removed permanently from the tree. @@ -1184,7 +1306,7 @@ class EngineSemanticsOwner { _attachments = {}; if (_oneTimePostUpdateCallbacks.isNotEmpty) { - for (ui.VoidCallback callback in _oneTimePostUpdateCallbacks) { + for (final ui.VoidCallback callback in _oneTimePostUpdateCallbacks) { callback(); } _oneTimePostUpdateCallbacks = []; @@ -1194,8 +1316,8 @@ class EngineSemanticsOwner { /// Returns the entire semantics tree for testing. /// /// Works only in debug mode. - Map? get debugSemanticsTree { - Map? result; + Map? get debugSemanticsTree { + Map? result; assert(() { result = _semanticsTree; return true; @@ -1206,6 +1328,7 @@ class EngineSemanticsOwner { /// The top-level DOM element of the semantics DOM element tree. html.Element? _rootSemanticsElement; + // ignore: prefer_function_declarations_over_variables TimestampFunction _now = () => DateTime.now(); void debugOverrideTimestampFunction(TimestampFunction value) { @@ -1218,11 +1341,11 @@ class EngineSemanticsOwner { final SemanticsHelper semanticsHelper = SemanticsHelper(); - /// Whether the user has requested that [updateSemantics] be called when - /// the semantic contents of window changes. + /// Whether the user has requested that [updateSemantics] be called when the + /// semantic contents of window changes. /// - /// The [ui.Window.onSemanticsEnabledChanged] callback is called whenever this - /// value changes. + /// The [ui.PlatformDispatcher.onSemanticsEnabledChanged] callback is called + /// whenever this value changes. /// /// This is separate from accessibility [mode], which controls how gestures /// are interpreted when this value is true. @@ -1252,10 +1375,7 @@ class EngineSemanticsOwner { _rootSemanticsElement = null; _gestureModeClock?.datetime = null; } - - if (window._onSemanticsEnabledChanged != null) { - window.invokeOnSemanticsEnabledChanged(); - } + EnginePlatformDispatcher.instance.updateSemanticsEnabled(_semanticsEnabled); } /// Controls how pointer events and browser-detected gestures are treated by @@ -1441,11 +1561,22 @@ class EngineSemanticsOwner { /// Updates the semantics tree from data in the [uiUpdate]. void updateSemantics(ui.SemanticsUpdate uiUpdate) { if (!_semanticsEnabled) { - return; + if (ui.debugEmulateFlutterTesterEnvironment) { + // Running Flutter widget tests in a fake environment. Don't enable + // engine semantics. Test semantics trees violate invariants in ways + // production implementation isn't built to handle. For example, tests + // routinely reset semantics node IDs, which is messing up the update + // process. + return; + } else { + // Running a real app. Auto-enable engine semantics. + semanticsHelper.dispose(); // placeholder no longer needed + semanticsEnabled = true; + } } final SemanticsUpdate update = uiUpdate as SemanticsUpdate; - for (SemanticsNodeUpdate nodeUpdate in update._nodeUpdates!) { + for (final SemanticsNodeUpdate nodeUpdate in update._nodeUpdates!) { final SemanticsObject object = getOrCreateObject(nodeUpdate.id); object.updateWith(nodeUpdate); } @@ -1453,19 +1584,7 @@ class EngineSemanticsOwner { if (_rootSemanticsElement == null) { final SemanticsObject root = _semanticsTree[0]!; _rootSemanticsElement = root.element; - // We render semantics inside the glasspane for proper focus and event - // handling. If semantics is behind the glasspane, the phone will disable - // focusing by touch, only by tabbing around the UI. If semantics is in - // front of glasspane, then DOM event won't bubble up to the glasspane so - // it can forward events to the framework. - // - // We insert the semantics root before the scene host. For all widgets - // in the scene, except for platform widgets, the scene host will pass the - // pointer events through to the semantics tree. However, for platform - // views, the pointer events will not pass through, and will be handled - // by the platform view. - domRenderer.glassPaneElement! - .insertBefore(_rootSemanticsElement!, domRenderer.sceneHostElement); + domRenderer.semanticsHostElement!.append(root.element); } _finalizeTree(); @@ -1478,7 +1597,7 @@ class EngineSemanticsOwner { // Ensure child ID list is consistent with the parent-child // relationship of the semantics tree. if (object!._childrenInTraversalOrder != null) { - for (int childId in object._childrenInTraversalOrder!) { + for (final int childId in object._childrenInTraversalOrder!) { final SemanticsObject? child = _semanticsTree[childId]; if (child == null) { throw AssertionError('Child #$childId is missing in the tree.'); @@ -1498,7 +1617,7 @@ class EngineSemanticsOwner { }); // Validate that all updates were applied - for (SemanticsNodeUpdate update in update._nodeUpdates!) { + for (final SemanticsNodeUpdate update in update._nodeUpdates!) { // Node was added to the tree. assert(_semanticsTree.containsKey(update.id)); } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart index db9a6cb0777d8..0d57780b29b12 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; + +import 'package:meta/meta.dart'; + +import '../browser_detection.dart'; +import 'semantics.dart'; /// The maximum [semanticsActivationAttempts] before we give up waiting for /// the user to enable semantics. @@ -16,7 +21,7 @@ const int kMaxSemanticsActivationAttempts = 20; /// For example when a 'mousedown' targeting a placeholder received following /// 'mouseup' is also not sent to the framework. /// Otherwise these events can cause unintended gestures on the framework side. -const Duration _periodToConsumeEvents = const Duration(milliseconds: 300); +const Duration _periodToConsumeEvents = Duration(milliseconds: 300); /// The message in the label for the placeholder element used to enable /// accessibility. @@ -33,7 +38,7 @@ String placeholderMessage = 'Enable accessibility'; /// event which should be forwarded to the framework. /// /// It does this by using a [SemanticsEnabler]. The [SemanticsEnabler] -/// implementation is choosen using form factor type. +/// implementation is chosen using form factor type. /// /// See [DesktopSemanticsEnabler], [MobileSemanticsEnabler]. class SemanticsHelper { @@ -42,15 +47,24 @@ class SemanticsHelper { @visibleForTesting set semanticsEnabler(SemanticsEnabler semanticsEnabler) { - this._semanticsEnabler = semanticsEnabler; + _semanticsEnabler = semanticsEnabler; } bool shouldEnableSemantics(html.Event event) { return _semanticsEnabler.shouldEnableSemantics(event); } - html.Element prepareAccesibilityPlaceholder() { - return _semanticsEnabler.prepareAccesibilityPlaceholder(); + html.Element prepareAccessibilityPlaceholder() { + return _semanticsEnabler.prepareAccessibilityPlaceholder(); + } + + /// Stops waiting for the user to enable semantics and removes the + /// placeholder. + /// + /// This is used when semantics is enabled programmatically and therefore the + /// placehodler is no longer needed. + void dispose() { + _semanticsEnabler.dispose(); } } @@ -78,59 +92,49 @@ abstract class SemanticsEnabler { /// should be forwarded to the framework. bool tryEnableSemantics(html.Event event); - /// Creates the placeholder for accesibility. + /// Creates the placeholder for accessibility. /// /// Puts it inside the glasspane. /// /// On focus the element announces that accessibility can be enabled by /// tapping/clicking. (Announcement depends on the assistive technology) - html.Element prepareAccesibilityPlaceholder(); + html.Element prepareAccessibilityPlaceholder(); - /// Whether platform is still consisering enabling semantics. + /// Whether platform is still considering enabling semantics. /// /// At this stage a relevant set of events are always assessed to see if /// they activate the semantics. /// /// If not they are sent to framework as normal events. bool get isWaitingToEnableSemantics; + + /// Stops waiting for the user to enable semantics and removes the placeholder. + void dispose(); } +/// The desktop semantics enabler uses a simpler strategy compared to mobile. +/// +/// A placeholder element is created completely outside the view and is not +/// reachable via touch or mouse. Assistive technology can still find it either +/// using keyboard shortcuts or via next/previous touch gesture (for touch +/// screens). This simplification removes the need for pointer event +/// disambiguation or timers. The placeholder simply waits for a click event +/// and enables semantics. @visibleForTesting class DesktopSemanticsEnabler extends SemanticsEnabler { - /// We do not immediately enable semantics when the user requests it, but - /// instead wait for a short period of time before doing it. This is because - /// the request comes as an event targeted on the [_semanticsPlaceholder]. - /// This event, depending on the browser, comes as a burst of events. - /// For example, Safari on MacOS sends "pointerup", "pointerdown". So during a - /// short time period we consume all events and prevent forwarding to the - /// framework. Otherwise, the events will be interpreted twice, once as a - /// request to activate semantics, and a second time by Flutter's gesture - /// recognizers. - @visibleForTesting - Timer? semanticsActivationTimer; - /// A temporary placeholder used to capture a request to activate semantics. html.Element? _semanticsPlaceholder; - /// The number of events we processed that could potentially activate - /// semantics. - int semanticsActivationAttempts = 0; - - /// Instructs [_tryEnableSemantics] to remove [_semanticsPlaceholder]. - /// - /// The placeholder is removed upon any next event. - bool _schedulePlaceholderRemoval = false; - /// Whether we are waiting for the user to enable semantics. @override bool get isWaitingToEnableSemantics => _semanticsPlaceholder != null; @override bool tryEnableSemantics(html.Event event) { - if (_schedulePlaceholderRemoval) { - _semanticsPlaceholder!.remove(); - _semanticsPlaceholder = null; - semanticsActivationTimer = null; + // Semantics may be enabled programmatically. If there's a race between that + // and the DOM event, we may end up here while there's no longer a placeholder + // to work with. + if (!isWaitingToEnableSemantics) { return true; } @@ -155,42 +159,23 @@ class DesktopSemanticsEnabler extends SemanticsEnabler { return true; } - semanticsActivationAttempts += 1; - if (semanticsActivationAttempts >= kMaxSemanticsActivationAttempts) { - // We have received multiple user events, none of which resulted in - // semantics activation. This is a signal that the user is not interested - // in semantics, and so we will stop waiting for it. - _schedulePlaceholderRemoval = true; - return true; - } - - if (semanticsActivationTimer != null) { - // We are in a waiting period to activate a timer. While the timer is - // active we should consume events pertaining to semantics activation. - // Otherwise the event will also be interpreted by the framework and - // potentially result in activating a gesture in the app. - return false; - } - // Check for the event target. - final bool enableConditionPassed = (event.target == _semanticsPlaceholder); + final bool enableConditionPassed = event.target == _semanticsPlaceholder; - if (enableConditionPassed) { - assert(semanticsActivationTimer == null); - semanticsActivationTimer = Timer(_periodToConsumeEvents, () { - EngineSemanticsOwner.instance.semanticsEnabled = true; - _schedulePlaceholderRemoval = true; - }); - return false; + if (!enableConditionPassed) { + // This was not a semantics activating event; forward as normal. + return true; } - // This was not a semantics activating event; forward as normal. - return true; + EngineSemanticsOwner.instance.semanticsEnabled = true; + dispose(); + return false; } @override - html.Element prepareAccesibilityPlaceholder() { - final html.Element placeholder = _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); + html.Element prepareAccessibilityPlaceholder() { + final html.Element placeholder = + _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); // Only listen to "click" because other kinds of events are reported via // PointerBinding. @@ -200,14 +185,16 @@ class DesktopSemanticsEnabler extends SemanticsEnabler { // Adding roles to semantics placeholder. 'aria-live' will make sure that // the content is announced to the assistive technology user as soon as the - // page receives focus. 'tab-index' makes sure the button is the first + // page receives focus. 'tabindex' makes sure the button is the first // target of tab. 'aria-label' is used to define the placeholder message // to the assistive technology user. placeholder ..setAttribute('role', 'button') - ..setAttribute('aria-live', 'true') + ..setAttribute('aria-live', 'polite') ..setAttribute('tabindex', '0') ..setAttribute('aria-label', placeholderMessage); + + // The placeholder sits just outside the window so only AT can reach it. placeholder.style ..position = 'absolute' ..left = '-1px' @@ -216,6 +203,12 @@ class DesktopSemanticsEnabler extends SemanticsEnabler { ..height = '1px'; return placeholder; } + + @override + void dispose() { + _semanticsPlaceholder?.remove(); + _semanticsPlaceholder = null; + } } @visibleForTesting @@ -255,13 +248,21 @@ class MobileSemanticsEnabler extends SemanticsEnabler { @override bool tryEnableSemantics(html.Event event) { + // Semantics may be enabled programmatically. If there's a race between that + // and the DOM event, we may end up here while there's no longer a placeholder + // to work with. + if (!isWaitingToEnableSemantics) { + return true; + } + if (_schedulePlaceholderRemoval) { - final bool removeNow = - (browserEngine != BrowserEngine.webkit || event.type == 'touchend'); + // The event type can also be click for VoiceOver. + final bool removeNow = browserEngine != BrowserEngine.webkit || + event.type == 'touchend' || + event.type == 'pointerup' || + event.type == 'click'; if (removeNow) { - _semanticsPlaceholder!.remove(); - _semanticsPlaceholder = null; - semanticsActivationTimer = null; + dispose(); } return true; } @@ -280,10 +281,16 @@ class MobileSemanticsEnabler extends SemanticsEnabler { return true; } + // ios-safari browsers which starts sending `pointer` events instead of + // `touch` events. (Tested with 12.1 which uses touch events vs 13.5 + // which uses pointer events.) const Set kInterestingEventTypes = { 'click', 'touchstart', 'touchend', + 'pointerdown', + 'pointermove', + 'pointerup', }; if (!kInterestingEventTypes.contains(event.type)) { @@ -299,64 +306,63 @@ class MobileSemanticsEnabler extends SemanticsEnabler { return false; } - // In Chrome the debouncing works well enough to detect accessibility - // request. - final bool blinkEnableConditionPassed = - browserEngine == BrowserEngine.blink && - EngineSemanticsOwner.instance.gestureMode == - GestureMode.browserGestures; - - // In Safari debouncing doesn't work. Instead we look at where exactly - // (within 1 pixel) the event landed. If it landed exactly in the middle of - // the placeholder we interpret it as a signal to enable accessibility. This - // is because when VoiceOver generates a tap it lands it in the middle of - // the focused element. This method is a bit flawed in that a user's finger - // could theoretically land in the middle of the element too. However, the - // chance of that happening is very small. Even low-end phones typically - // have >2 million pixels (e.g. Moto G4). It is very unlikely that a user - // will land their finger exactly in the middle. In the worst case an - // unlucky user would accidentally enable accessibility and the app will be - // slightly slower than normal, but the app will continue functioning as - // normal. Our semantics tree is designed to not interfere with Flutter's - // gesture detection. - bool safariEnableConditionPassed = false; - if (browserEngine == BrowserEngine.webkit) { - html.Point activationPoint; - - switch (event.type) { - case 'click': - final html.MouseEvent click = event as html.MouseEvent; - activationPoint = click.offset; - break; - case 'touchstart': - case 'touchend': - final html.TouchEvent touch = event as html.TouchEvent; - activationPoint = touch.changedTouches!.first.client; - break; - default: - // The event is not relevant, forward to framework as normal. - return true; - } + // Look at where exactly (within 1 pixel) the event landed. If it landed + // exactly in the middle of the placeholder we interpret it as a signal + // to enable accessibility. This is because when VoiceOver and TalkBack + // generate a tap it lands it in the middle of the focused element. This + // method is a bit flawed in that a user's finger could theoretically land + // in the middle of the element too. However, the chance of that happening + // is very small. Even low-end phones typically have >2 million pixels + // (e.g. Moto G4). It is very unlikely that a user will land their finger + // exactly in the middle. In the worst case an unlucky user would + // accidentally enable accessibility and the app will be slightly slower + // than normal, but the app will continue functioning as normal. Our + // semantics tree is designed to not interfere with Flutter's gesture + // detection. + bool enableConditionPassed = false; + html.Point activationPoint; + + switch (event.type) { + case 'click': + final html.MouseEvent click = event as html.MouseEvent; + activationPoint = click.offset; + break; + case 'touchstart': + case 'touchend': + final html.TouchEvent touch = event as html.TouchEvent; + activationPoint = touch.changedTouches!.first.client; + break; + case 'pointerdown': + case 'pointerup': + final html.PointerEvent touch = event as html.PointerEvent; + activationPoint = html.Point(touch.client.x, touch.client.y); + break; + default: + // The event is not relevant, forward to framework as normal. + return true; + } - final html.Rectangle activatingElementRect = - domRenderer.glassPaneElement!.getBoundingClientRect(); - final double midX = (activatingElementRect.left + - (activatingElementRect.right - activatingElementRect.left) / 2).toDouble(); - final double midY = (activatingElementRect.top + - (activatingElementRect.bottom - activatingElementRect.top) / 2).toDouble(); - final double deltaX = activationPoint.x.toDouble() - midX; - final double deltaY = activationPoint.y.toDouble() - midY; - final double deltaSquared = deltaX * deltaX + deltaY * deltaY; - if (deltaSquared < 1.0) { - safariEnableConditionPassed = true; - } + final html.Rectangle activatingElementRect = + _semanticsPlaceholder!.getBoundingClientRect(); + final double midX = (activatingElementRect.left + + (activatingElementRect.right - activatingElementRect.left) / 2) + .toDouble(); + final double midY = (activatingElementRect.top + + (activatingElementRect.bottom - activatingElementRect.top) / 2) + .toDouble(); + final double deltaX = activationPoint.x.toDouble() - midX; + final double deltaY = activationPoint.y.toDouble() - midY; + final double deltaSquared = deltaX * deltaX + deltaY * deltaY; + if (deltaSquared < 1.0) { + enableConditionPassed = true; } - if (blinkEnableConditionPassed || safariEnableConditionPassed) { + if (enableConditionPassed) { assert(semanticsActivationTimer == null); + _schedulePlaceholderRemoval = true; semanticsActivationTimer = Timer(_periodToConsumeEvents, () { + dispose(); EngineSemanticsOwner.instance.semanticsEnabled = true; - _schedulePlaceholderRemoval = true; }); return false; } @@ -366,8 +372,9 @@ class MobileSemanticsEnabler extends SemanticsEnabler { } @override - html.Element prepareAccesibilityPlaceholder() { - final html.Element placeholder = _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); + html.Element prepareAccessibilityPlaceholder() { + final html.Element placeholder = + _semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder'); // Only listen to "click" because other kinds of events are reported via // PointerBinding. @@ -387,4 +394,11 @@ class MobileSemanticsEnabler extends SemanticsEnabler { return placeholder; } + + @override + void dispose() { + _semanticsPlaceholder?.remove(); + _semanticsPlaceholder = null; + semanticsActivationTimer = null; + } } diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index 3f58195b1d46e..99fd1c2761e95 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../platform_dispatcher.dart'; +import 'semantics.dart'; /// Listens to HTML "click" gestures detected by the browser. /// @@ -21,6 +25,10 @@ class Tappable extends RoleManager { void update() { final html.Element element = semanticsObject.element; + // "tab-index=0" is used to allow keyboard traversal of non-form elements. + // See also: https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets + element.tabIndex = 0; + semanticsObject.setAriaRole( 'button', semanticsObject.hasFlag(ui.SemanticsFlag.isButton)); @@ -40,7 +48,7 @@ class Tappable extends RoleManager { GestureMode.browserGestures) { return; } - window.invokeOnSemanticsAction( + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); }; element.addEventListener('click', _clickListener); @@ -49,6 +57,11 @@ class Tappable extends RoleManager { _stopListening(); } } + + // Request focus so that the AT shifts a11y focus to this node. + if (semanticsObject.isFlagsDirty && semanticsObject.hasFocus) { + element.focus(); + } } void _stopListening() { diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 243a154c708b6..0e3f4a7fc8b10 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -2,8 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import '../platform_dispatcher.dart'; +import '../text_editing/text_editing.dart'; +import 'semantics.dart'; /// Text editing used by accesibility mode. /// @@ -15,56 +21,178 @@ part of engine; /// This class is still responsible for hooking up the DOM element with the /// [HybridTextEditing] instance so that changes are communicated to Flutter. class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy { + /// Initializes the [SemanticsTextEditingStrategy] singleton. + /// + /// This method must be called prior to accessing [instance]. + static SemanticsTextEditingStrategy ensureInitialized(HybridTextEditing owner) { + if (_instance != null && instance.owner == owner) { + return instance; + } + return _instance = SemanticsTextEditingStrategy(owner); + } + + /// The [SemanticsTextEditingStrategy] singleton. + static SemanticsTextEditingStrategy get instance => _instance!; + static SemanticsTextEditingStrategy? _instance; + /// Creates a [SemanticsTextEditingStrategy] that eagerly instantiates /// [domElement] so the caller can insert it before calling /// [SemanticsTextEditingStrategy.enable]. - SemanticsTextEditingStrategy( - HybridTextEditing owner, html.HtmlElement domElement) - : super(owner) { - // Make sure the DOM element is of a type that we support for text editing. - // TODO(yjbanov): move into initializer list when https://github.com/dart-lang/sdk/issues/37881 is fixed. - assert((domElement is html.InputElement) || - (domElement is html.TextAreaElement)); - super.domElement = domElement; + SemanticsTextEditingStrategy(HybridTextEditing owner) + : super(owner); + + /// The text field whose DOM element is currently used for editing. + /// + /// If this field is null, no editing takes place. + TextField? activeTextField; + + /// Current input configuration supplied by the "flutter/textinput" channel. + InputConfiguration? inputConfig; + + /// The semantics implementation does not operate on DOM nodes, but only + /// remembers the config and callbacks. This is because the DOM nodes are + /// supplied in the semantics update and enabled by [activate]. + @override + void enable( + InputConfiguration inputConfig, { + required OnChangeCallback onChange, + required OnActionCallback onAction, + }) { + this.inputConfig = inputConfig; + this.onChange = onChange; + this.onAction = onAction; + } + + /// Attaches the DOM element owned by [textField] to the text editing + /// strategy. + /// + /// This method must be called after [enable] to name sure that [inputConfig], + /// [onChange], and [onAction] are not null. + void activate(TextField textField) { + assert( + inputConfig != null && onChange != null && onAction != null, + '"enable" should be called before "enableFromSemantics" and initialize input configuration', + ); + + if (activeTextField == textField) { + // The specified field is already active. Skip. + return; + } else if (activeTextField != null) { + // Another text field is currently active. Deactivate it before switching. + disable(); + } + + activeTextField = textField; + domElement = textField.editableElement; + _syncStyle(); + super.enable(inputConfig!, onChange: onChange!, onAction: onAction!); + } + + /// Detaches the DOM element owned by [textField] from this text editing + /// strategy. + /// + /// Typically at this point the element loses focus (blurs) and stops being + /// used for editing. + void deactivate(TextField textField) { + if (activeTextField == textField) { + disable(); + } } @override void disable() { // We don't want to remove the DOM element because the caller is responsible - // for that. - // - // Remove focus from the editable element to cause the keyboard to hide. + // for that. However we still want to stop editing, cleanup the handlers. + if (!isEnabled) { + return; + } + + isEnabled = false; + style = null; + geometry = null; + + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].cancel(); + } + subscriptions.clear(); + lastEditingState = null; + + // If the text element still has focus, remove focus from the editable + // element to cause the on-screen keyboard, if any, to hide (e.g. on iOS, + // Android). // Otherwise, the keyboard stays on screen even when the user navigates to // a different screen (e.g. by hitting the "back" button). - domElement.blur(); + domElement?.blur(); + domElement = null; + activeTextField = null; + _queuedStyle = null; } @override - void initializeElementPlacement() { - // Element placement is done by [TextField]. + void addEventHandlers() { + if (inputConfiguration.autofillGroup != null) { + subscriptions + .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); + } + + // Subscribe to text and selection changes. + subscriptions.add(activeDomElement.onInput.listen(handleChange)); + subscriptions.add(activeDomElement.onKeyDown.listen(maybeSendAction)); + subscriptions.add(html.document.onSelectionChange.listen(handleChange)); + preventDefaultForMouseEvents(); } @override void initializeTextEditing(InputConfiguration inputConfig, - {_OnChangeCallback? onChange, _OnActionCallback? onAction}) { - // In accesibilty mode, the user of this class is supposed to insert the - // [domElement] on their own. Let's make sure they did. - assert(html.document.body!.contains(domElement)); - + {OnChangeCallback? onChange, OnActionCallback? onAction}) { isEnabled = true; - _inputConfiguration = inputConfig; - _onChange = onChange; - _onAction = onAction; + inputConfiguration = inputConfig; + onChange = onChange; + onAction = onAction; + applyConfiguration(inputConfig); + } - domElement.focus(); + @override + void placeElement() { + // If this text editing element is a part of an autofill group. + if (hasAutofillGroup) { + placeForm(); + } + activeDomElement.focus(); } @override - void setEditingState(EditingState? editingState) { - super.setEditingState(editingState); + void initializeElementPlacement() { + // Element placement is done by [TextField]. + } + + @override + void placeForm() { + } + + @override + void updateElementPlacement(EditableTextGeometry textGeometry) { + // Element placement is done by [TextField]. + } + + EditableTextStyle? _queuedStyle; - // Refocus after setting editing state. - domElement.focus(); + @override + void updateElementStyle(EditableTextStyle textStyle) { + _queuedStyle = textStyle; + _syncStyle(); + } + + /// Apply style to the element, if both style and element are available. + /// + /// Because style is supplied by the "flutter/textinput" channel and the DOM + /// element is supplied by the semantics tree, the existence of both at the + /// same time is not guaranteed. + void _syncStyle() { + if (_queuedStyle == null || domElement == null) { + return; + } + super.updateElementStyle(_queuedStyle!); } } @@ -79,19 +207,15 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy { class TextField extends RoleManager { TextField(SemanticsObject semanticsObject) : super(Role.textField, semanticsObject) { - final html.HtmlElement editableDomElement = + editableElement = semanticsObject.hasFlag(ui.SemanticsFlag.isMultiline) ? html.TextAreaElement() : html.InputElement(); - textEditingElement = SemanticsTextEditingStrategy( - textEditing, - editableDomElement, - ); _setupDomElement(); } - SemanticsTextEditingStrategy? textEditingElement; - html.Element get _textFieldElement => textEditingElement!.domElement; + /// The element used for editing, e.g. `
breaks based on layout results. + ..whiteSpace = 'pre'; + + if (width > longestLine) { + // In this case, we set the width so that the CSS text-align property + // works correctly. + // When `longestLine` is >= `paragraph.width` that means the DOM element + // will automatically size itself to fit the longest line, so there's no + // need to set an explicit width. + cssStyle.width = '${width}px'; + } + + if (paragraphStyle.maxLines != null || paragraphStyle.ellipsis != null) { + cssStyle + ..overflowY = 'hidden' + ..height = '${height}px'; + } + + // 2. Append all spans to the paragraph. + + FlatTextSpan? span; + + html.HtmlElement element = rootElement; + final List lines = computeLineMetrics(); + + for (int i = 0; i < lines.length; i++) { + // Insert a
element before each line except the first line. + if (i > 0) { + domRenderer.append(element, domRenderer.createElement('br')); + } + + final EngineLineMetrics line = lines[i]; + final List boxes = line.boxes!; + final StringBuffer buffer = StringBuffer(); + + int j = 0; + while (j < boxes.length) { + final RangeBox box = boxes[j++]; + if (box is SpanBox && box.span == span) { + buffer.write(box.toText()); + continue; + } + + if (buffer.isNotEmpty) { + domRenderer.appendText(element, buffer.toString()); + buffer.clear(); + } + + if (box is SpanBox) { + span = box.span; + element = domRenderer.createElement('span') as html.HtmlElement; + applyTextStyleToElement( + element: element, + style: box.span.style, + isSpan: true, + ); + domRenderer.append(rootElement, element); + buffer.write(box.toText()); + } else if (box is PlaceholderBox) { + span = null; + // If there's a line-end after this placeholder, we want the
to + // be inserted in the root paragraph element. + element = rootElement; + domRenderer.append( + rootElement, + createPlaceholderElement(placeholder: box.placeholder), + ); + } else { + throw UnimplementedError('Unknown box type: ${box.runtimeType}'); + } + } + + if (buffer.isNotEmpty) { + domRenderer.appendText(element, buffer.toString()); + buffer.clear(); + } + + final String? ellipsis = line.ellipsis; + if (ellipsis != null) { + domRenderer.appendText(element, ellipsis); + } + } + + return rootElement; + } + + @override + List getBoxesForPlaceholders() { + return _layoutService.getBoxesForPlaceholders(); + } + + @override + List getBoxesForRange( + int start, + int end, { + ui.BoxHeightStyle boxHeightStyle = ui.BoxHeightStyle.tight, + ui.BoxWidthStyle boxWidthStyle = ui.BoxWidthStyle.tight, + }) { + return _layoutService.getBoxesForRange(start, end, boxHeightStyle, boxWidthStyle); + } + + @override + ui.TextPosition getPositionForOffset(ui.Offset offset) { + return _layoutService.getPositionForOffset(offset); + } + + @override + ui.TextRange getWordBoundary(ui.TextPosition position) { + final String text = toPlainText(); + + final int start = WordBreaker.prevBreakIndex(text, position.offset + 1); + final int end = WordBreaker.nextBreakIndex(text, position.offset); + return ui.TextRange(start: start, end: end); + } + + @override + ui.TextRange getLineBoundary(ui.TextPosition position) { + final int index = position.offset; + final List lines = computeLineMetrics(); + + int i; + for (i = 0; i < lines.length - 1; i++) { + final EngineLineMetrics line = lines[i]; + if (index >= line.startIndex && index < line.endIndex) { + break; + } + } + + final EngineLineMetrics line = lines[i]; + return ui.TextRange(start: line.startIndex, end: line.endIndex); + } + + @override + List computeLineMetrics() { + return _layoutService.lines; + } +} + +/// Applies a paragraph [style] to an [element], translating the properties to +/// their corresponding CSS equivalents. +/// +/// As opposed to [_applyParagraphStyleToElement], this method only applies +/// styles that are necessary at the paragraph level. Other styles (e.g. font +/// size) are always applied at the span level so they aren't needed at the +/// paragraph level. +void _applyNecessaryParagraphStyles({ + required html.HtmlElement element, + required EngineParagraphStyle style, +}) { + final html.CssStyleDeclaration cssStyle = element.style; + + if (style.textAlign != null) { + cssStyle.textAlign = textAlignToCssValue( + style.textAlign, style.textDirection ?? ui.TextDirection.ltr); + } + if (style.lineHeight != null) { + cssStyle.lineHeight = '${style.lineHeight}'; + } + if (style.textDirection != null) { + cssStyle.direction = textDirectionToCss(style.textDirection); + } +} + +/// Applies some span-level style to a paragraph [element]. +/// +/// For example, it looks for the greatest font size among spans, and applies it +/// to the paragraph. While this seems to have no effect, it prevents the +/// paragraph from inheriting its font size from the body tag, which leads to +/// incorrect vertical alignment of spans. +void _applySpanStylesToParagraph({ + required html.HtmlElement element, + required List spans, +}) { + double fontSize = 0.0; + String? fontFamily; + for (final ParagraphSpan span in spans) { + if (span is FlatTextSpan) { + final double? spanFontSize = span.style.fontSize; + if (spanFontSize != null && spanFontSize > fontSize) { + fontSize = spanFontSize; + if (span.style.isFontFamilyProvided) { + fontFamily = span.style.effectiveFontFamily; + } + } + } + } + + final html.CssStyleDeclaration cssStyle = element.style; + if (fontSize != 0.0) { + cssStyle.fontSize = '${fontSize}px'; + } + if (fontFamily != null) { + cssStyle.fontFamily = canonicalizeFontFamily(fontFamily); + } +} + +/// A common interface for all types of spans that make up a paragraph. +/// +/// These spans are stored as a flat list in the paragraph object. +abstract class ParagraphSpan { + /// The index of the beginning of the range of text represented by this span. + int get start; + + /// The index of the end of the range of text represented by this span. + int get end; +} + +/// Represent a span of text in the paragraph. +/// +/// It's a "flat" text span as opposed to the framework text spans that are +/// hierarchical. +/// +/// Instead of keeping spans and styles in a tree hierarchy like the framework +/// does, we flatten the structure and resolve/merge all the styles from parent +/// nodes. +class FlatTextSpan implements ParagraphSpan { + /// Creates a [FlatTextSpan] with the given [style], representing the span of + /// text in the range between [start] and [end]. + FlatTextSpan({ + required this.style, + required this.start, + required this.end, + }); + + /// The resolved style of the span. + final EngineTextStyle style; + + @override + final int start; + + @override + final int end; + + String textOf(CanvasParagraph paragraph) { + final String text = paragraph.toPlainText(); + assert(end <= text.length); + return text.substring(start, end); + } +} + +class PlaceholderSpan extends ParagraphPlaceholder implements ParagraphSpan { + PlaceholderSpan( + int index, + double width, + double height, + ui.PlaceholderAlignment alignment, { + required double baselineOffset, + required ui.TextBaseline baseline, + }) : start = index, + end = index, + super( + width, + height, + alignment, + baselineOffset: baselineOffset, + baseline: baseline, + ); + + @override + final int start; + + @override + final int end; +} + +/// Represents a node in the tree of text styles pushed to [ui.ParagraphBuilder]. +/// +/// The [ui.ParagraphBuilder.pushText] and [ui.ParagraphBuilder.pop] operations +/// represent the entire tree of styles in the paragraph. In our implementation, +/// we don't need to keep the entire tree structure in memory. At any point in +/// time, we only need a stack of nodes that represent the current branch in the +/// tree. The items in the stack are [StyleNode] objects. +abstract class StyleNode { + /// Create a child for this style node. + /// + /// We are not creating a tree structure, hence there's no need to keep track + /// of the children. + ChildStyleNode createChild(EngineTextStyle style) { + return ChildStyleNode(parent: this, style: style); + } + + EngineTextStyle? _cachedStyle; + + /// Generates the final text style to be applied to the text span. + /// + /// The resolved text style is equivalent to the entire ascendent chain of + /// parent style nodes. + EngineTextStyle resolveStyle() { + final EngineTextStyle? style = _cachedStyle; + if (style == null) { + return _cachedStyle ??= EngineTextStyle( + color: _color, + decoration: _decoration, + decorationColor: _decorationColor, + decorationStyle: _decorationStyle, + decorationThickness: _decorationThickness, + fontWeight: _fontWeight, + fontStyle: _fontStyle, + textBaseline: _textBaseline, + fontFamily: _fontFamily, + fontFamilyFallback: _fontFamilyFallback, + fontFeatures: _fontFeatures, + fontSize: _fontSize, + letterSpacing: _letterSpacing, + wordSpacing: _wordSpacing, + height: _height, + locale: _locale, + background: _background, + foreground: _foreground, + shadows: _shadows, + ); + } + return style; + } + + ui.Color? get _color; + ui.TextDecoration? get _decoration; + ui.Color? get _decorationColor; + ui.TextDecorationStyle? get _decorationStyle; + double? get _decorationThickness; + ui.FontWeight? get _fontWeight; + ui.FontStyle? get _fontStyle; + ui.TextBaseline? get _textBaseline; + String get _fontFamily; + List? get _fontFamilyFallback; + List? get _fontFeatures; + double get _fontSize; + double? get _letterSpacing; + double? get _wordSpacing; + double? get _height; + ui.Locale? get _locale; + ui.Paint? get _background; + ui.Paint? get _foreground; + List? get _shadows; +} + +/// Represents a non-root [StyleNode]. +class ChildStyleNode extends StyleNode { + /// Creates a [ChildStyleNode] with the given [parent] and [style]. + ChildStyleNode({required this.parent, required this.style}); + + /// The parent node to be used when resolving text styles. + final StyleNode parent; + + /// The text style associated with the current node. + final EngineTextStyle style; + + // Read these properties from the TextStyle associated with this node. If the + // property isn't defined, go to the parent node. + + @override + ui.Color? get _color => style.color ?? (_foreground == null ? parent._color : null); + + @override + ui.TextDecoration? get _decoration => style.decoration ?? parent._decoration; + + @override + ui.Color? get _decorationColor => style.decorationColor ?? parent._decorationColor; + + @override + ui.TextDecorationStyle? get _decorationStyle => style.decorationStyle ?? parent._decorationStyle; + + @override + double? get _decorationThickness => style.decorationThickness ?? parent._decorationThickness; + + @override + ui.FontWeight? get _fontWeight => style.fontWeight ?? parent._fontWeight; + + @override + ui.FontStyle? get _fontStyle => style.fontStyle ?? parent._fontStyle; + + @override + ui.TextBaseline? get _textBaseline => style.textBaseline ?? parent._textBaseline; + + @override + List? get _fontFamilyFallback => style.fontFamilyFallback ?? parent._fontFamilyFallback; + + @override + List? get _fontFeatures => style.fontFeatures ?? parent._fontFeatures; + + @override + double get _fontSize => style.fontSize ?? parent._fontSize; + + @override + double? get _letterSpacing => style.letterSpacing ?? parent._letterSpacing; + + @override + double? get _wordSpacing => style.wordSpacing ?? parent._wordSpacing; + + @override + double? get _height => style.height ?? parent._height; + + @override + ui.Locale? get _locale => style.locale ?? parent._locale; + + @override + ui.Paint? get _background => style.background ?? parent._background; + + @override + ui.Paint? get _foreground => style.foreground ?? parent._foreground; + + @override + List? get _shadows => style.shadows ?? parent._shadows; + + // Font family is slightly different from the other properties above. It's + // never null on the TextStyle object, so we use `isFontFamilyProvided` to + // check if font family is defined or not. + @override + String get _fontFamily => style.isFontFamilyProvided ? style.fontFamily : parent._fontFamily; +} + +/// The root style node for the paragraph. +/// +/// The style of the root is derived from a [ui.ParagraphStyle] and is the root +/// style for all spans in the paragraph. +class RootStyleNode extends StyleNode { + /// Creates a [RootStyleNode] from [paragraphStyle]. + RootStyleNode(this.paragraphStyle); + + /// The style of the paragraph being built. + final EngineParagraphStyle paragraphStyle; + + @override + final ui.Color _color = _defaultTextColor; + + @override + ui.TextDecoration? get _decoration => null; + + @override + ui.Color? get _decorationColor => null; + + @override + ui.TextDecorationStyle? get _decorationStyle => null; + + @override + double? get _decorationThickness => null; + + @override + ui.FontWeight? get _fontWeight => paragraphStyle.fontWeight; + @override + ui.FontStyle? get _fontStyle => paragraphStyle.fontStyle; + + @override + ui.TextBaseline? get _textBaseline => null; + + @override + String get _fontFamily => paragraphStyle.fontFamily ?? DomRenderer.defaultFontFamily; + + @override + List? get _fontFamilyFallback => null; + + @override + List? get _fontFeatures => null; + + @override + double get _fontSize => paragraphStyle.fontSize ?? DomRenderer.defaultFontSize; + + @override + double? get _letterSpacing => null; + + @override + double? get _wordSpacing => null; + + @override + double? get _height => paragraphStyle.height; + + @override + ui.Locale? get _locale => paragraphStyle.locale; + + @override + ui.Paint? get _background => null; + + @override + ui.Paint? get _foreground => null; + + @override + List? get _shadows => null; +} + +/// Builds a [CanvasParagraph] containing text with the given styling +/// information. +class CanvasParagraphBuilder implements ui.ParagraphBuilder { + /// Creates a [CanvasParagraphBuilder] object, which is used to create a + /// [CanvasParagraph]. + CanvasParagraphBuilder(EngineParagraphStyle style) + : _paragraphStyle = style, + _rootStyleNode = RootStyleNode(style); + + final StringBuffer _plainTextBuffer = StringBuffer(); + final EngineParagraphStyle _paragraphStyle; + + final List _spans = []; + final List _styleStack = []; + + RootStyleNode _rootStyleNode; + StyleNode get _currentStyleNode => _styleStack.isEmpty + ? _rootStyleNode + : _styleStack[_styleStack.length - 1]; + + @override + int get placeholderCount => _placeholderCount; + int _placeholderCount = 0; + + @override + List get placeholderScales => _placeholderScales; + final List _placeholderScales = []; + + @override + void addPlaceholder( + double width, + double height, + ui.PlaceholderAlignment alignment, { + double scale = 1.0, + double? baselineOffset, + ui.TextBaseline? baseline, + }) { + // TODO(mdebbar): for measurement of placeholders, look at: + // - https://github.com/flutter/engine/blob/c0f7e8acf9318d264ad6a235facd097de597ffcc/third_party/txt/src/txt/paragraph_txt.cc#L325-L350 + + // Require a baseline to be specified if using a baseline-based alignment. + assert(!(alignment == ui.PlaceholderAlignment.aboveBaseline || + alignment == ui.PlaceholderAlignment.belowBaseline || + alignment == ui.PlaceholderAlignment.baseline) || baseline != null); + + _placeholderCount++; + _placeholderScales.add(scale); + _spans.add(PlaceholderSpan( + _plainTextBuffer.length, + width * scale, + height * scale, + alignment, + baselineOffset: (baselineOffset ?? height) * scale, + baseline: baseline ?? ui.TextBaseline.alphabetic, + )); + } + + @override + void pushStyle(ui.TextStyle style) { + _styleStack.add(_currentStyleNode.createChild(style as EngineTextStyle)); + } + + @override + void pop() { + if (_styleStack.isNotEmpty) { + _styleStack.removeLast(); + } + } + + bool _drawOnCanvas = true; + + @override + void addText(String text) { + final EngineTextStyle style = _currentStyleNode.resolveStyle(); + final int start = _plainTextBuffer.length; + _plainTextBuffer.write(text); + final int end = _plainTextBuffer.length; + + if (_drawOnCanvas) { + final ui.TextDecoration? decoration = style.decoration; + if (decoration != null && decoration != ui.TextDecoration.none) { + _drawOnCanvas = false; + } + } + + if (_drawOnCanvas) { + final List? fontFeatures = style.fontFeatures; + if (fontFeatures != null && fontFeatures.isNotEmpty) { + _drawOnCanvas = false; + } + } + + _spans.add(FlatTextSpan(style: style, start: start, end: end)); + } + + @override + CanvasParagraph build() { + return CanvasParagraph( + _spans, + paragraphStyle: _paragraphStyle, + plainText: _plainTextBuffer.toString(), + placeholderCount: _placeholderCount, + drawOnCanvas: _drawOnCanvas, + ); + } +} diff --git a/lib/web_ui/lib/src/engine/text/font_collection.dart b/lib/web_ui/lib/src/engine/text/font_collection.dart index 96a63e1ebbe73..c56b77d5f057a 100644 --- a/lib/web_ui/lib/src/engine/text/font_collection.dart +++ b/lib/web_ui/lib/src/engine/text/font_collection.dart @@ -2,13 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - -const String _ahemFontFamily = 'Ahem'; -const String _ahemFontUrl = 'packages/ui/assets/ahem.ttf'; -const String _robotoFontFamily = 'Roboto'; -const String _robotoFontUrl = 'packages/ui/assets/Roboto-Regular.ttf'; +import 'dart:async'; +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:typed_data'; + +import '../assets.dart'; +import '../browser_detection.dart'; +import '../util.dart'; +import 'layout_service.dart'; + +const String ahemFontFamily = 'Ahem'; +const String ahemFontUrl = 'packages/ui/assets/ahem.ttf'; +const String robotoFontFamily = 'Roboto'; +const String robotoTestFontUrl = 'packages/ui/assets/Roboto-Regular.ttf'; /// This class is responsible for registering and loading fonts. /// @@ -29,8 +37,7 @@ class FontCollection { byteData = await assetManager.load('FontManifest.json'); } on AssetManagerException catch (e) { if (e.httpStatus == 404) { - html.window.console - .warn('Font manifest does not exist at `${e.url}` – ignoring.'); + printWarning('Font manifest does not exist at `${e.url}` – ignoring.'); return; } else { rethrow; @@ -38,7 +45,7 @@ class FontCollection { } final List? fontManifest = - json.decode(utf8.decode(byteData.buffer.asUint8List())); + json.decode(utf8.decode(byteData.buffer.asUint8List())) as List?; if (fontManifest == null) { throw AssertionError( 'There was a problem trying to load FontManifest.json'); @@ -50,15 +57,15 @@ class FontCollection { _assetFontManager = _PolyfillFontManager(); } - for (Map fontFamily in fontManifest.cast>()) { - final String? family = fontFamily['family']; - final List fontAssets = fontFamily['fonts']; + for (final Map fontFamily + in fontManifest.cast>()) { + final String? family = fontFamily.tryString('family'); + final List> fontAssets = fontFamily.castList>('fonts'); - for (dynamic fontAssetItem in fontAssets) { - final Map fontAsset = fontAssetItem; - final String asset = fontAsset['asset']; + for (final Map fontAsset in fontAssets) { + final String asset = fontAsset.readString('asset'); final Map descriptors = {}; - for (String descriptor in fontAsset.keys) { + for (final String descriptor in fontAsset.keys) { if (descriptor != 'asset') { descriptors[descriptor] = '${fontAsset[descriptor]}'; } @@ -77,9 +84,9 @@ class FontCollection { void debugRegisterTestFonts() { _testFontManager = FontManager(); _testFontManager!.registerAsset( - _ahemFontFamily, 'url($_ahemFontUrl)', const {}); - _testFontManager!.registerAsset( - _robotoFontFamily, 'url($_robotoFontUrl)', const {}); + ahemFontFamily, 'url($ahemFontUrl)', const {}); + _testFontManager!.registerAsset(robotoFontFamily, + 'url($robotoTestFontUrl)', const {}); } /// Returns a [Future] that completes when the registered fonts are loaded @@ -107,11 +114,11 @@ class FontManager { // For example font family 'Ahem!' does not fall into this category // so the family name will be wrapped in quotes. static final RegExp notPunctuation = - RegExp(r"[a-z0-9\s]+", caseSensitive: false); + RegExp(r'[a-z0-9\s]+', caseSensitive: false); // Regular expression to detect tokens starting with a digit. // For example font family 'Goudy Bookletter 1911' falls into this // category. - static final RegExp startWithDigit = RegExp(r"\b\d"); + static final RegExp startWithDigit = RegExp(r'\b\d'); factory FontManager() { if (supportsFontLoadingApi) { @@ -178,14 +185,22 @@ class FontManager { try { final html.FontFace fontFace = html.FontFace(family, asset, descriptors); _fontLoadingFutures.add(fontFace.load().then((_) { - html.document.fonts!.add(fontFace); + // We could do: + // ``` + // html.document.fonts!.add(fontFace); + // ``` + // But dart:html expects the return value to be non-null, and Firefox + // returns null. This causes the app to crash in Firefox with a null + // check exception. + // + // TODO(mdebbar): Revert this once the dart:html type is fixed. + // https://github.com/dart-lang/sdk/issues/45676 + js_util.callMethod(html.document.fonts!, 'add', [fontFace]); }, onError: (dynamic e) { - html.window.console - .warn('Error while trying to load font family "$family":\n$e'); + printWarning('Error while trying to load font family "$family":\n$e'); })); } catch (e) { - html.window.console - .warn('Error while loading font family "$family":\n$e'); + printWarning('Error while loading font family "$family":\n$e'); } } @@ -199,7 +214,7 @@ class FontManager { // There might be paragraph measurements for this new font before it is // loaded. They were measured using fallback font, so we should clear the // cache. - TextMeasurementService.clearCache(); + Spanometer.clearRulersCache(); }, onError: (dynamic exception) { // Failures here will throw an html.DomException which confusingly // does not implement Exception or Error. Rethrow an Exception so it can @@ -240,8 +255,8 @@ class _PolyfillFontManager extends FontManager { paragraph.style.position = 'absolute'; paragraph.style.visibility = 'hidden'; paragraph.style.fontSize = '72px'; - final String fallbackFontName = browserEngine == BrowserEngine.ie11 ? - 'Times New Roman' : 'sans-serif'; + final String fallbackFontName = + browserEngine == BrowserEngine.ie11 ? 'Times New Roman' : 'sans-serif'; paragraph.style.fontFamily = fallbackFontName; if (descriptors['style'] != null) { paragraph.style.fontStyle = descriptors['style']; @@ -308,5 +323,8 @@ class _PolyfillFontManager extends FontManager { } } -final bool supportsFontLoadingApi = js_util.hasProperty(html.window, 'FontFace'); -final bool supportsFontsClearApi = js_util.hasProperty(html.document, 'fonts') && js_util.hasProperty(html.document.fonts!, 'clear'); +final bool supportsFontLoadingApi = + js_util.hasProperty(html.window, 'FontFace'); +final bool supportsFontsClearApi = + js_util.hasProperty(html.document, 'fonts') && + js_util.hasProperty(html.document.fonts!, 'clear'); diff --git a/lib/web_ui/lib/src/engine/text/layout_service.dart b/lib/web_ui/lib/src/engine/text/layout_service.dart new file mode 100644 index 0000000000000..6d3c374523d05 --- /dev/null +++ b/lib/web_ui/lib/src/engine/text/layout_service.dart @@ -0,0 +1,1551 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import 'canvas_paragraph.dart'; +import 'line_breaker.dart'; +import 'measurement.dart'; +import 'paragraph.dart'; +import 'ruler.dart'; +import 'text_direction.dart'; + +/// Performs layout on a [CanvasParagraph]. +/// +/// It uses a [html.CanvasElement] to measure text. +class TextLayoutService { + TextLayoutService(this.paragraph); + + final CanvasParagraph paragraph; + + final html.CanvasRenderingContext2D context = html.CanvasElement().context2D; + + // *** Results of layout *** // + + // Look at the Paragraph class for documentation of the following properties. + + double width = -1.0; + + double height = 0.0; + + EngineLineMetrics? longestLine; + + double minIntrinsicWidth = 0.0; + + double maxIntrinsicWidth = 0.0; + + double alphabeticBaseline = -1.0; + + double ideographicBaseline = -1.0; + + bool didExceedMaxLines = false; + + final List lines = []; + + // *** Convenient shortcuts used during layout *** // + + int? get maxLines => paragraph.paragraphStyle.maxLines; + bool get unlimitedLines => maxLines == null; + + String? get ellipsis => paragraph.paragraphStyle.ellipsis; + bool get hasEllipsis => ellipsis != null; + + /// Performs the layout on a paragraph given the [constraints]. + /// + /// The function starts by resetting all layout-related properties. Then it + /// starts looping through the paragraph to calculate all layout metrics. + /// + /// It uses a [Spanometer] to perform measurements within spans of the + /// paragraph. It also uses [LineBuilders] to generate [EngineLineMetrics] as + /// it iterates through the paragraph. + /// + /// The main loop keeps going until: + /// + /// 1. The end of the paragraph is reached (i.e. LineBreakType.endOfText). + /// 2. Enough lines have been computed to satisfy [maxLines]. + /// 3. An ellipsis is appended because of an overflow. + void performLayout(ui.ParagraphConstraints constraints) { + final int spanCount = paragraph.spans.length; + + // Reset results from previous layout. + width = constraints.width; + height = 0.0; + longestLine = null; + minIntrinsicWidth = 0.0; + maxIntrinsicWidth = 0.0; + didExceedMaxLines = false; + lines.clear(); + + if (spanCount == 0) { + return; + } + + final Spanometer spanometer = Spanometer(paragraph, context); + + int spanIndex = 0; + LineBuilder currentLine = + LineBuilder.first(paragraph, spanometer, maxWidth: constraints.width); + + // The only way to exit this while loop is by hitting one of the `break;` + // statements (e.g. when we reach `endOfText`, when ellipsis has been + // appended). + while (true) { + // ************************** // + // *** HANDLE END OF TEXT *** // + // ************************** // + + // All spans have been consumed. + final bool reachedEnd = spanIndex == spanCount; + if (reachedEnd) { + // In some cases, we need to extend the line to the end of text and + // build it: + // + // 1. Line is not empty. This could happen when the last span is a + // placeholder. + // + // 2. We haven't reached `LineBreakType.endOfText` yet. This could + // happen when the last character is a new line. + if (currentLine.isNotEmpty || currentLine.end.type != LineBreakType.endOfText) { + currentLine.extendToEndOfText(); + lines.add(currentLine.build()); + } + break; + } + + // ********************************* // + // *** THE MAIN MEASUREMENT PART *** // + // ********************************* // + + final ParagraphSpan span = paragraph.spans[spanIndex]; + + if (span is PlaceholderSpan) { + if (currentLine.widthIncludingSpace + span.width <= constraints.width) { + // The placeholder fits on the current line. + currentLine.addPlaceholder(span); + } else { + // The placeholder can't fit on the current line. + if (currentLine.isNotEmpty) { + lines.add(currentLine.build()); + currentLine = currentLine.nextLine(); + } + currentLine.addPlaceholder(span); + } + spanIndex++; + } else if (span is FlatTextSpan) { + spanometer.currentSpan = span; + final DirectionalPosition nextBreak = currentLine.findNextBreak(); + final double additionalWidth = + currentLine.getAdditionalWidthTo(nextBreak.lineBreak); + + if (currentLine.width + additionalWidth <= constraints.width) { + // TODO(mdebbar): Handle the case when `nextBreak` is just a span end + // that shouldn't extend the line yet. + + // The line can extend to `nextBreak` without overflowing. + currentLine.extendTo(nextBreak); + if (nextBreak.type == LineBreakType.mandatory) { + lines.add(currentLine.build()); + currentLine = currentLine.nextLine(); + } + } else { + // The chunk of text can't fit into the current line. + final bool isLastLine = + (hasEllipsis && unlimitedLines) || lines.length + 1 == maxLines; + + if (isLastLine && hasEllipsis) { + // We've reached the line that requires an ellipsis to be appended + // to it. + + currentLine.forceBreak( + nextBreak, + allowEmpty: true, + ellipsis: ellipsis, + ); + lines.add(currentLine.build(ellipsis: ellipsis)); + break; + } else if (currentLine.isEmpty) { + // The current line is still empty, which means we are dealing + // with a single block of text that doesn't fit in a single line. + // We need to force-break it without adding an ellipsis. + + currentLine.forceBreak(nextBreak, allowEmpty: false); + lines.add(currentLine.build()); + currentLine = currentLine.nextLine(); + } else { + // Normal line break. + lines.add(currentLine.build()); + currentLine = currentLine.nextLine(); + } + } + + // Only go to the next span if we've reached the end of this span. + if (currentLine.end.index >= span.end) { + currentLine.createBox(); + ++spanIndex; + } + } else { + throw UnimplementedError('Unknown span type: ${span.runtimeType}'); + } + + if (lines.length == maxLines) { + break; + } + } + + // ************************************************** // + // *** PARAGRAPH BASELINE & HEIGHT & LONGEST LINE *** // + // ************************************************** // + + for (final EngineLineMetrics line in lines) { + height += line.height; + if (alphabeticBaseline == -1.0) { + alphabeticBaseline = line.baseline; + ideographicBaseline = alphabeticBaseline * baselineRatioHack; + } + final double longestLineWidth = longestLine?.width ?? 0.0; + if (longestLineWidth < line.width) { + longestLine = line; + } + } + + // ******************************** // + // *** MAX/MIN INTRINSIC WIDTHS *** // + // ******************************** // + + spanIndex = 0; + currentLine = + LineBuilder.first(paragraph, spanometer, maxWidth: constraints.width); + + while (spanIndex < spanCount) { + final ParagraphSpan span = paragraph.spans[spanIndex]; + bool breakToNextLine = false; + + if (span is PlaceholderSpan) { + currentLine.addPlaceholder(span); + spanIndex++; + } else if (span is FlatTextSpan) { + spanometer.currentSpan = span; + final DirectionalPosition nextBreak = currentLine.findNextBreak(); + + // For the purpose of max intrinsic width, we don't care if the line + // fits within the constraints or not. So we always extend it. + currentLine.extendTo(nextBreak); + if (nextBreak.type == LineBreakType.mandatory) { + // We don't want to break the line now because we want to update + // min/max intrinsic widths below first. + breakToNextLine = true; + } + + // Only go to the next span if we've reached the end of this span. + if (currentLine.end.index >= span.end) { + spanIndex++; + } + } + + final double widthOfLastSegment = currentLine.lastSegment.width; + if (minIntrinsicWidth < widthOfLastSegment) { + minIntrinsicWidth = widthOfLastSegment; + } + + // Max intrinsic width includes the width of trailing spaces. + if (maxIntrinsicWidth < currentLine.widthIncludingSpace) { + maxIntrinsicWidth = currentLine.widthIncludingSpace; + } + + if (breakToNextLine) { + currentLine = currentLine.nextLine(); + } + } + } + + List getBoxesForPlaceholders() { + final List boxes = []; + for (final EngineLineMetrics line in lines) { + for (final RangeBox box in line.boxes!) { + if (box is PlaceholderBox) { + boxes.add(box.toTextBox(line)); + } + } + } + return boxes; + } + + List getBoxesForRange( + int start, + int end, + ui.BoxHeightStyle boxHeightStyle, + ui.BoxWidthStyle boxWidthStyle, + ) { + // Zero-length ranges and invalid ranges return an empty list. + if (start >= end || start < 0 || end < 0) { + return []; + } + + final int length = paragraph.toPlainText().length; + // Ranges that are out of bounds should return an empty list. + if (start > length || end > length) { + return []; + } + + final List boxes = []; + + for (final EngineLineMetrics line in lines) { + if (line.overlapsWith(start, end)) { + for (final RangeBox box in line.boxes!) { + if (box is SpanBox && box.overlapsWith(start, end)) { + boxes.add(box.intersect(line, start, end)); + } + } + } + } + return boxes; + } + + ui.TextPosition getPositionForOffset(ui.Offset offset) { + // After layout, each line has boxes that contain enough information to make + // it possible to do hit testing. Once we find the box, we look inside that + // box to find where exactly the `offset` is located. + + final EngineLineMetrics line = _findLineForY(offset.dy); + // [offset] is to the left of the line. + if (offset.dx <= line.left) { + return ui.TextPosition( + offset: line.startIndex, + affinity: ui.TextAffinity.downstream, + ); + } + + // [offset] is to the right of the line. + if (offset.dx >= line.left + line.widthWithTrailingSpaces) { + return ui.TextPosition( + offset: line.endIndexWithoutNewlines, + affinity: ui.TextAffinity.upstream, + ); + } + + final double dx = offset.dx - line.left; + for (final RangeBox box in line.boxes!) { + if (box.left <= dx && dx <= box.right) { + return box.getPositionForX(dx); + } + } + // Is this ever reachable? + return ui.TextPosition(offset: line.startIndex); + } + + EngineLineMetrics _findLineForY(double y) { + // We could do a binary search here but it's not worth it because the number + // of line is typically low, and each iteration is a cheap comparison of + // doubles. + for (final EngineLineMetrics line in lines) { + if (y <= line.height) { + return line; + } + y -= line.height; + } + return lines.last; + } +} + +/// Represents a box inside a paragraph span with the range of [start] to [end]. +/// +/// The box's coordinates are all relative to the line it belongs to. For +/// example, [left] is the distance from the left edge of the line to the left +/// edge of the box. +/// +/// This is what the various measurements/coordinates look like for a box in an +/// LTR paragraph: +/// +/// *------------------------lineWidth------------------* +/// *--width--* +/// ┌─────────────────┬─────────┬───────────────────────┐ +/// │ │---BOX---│ │ +/// └─────────────────┴─────────┴───────────────────────┘ +/// *---startOffset---* +/// *------left-------* +/// *--------endOffset----------* +/// *----------right------------* +/// +/// +/// And in an RTL paragraph, [startOffset] and [endOffset] are flipped because +/// the line starts from the right. Here's what they look like: +/// +/// *------------------------lineWidth------------------* +/// *--width--* +/// ┌─────────────────┬─────────┬───────────────────────┐ +/// │ │---BOX---│ │ +/// └─────────────────┴─────────┴───────────────────────┘ +/// *------startOffset------* +/// *------left-------* +/// *-----------endOffset-------------* +/// *----------right------------* +/// +abstract class RangeBox { + RangeBox( + this.start, + this.end, + this.width, + this.paragraphDirection, + this.boxDirection, + ); + + final LineBreakResult start; + final LineBreakResult end; + + /// The distance from the beginning of the line to the beginning of the box. + late final double startOffset; + + /// The distance from the beginning of the line to the end of the box. + double get endOffset => startOffset + width; + + /// The distance from the left edge of the line to the left edge of the box. + double get left => paragraphDirection == ui.TextDirection.ltr + ? startOffset + : lineWidth - endOffset; + + /// The distance from the left edge of the line to the right edge of the box. + double get right => paragraphDirection == ui.TextDirection.ltr + ? endOffset + : lineWidth - startOffset; + + /// The distance from the left edge of the box to the right edge of the box. + final double width; + + /// The width of the line that this box belongs to. + late final double lineWidth; + + /// The text direction of the paragraph that this box belongs to. + final ui.TextDirection paragraphDirection; + + /// Indicates how this box flows among other boxes. + /// + /// Example: In an LTR paragraph, the text "ABC hebrew_word 123 DEF" is shown + /// visually in the following order: + /// + /// +-------------------------------+ + /// | ABC | 123 | drow_werbeh | DEF | + /// +-------------------------------+ + /// box direction: LTR RTL RTL LTR + /// ----> <---- <------------ ----> + /// + /// (In the above example, we are ignoring whitespace to simplify). + final ui.TextDirection boxDirection; + + /// Returns a [ui.TextBox] representing this range box in the given [line]. + /// + /// The coordinates of the resulting [ui.TextBox] are relative to the + /// paragraph, not to the line. + ui.TextBox toTextBox(EngineLineMetrics line); + + /// Returns the text position within this box's range that's closest to the + /// given [x] offset. + /// + /// The [x] offset is expected to be relative to the left edge of the line, + /// just like the coordinates of this box. + ui.TextPosition getPositionForX(double x); +} + +/// Represents a box for a [PlaceholderSpan]. +class PlaceholderBox extends RangeBox { + PlaceholderBox( + this.placeholder, { + required LineBreakResult index, + required ui.TextDirection paragraphDirection, + required ui.TextDirection boxDirection, + }) : super(index, index, placeholder.width, paragraphDirection, boxDirection); + + final PlaceholderSpan placeholder; + + @override + ui.TextBox toTextBox(EngineLineMetrics line) { + final double left = line.left + this.left; + final double right = line.left + this.right; + + final double lineTop = line.baseline - line.ascent; + + final double top; + switch (placeholder.alignment) { + case ui.PlaceholderAlignment.top: + top = lineTop; + break; + + case ui.PlaceholderAlignment.middle: + top = lineTop + (line.height - placeholder.height) / 2; + break; + + case ui.PlaceholderAlignment.bottom: + top = lineTop + line.height - placeholder.height; + break; + + case ui.PlaceholderAlignment.aboveBaseline: + top = line.baseline - placeholder.height; + break; + + case ui.PlaceholderAlignment.belowBaseline: + top = line.baseline; + break; + + case ui.PlaceholderAlignment.baseline: + top = line.baseline - placeholder.baselineOffset; + break; + } + + return ui.TextBox.fromLTRBD( + left, + top, + right, + top + placeholder.height, + paragraphDirection, + ); + } + + @override + ui.TextPosition getPositionForX(double x) { + // See if `x` is closer to the left edge or the right edge of the box. + final bool closerToLeft = x - left < right - x; + return ui.TextPosition( + offset: start.index, + affinity: closerToLeft ? ui.TextAffinity.upstream : ui.TextAffinity.downstream, + ); + } +} + +/// Represents a box in a [FlatTextSpan]. +class SpanBox extends RangeBox { + SpanBox( + this.spanometer, { + required LineBreakResult start, + required LineBreakResult end, + required double width, + required ui.TextDirection paragraphDirection, + required ui.TextDirection boxDirection, + required this.contentDirection, + required this.isSpaceOnly, + }) : span = spanometer.currentSpan, + height = spanometer.height, + baseline = spanometer.ascent, + super(start, end, width, paragraphDirection, boxDirection); + + + final Spanometer spanometer; + final FlatTextSpan span; + + /// The direction of the text inside this box. + /// + /// To illustrate the difference between [boxDirection] and [contentDirection] + /// here's an example: + /// + /// In an LTR paragraph, the text "ABC hebrew_word 123 DEF" is rendered as + /// follows: + /// + /// ----> <---- <------------ ----> + /// box direction: LTR RTL RTL LTR + /// +-------------------------------+ + /// | ABC | 123 | drow_werbeh | DEF | + /// +-------------------------------+ + /// content direction: LTR LTR RTL LTR + /// ----> ----> <------------ ----> + /// + /// Notice the box containing "123" flows in the RTL direction (because it + /// comes after an RTL box), while the content of the box flows in the LTR + /// direction (i.e. the text is shown as "123" not "321"). + final ui.TextDirection contentDirection; + + /// Whether this box is made of only white space. + final bool isSpaceOnly; + + /// Whether the contents of this box flow in the left-to-right direction. + bool get isContentLtr => contentDirection == ui.TextDirection.ltr; + + /// Whether the contents of this box flow in the right-to-left direction. + bool get isContentRtl => !isContentLtr; + + /// The distance from the top edge to the bottom edge of the box. + final double height; + + /// The distance from the top edge of the box to the alphabetic baseline of + /// the box. + final double baseline; + + /// Whether this box's range overlaps with the range from [startIndex] to + /// [endIndex]. + bool overlapsWith(int startIndex, int endIndex) { + return startIndex < end.index && start.index < endIndex; + } + + /// Returns the substring of the paragraph that's represented by this box. + /// + /// Trailing newlines are omitted, if any. + String toText() { + return spanometer.paragraph.toPlainText().substring(start.index, end.indexWithoutTrailingNewlines); + } + + /// Returns a [ui.TextBox] representing this range box in the given [line]. + /// + /// The coordinates of the resulting [ui.TextBox] are relative to the + /// paragraph, not to the line. + @override + ui.TextBox toTextBox(EngineLineMetrics line) { + return intersect(line, start.index, end.index); + } + + /// Performs the intersection of this box with the range given by [start] and + /// [end] indices, and returns a [ui.TextBox] representing that intersection. + /// + /// The coordinates of the resulting [ui.TextBox] are relative to the + /// paragraph, not to the line. + ui.TextBox intersect(EngineLineMetrics line, int start, int end) { + final double top = line.baseline - baseline; + + final double before; + if (start <= this.start.index) { + before = 0.0; + } else { + spanometer.currentSpan = span; + before = spanometer._measure(this.start.index, start); + } + + final double after; + if (end >= this.end.indexWithoutTrailingNewlines) { + after = 0.0; + } else { + spanometer.currentSpan = span; + after = spanometer._measure(end, this.end.indexWithoutTrailingNewlines); + } + + final double left, right; + if (isContentLtr) { + // Example: let's say the text is "Loremipsum" and we want to get the box + // for "rem". In this case, `before` is the width of "Lo", and `after` + // is the width of "ipsum". + // + // Here's how the measurements/coordinates look like: + // + // before after + // |----| |----------| + // +---------------------+ + // | L o r e m i p s u m | + // +---------------------+ + // this.left ^ ^ this.right + left = this.left + before; + right = this.right - after; + } else { + // Example: let's say the text is "txet_werbeH" ("Hebrew_text" flowing from + // right to left). Say we want to get the box for "brew". The `before` is + // the width of "He", and `after` is the width of "_text". + // + // after before + // |----------| |----| + // +-----------------------+ + // | t x e t _ w e r b e H | + // +-----------------------+ + // this.left ^ ^ this.right + // + // Notice how `before` and `after` are reversed in the RTL example. That's + // because the text flows from right to left. + left = this.left + after; + right = this.right - before; + } + + // The [RangeBox]'s left and right edges are relative to the line. In order + // to make them relative to the paragraph, we need to add the left edge of + // the line. + return ui.TextBox.fromLTRBD( + line.left + left, + top, + line.left + right, + top + height, + contentDirection, + ); + } + + /// Transforms the [x] coordinate to be relative to this box and matches the + /// flow of content. + /// + /// In LTR paragraphs, the [startOffset] and [endOffset] of an RTL box + /// indicate the visual beginning and end of the box. But the text inside the + /// box flows in the opposite direction (from [endOffset] to [startOffset]). + /// + /// The X (input) is relative to the line, and always from left-to-right + /// independent of paragraph and content direction. + /// + /// Here's how it looks for a box with LTR content: + /// + /// *------------------------lineWidth------------------* + /// *---------------X (input) + /// ┌───────────┬────────────────────────┬───────────────┐ + /// │ │ --content-direction--> │ │ + /// └───────────┴────────────────────────┴───────────────┘ + /// *---X' (output) + /// *---left----* + /// *---------------right----------------* + /// + /// + /// And here's how it looks for a box with RTL content: + /// + /// *------------------------lineWidth------------------* + /// *----------------X (input) + /// ┌───────────┬────────────────────────┬───────────────┐ + /// │ │ <--content-direction-- │ │ + /// └───────────┴────────────────────────┴───────────────┘ + /// (output) X'------------------* + /// *---left----* + /// *---------------right----------------* + /// + double _makeXRelativeToContent(double x) { + return isContentRtl ? right - x : x - left; + } + + @override + ui.TextPosition getPositionForX(double x) { + spanometer.currentSpan = span; + + x = _makeXRelativeToContent(x); + + final int startIndex = start.index; + final int endIndex = end.indexWithoutTrailingNewlines; + // The resulting `cutoff` is the index of the character where the `x` offset + // falls. We should return the text position of either `cutoff` or + // `cutoff + 1` depending on which one `x` is closer to. + // + // offset x + // ↓ + // "A B C D E F" + // ↑ + // cutoff + final int cutoff = spanometer.forceBreak( + startIndex, + endIndex, + availableWidth: x, + allowEmpty: true, + ); + + if (cutoff == endIndex) { + return ui.TextPosition( + offset: cutoff, + affinity: ui.TextAffinity.upstream, + ); + } + + final double lowWidth = spanometer._measure(startIndex, cutoff); + final double highWidth = spanometer._measure(startIndex, cutoff + 1); + + // See if `x` is closer to `cutoff` or `cutoff + 1`. + if (x - lowWidth < highWidth - x) { + // The offset is closer to cutoff. + return ui.TextPosition( + offset: cutoff, + affinity: ui.TextAffinity.downstream, + ); + } else { + // The offset is closer to cutoff + 1. + return ui.TextPosition( + offset: cutoff + 1, + affinity: ui.TextAffinity.upstream, + ); + } + } +} + +/// Represents a segment in a line of a paragraph. +/// +/// For example, this line: "Lorem ipsum dolor sit" is broken up into the +/// following segments: +/// +/// - "Lorem " +/// - "ipsum " +/// - "dolor " +/// - "sit" +class LineSegment { + LineSegment({ + required this.span, + required this.start, + required this.end, + required this.width, + required this.widthIncludingSpace, + }); + + /// The span that this segment belongs to. + final ParagraphSpan span; + + /// The index of the beginning of the segment in the paragraph. + final LineBreakResult start; + + /// The index of the end of the segment in the paragraph. + final LineBreakResult end; + + /// The width of the segment excluding any trailing white space. + final double width; + + /// The width of the segment including any trailing white space. + final double widthIncludingSpace; + + /// The width of the trailing white space in the segment. + double get widthOfTrailingSpace => widthIncludingSpace - width; + + /// Whether this segment is made of only white space. + /// + /// We rely on the [width] to determine this because relying on incides + /// doesn't work well for placeholders (they are zero-length strings). + bool get isSpaceOnly => width == 0; +} + +/// Builds instances of [EngineLineMetrics] for the given [paragraph]. +/// +/// Usage of this class starts by calling [LineBuilder.first] to start building +/// the first line of the paragraph. +/// +/// Then new line breaks can be found by calling [LineBuilder.findNextBreak]. +/// +/// The line can be extended one or more times before it's built by calling +/// [LineBuilder.build] which generates the [EngineLineMetrics] instace. +/// +/// To start building the next line, simply call [LineBuilder.nextLine] which +/// creates a new [LineBuilder] that can be extended and built and so on. +class LineBuilder { + LineBuilder._( + this.paragraph, + this.spanometer, { + required this.maxWidth, + required this.start, + required this.lineNumber, + required this.accumulatedHeight, + }) : end = start; + + /// Creates a [LineBuilder] for the first line in a paragraph. + factory LineBuilder.first( + CanvasParagraph paragraph, + Spanometer spanometer, { + required double maxWidth, + }) { + return LineBuilder._( + paragraph, + spanometer, + maxWidth: maxWidth, + lineNumber: 0, + start: const LineBreakResult.sameIndex(0, LineBreakType.prohibited), + accumulatedHeight: 0.0, + ); + } + + final List _segments = []; + final List _boxes = []; + + final double maxWidth; + final CanvasParagraph paragraph; + final Spanometer spanometer; + final LineBreakResult start; + final int lineNumber; + + /// The accumulated height of all preceding lines, excluding the current line. + final double accumulatedHeight; + + /// The index of the end of the line so far. + LineBreakResult end; + + /// The width of the line so far, excluding trailing white space. + double width = 0.0; + + /// The width of the line so far, including trailing white space. + double widthIncludingSpace = 0.0; + + /// The width of trailing white space in the line. + double get widthOfTrailingSpace => widthIncludingSpace - width; + + /// The distance from the top of the line to the alphabetic baseline. + double ascent = 0.0; + + /// The distance from the bottom of the line to the alphabetic baseline. + double descent = 0.0; + + /// The height of the line so far. + double get height => ascent + descent; + + /// The last segment in this line. + LineSegment get lastSegment => _segments.last; + + bool get isEmpty => _segments.isEmpty; + bool get isNotEmpty => _segments.isNotEmpty; + + /// The horizontal offset necessary for the line to be correctly aligned. + double get alignOffset { + final double emptySpace = maxWidth - width; + final ui.TextAlign textAlign = paragraph.paragraphStyle.effectiveTextAlign; + + switch (textAlign) { + case ui.TextAlign.center: + return emptySpace / 2.0; + case ui.TextAlign.right: + return emptySpace; + case ui.TextAlign.start: + return _paragraphDirection == ui.TextDirection.rtl ? emptySpace : 0.0; + case ui.TextAlign.end: + return _paragraphDirection == ui.TextDirection.rtl ? 0.0 : emptySpace; + default: + return 0.0; + } + } + + /// Measures the width of text between the end of this line and [newEnd]. + double getAdditionalWidthTo(LineBreakResult newEnd) { + // If the extension is all made of space characters, it shouldn't add + // anything to the width. + if (end.index == newEnd.indexWithoutTrailingSpaces) { + return 0.0; + } + + return widthOfTrailingSpace + spanometer.measure(end, newEnd); + } + + bool get _isLastBoxAPlaceholder { + if (_boxes.isEmpty) { + return false; + } + return _boxes.last is PlaceholderBox; + } + + ui.TextDirection get _paragraphDirection => + paragraph.paragraphStyle.effectiveTextDirection; + + late ui.TextDirection _currentBoxDirection = _paragraphDirection; + + late ui.TextDirection _currentContentDirection = _paragraphDirection; + + bool _shouldCreateBoxBeforeExtendingTo(DirectionalPosition newEnd) { + // When the direction changes, we need to make sure to put them in separate + // boxes. + return newEnd.isSpaceOnly || _currentBoxDirection != newEnd.textDirection || _currentContentDirection != newEnd.textDirection; + } + + /// Extends the line by setting a [newEnd]. + void extendTo(DirectionalPosition newEnd) { + ascent = math.max(ascent, spanometer.ascent); + descent = math.max(descent, spanometer.descent); + + // When the direction changes, we need to make sure to put them in separate + // boxes. + if (_shouldCreateBoxBeforeExtendingTo(newEnd)) { + createBox(); + } + _currentBoxDirection = newEnd.textDirection ?? _currentBoxDirection; + _currentContentDirection = newEnd.textDirection ?? ui.TextDirection.ltr; + + _addSegment(_createSegment(newEnd.lineBreak)); + if (newEnd.isSpaceOnly) { + // Whitespace sequences go in their own boxes. + createBox(isSpaceOnly: true); + } + } + + /// Extends the line to the end of the paragraph. + void extendToEndOfText() { + if (end.type == LineBreakType.endOfText) { + return; + } + + final LineBreakResult endOfText = LineBreakResult.sameIndex( + paragraph.toPlainText().length, + LineBreakType.endOfText, + ); + + // The spanometer may not be ready in some cases. E.g. when the paragraph + // is made up of only placeholders and no text. + if (spanometer.isReady) { + ascent = math.max(ascent, spanometer.ascent); + descent = math.max(descent, spanometer.descent); + _addSegment(_createSegment(endOfText)); + } else { + end = endOfText; + } + } + + void addPlaceholder(PlaceholderSpan placeholder) { + // Increase the line's height to fit the placeholder, if necessary. + final double ascent, descent; + switch (placeholder.alignment) { + case ui.PlaceholderAlignment.top: + // The placeholder is aligned to the top of text, which means it has the + // same `ascent` as the remaining text. We only need to extend the + // `descent` enough to fit the placeholder. + ascent = this.ascent; + descent = placeholder.height - this.ascent; + break; + + case ui.PlaceholderAlignment.bottom: + // The opposite of `top`. The `descent` is the same, but we extend the + // `ascent`. + ascent = placeholder.height - this.descent; + descent = this.descent; + break; + + case ui.PlaceholderAlignment.middle: + final double textMidPoint = height / 2; + final double placeholderMidPoint = placeholder.height / 2; + final double diff = placeholderMidPoint - textMidPoint; + ascent = this.ascent + diff; + descent = this.descent + diff; + break; + + case ui.PlaceholderAlignment.aboveBaseline: + ascent = placeholder.height; + descent = 0.0; + break; + + case ui.PlaceholderAlignment.belowBaseline: + ascent = 0.0; + descent = placeholder.height; + break; + + case ui.PlaceholderAlignment.baseline: + ascent = placeholder.baselineOffset; + descent = placeholder.height - ascent; + break; + } + + this.ascent = math.max(this.ascent, ascent); + this.descent = math.max(this.descent, descent); + + _addSegment(LineSegment( + span: placeholder, + start: end, + end: end, + width: placeholder.width, + widthIncludingSpace: placeholder.width, + )); + + // Add the placeholder box. + _boxes.add(PlaceholderBox( + placeholder, + index: _currentBoxStart, + paragraphDirection: _paragraphDirection, + boxDirection: _currentBoxDirection, + )); + _currentBoxStartOffset = widthIncludingSpace; + } + + /// Creates a new segment to be appended to the end of this line. + LineSegment _createSegment(LineBreakResult segmentEnd) { + // The segment starts at the end of the line. + final LineBreakResult segmentStart = end; + return LineSegment( + span: spanometer.currentSpan, + start: segmentStart, + end: segmentEnd, + width: spanometer.measure(segmentStart, segmentEnd), + widthIncludingSpace: + spanometer.measureIncludingSpace(segmentStart, segmentEnd), + ); + } + + /// Adds a segment to this line. + /// + /// It adjusts the width properties to accommodate the new segment. It also + /// sets the line end to the end of the segment. + void _addSegment(LineSegment segment) { + _segments.add(segment); + + // Adding a space-only segment has no effect on `width` because it doesn't + // include trailing white space. + if (!segment.isSpaceOnly) { + // Add the width of previous trailing space. + width += widthOfTrailingSpace + segment.width; + } + widthIncludingSpace += segment.widthIncludingSpace; + end = segment.end; + } + + /// Removes the latest [LineSegment] added by [_addSegment]. + /// + /// It re-adjusts the width properties and the end of the line. + LineSegment _popSegment() { + final LineSegment poppedSegment = _segments.removeLast(); + + if (_segments.isEmpty) { + width = 0.0; + widthIncludingSpace = 0.0; + end = start; + } else { + widthIncludingSpace -= poppedSegment.widthIncludingSpace; + end = lastSegment.end; + + // Now, let's figure out what to do with `width`. + + // Popping a space-only segment has no effect on `width`. + if (!poppedSegment.isSpaceOnly) { + // First, we subtract the width of the popped segment. + width -= poppedSegment.width; + + // Second, we subtract all trailing spaces from `width`. There could be + // multiple trailing segments that are space-only. + double widthOfTrailingSpace = 0.0; + int i = _segments.length - 1; + while (i >= 0 && _segments[i].isSpaceOnly) { + // Since the segment is space-only, `widthIncludingSpace` contains + // the width of the space and nothing else. + widthOfTrailingSpace += _segments[i].widthIncludingSpace; + i--; + } + if (i >= 0) { + // Having `i >= 0` means in the above loop we stopped at a + // non-space-only segment. We should also subtract its trailing spaces. + widthOfTrailingSpace += _segments[i].widthOfTrailingSpace; + } + width -= widthOfTrailingSpace; + } + } + + return poppedSegment; + } + + /// Force-breaks the line in order to fit in [maxWidth] while trying to extend + /// to [nextBreak]. + /// + /// This should only be called when there isn't enough width to extend to + /// [nextBreak], and either of the following is true: + /// + /// 1. An ellipsis is being appended to this line, OR + /// 2. The line doesn't have any line break opportunities and has to be + /// force-broken. + void forceBreak( + DirectionalPosition nextBreak, { + required bool allowEmpty, + String? ellipsis, + }) { + if (ellipsis == null) { + final double availableWidth = maxWidth - widthIncludingSpace; + final int breakingPoint = spanometer.forceBreak( + end.index, + nextBreak.lineBreak.indexWithoutTrailingSpaces, + availableWidth: availableWidth, + allowEmpty: allowEmpty, + ); + + // This condition can be true in the following case: + // 1. Next break is only one character away, with zero or many spaces. AND + // 2. There isn't enough width to fit the single character. AND + // 3. `allowEmpty` is false. + if (breakingPoint == nextBreak.lineBreak.indexWithoutTrailingSpaces) { + // In this case, we just extend to `nextBreak` instead of creating a new + // artificial break. It's safe (and better) to do so, because we don't + // want the trailing white space to go to the next line. + extendTo(nextBreak); + } else { + extendTo(nextBreak.copyWithIndex(breakingPoint)); + } + return; + } + + // For example: "foo bar baz". Let's say all characters have the same width, and + // the constraint width can only fit 9 characters "foo bar b". So if the + // paragraph has an ellipsis, we can't just remove the last segment "baz" + // and replace it with "..." because that would overflow. + // + // We need to keep popping segments until we are able to fit the "..." + // without overflowing. In this example, that would be: "foo ba..." + + final double ellipsisWidth = spanometer.measureText(ellipsis); + final double availableWidth = maxWidth - ellipsisWidth; + + // First, we create the new segment until `nextBreak`. + LineSegment segmentToBreak = _createSegment(nextBreak.lineBreak); + + // Then, we keep popping until we find the segment that has to be broken. + // After the loop ends, two things are correct: + // 1. All remaining segments in `_segments` can fit within constraints. + // 2. Adding `segmentToBreak` causes the line to overflow. + while (_segments.isNotEmpty && widthIncludingSpace > availableWidth) { + segmentToBreak = _popSegment(); + } + + spanometer.currentSpan = segmentToBreak.span as FlatTextSpan; + final double availableWidthForSegment = + availableWidth - widthIncludingSpace; + final int breakingPoint = spanometer.forceBreak( + segmentToBreak.start.index, + segmentToBreak.end.index, + availableWidth: availableWidthForSegment, + allowEmpty: allowEmpty, + ); + + // There's a possibility that the end of line has moved backwards, so we + // need to remove some boxes in that case. + while (_boxes.isNotEmpty && _boxes.last.end.index > breakingPoint) { + _boxes.removeLast(); + } + _currentBoxStartOffset = widthIncludingSpace; + + extendTo(nextBreak.copyWithIndex(breakingPoint)); + } + + LineBreakResult get _currentBoxStart { + if (_boxes.isEmpty) { + return start; + } + // The end of the last box is the start of the new box. + return _boxes.last.end; + } + + double _currentBoxStartOffset = 0.0; + + double get _currentBoxWidth => widthIncludingSpace - _currentBoxStartOffset; + + /// Cuts a new box in the line. + /// + /// If this is the first box in the line, it'll start at the beginning of the + /// line. Else, it'll start at the end of the last box. + /// + /// A box should be cut whenever the end of line is reached, when switching + /// from one span to another, or when switching text direction. + /// + /// [isSpaceOnly] indicates that the box contains nothing but whitespace + /// characters. + void createBox({bool isSpaceOnly = false}) { + final LineBreakResult boxStart = _currentBoxStart; + final LineBreakResult boxEnd = end; + // Avoid creating empty boxes. This could happen when the end of a span + // coincides with the end of a line. In this case, `createBox` is called twice. + if (boxStart.index == boxEnd.index) { + return; + } + + _boxes.add(SpanBox( + spanometer, + start: boxStart, + end: boxEnd, + width: _currentBoxWidth, + paragraphDirection: _paragraphDirection, + boxDirection: _currentBoxDirection, + contentDirection: _currentContentDirection, + isSpaceOnly: isSpaceOnly, + )); + + _currentBoxStartOffset = widthIncludingSpace; + } + + /// Builds the [EngineLineMetrics] instance that represents this line. + EngineLineMetrics build({String? ellipsis}) { + // At the end of each line, we cut the last box of the line. + createBox(); + _positionBoxes(); + + final double ellipsisWidth = + ellipsis == null ? 0.0 : spanometer.measureText(ellipsis); + + final int endIndexWithoutNewlines = math.max(start.index, end.indexWithoutTrailingNewlines); + final bool hardBreak; + if (end.type != LineBreakType.endOfText && _isLastBoxAPlaceholder) { + hardBreak = false; + } else { + hardBreak = end.isHard; + } + return EngineLineMetrics.rich( + lineNumber, + ellipsis: ellipsis, + startIndex: start.index, + endIndex: end.index, + endIndexWithoutNewlines: endIndexWithoutNewlines, + hardBreak: hardBreak, + width: width + ellipsisWidth, + widthWithTrailingSpaces: widthIncludingSpace + ellipsisWidth, + left: alignOffset, + height: height, + baseline: accumulatedHeight + ascent, + ascent: ascent, + descent: descent, + boxes: _boxes, + ); + } + + /// Positions the boxes and takes into account their directions, and the + /// paragraph's direction. + void _positionBoxes() { + final List boxes = _boxes; + + int i = 0; + double cumulativeWidth = 0.0; + while (i < boxes.length) { + final RangeBox box = boxes[i]; + if (box.boxDirection == _paragraphDirection) { + // The box is in the same direction as the paragraph. + box.startOffset = cumulativeWidth; + box.lineWidth = width; + + cumulativeWidth += box.width; + i++; + continue; + } + + // At this point, we found a box that has the opposite direction to the + // paragraph. This could be a sequence of one or more boxes. + // + // These boxes should flow in the opposite direction. So we need to + // position them in reverse order. + // + // If the last box in the sequence is a space-only box (contains only + // whitespace characters), it should be excluded from the sequence. + // + // Example: an LTR paragraph with the contents: + // + // "ABC rtl1 rtl2 rtl3 XYZ" + // ^ ^ ^ ^ + // SP1 SP2 SP3 SP4 + // + // + // box direction: LTR RTL LTR + // |------>|<-----------------------|------> + // +----------------------------------------+ + // | ABC | | rtl3 | | rtl2 | | rtl1 | | XYZ | + // +----------------------------------------+ + // ^ ^ ^ ^ + // SP1 SP3 SP2 SP4 + // + // Notice how SP2 and SP3 are flowing in the RTL direction because of the + // surrounding RTL words. SP4 is also preceded by an RTL word, but it marks + // the end of the RTL sequence, so it goes back to flowing in the paragraph + // direction (LTR). + + final int first = i; + int lastNonSpaceBox = first; + i++; + while (i < boxes.length && boxes[i].boxDirection != _paragraphDirection) { + final RangeBox box = boxes[i]; + if (box is SpanBox && box.isSpaceOnly) { + // Do nothing. + } else { + lastNonSpaceBox = i; + } + i++; + } + final int last = lastNonSpaceBox; + i = lastNonSpaceBox + 1; + + // The range (first:last) is the entire sequence of boxes that have the + // opposite direction to the paragraph. + final double sequenceWidth = + _positionBoxesInReverse(boxes, first, last, startOffset: cumulativeWidth); + cumulativeWidth += sequenceWidth; + } + } + + /// Positions a sequence of boxes in the direction opposite to the paragraph + /// text direction. + /// + /// This is needed when a right-to-left sequence appears in the middle of a + /// left-to-right paragraph, or vice versa. + /// + /// Returns the total width of all the positioned boxes in the sequence. + /// + /// [first] and [last] are expected to be inclusive. + double _positionBoxesInReverse( + List boxes, + int first, + int last, { + required double startOffset, + }) { + double cumulativeWidth = 0.0; + for (int i = last; i >= first; i--) { + // Update the visual position of each box. + final RangeBox box = boxes[i]; + assert(box.boxDirection != _paragraphDirection); + box.startOffset = startOffset + cumulativeWidth; + box.lineWidth = width; + + cumulativeWidth += box.width; + } + return cumulativeWidth; + } + + /// Finds the next line break after the end of this line. + DirectionalPosition findNextBreak() { + final String text = paragraph.toPlainText(); + final int maxEnd = spanometer.currentSpan.end; + final LineBreakResult result = nextLineBreak(text, end.index, maxEnd: maxEnd); + // The current end of the line is the beginning of the next block. + return getDirectionalBlockEnd(text, end, result); + } + + /// Creates a new [LineBuilder] to build the next line in the paragraph. + LineBuilder nextLine() { + return LineBuilder._( + paragraph, + spanometer, + maxWidth: maxWidth, + start: end, + lineNumber: lineNumber + 1, + accumulatedHeight: accumulatedHeight + height, + ); + } +} + +/// Responsible for taking measurements within spans of a paragraph. +/// +/// Can't perform measurements across spans. To measure across spans, multiple +/// measurements have to be taken. +/// +/// Before performing any measurement, the [currentSpan] has to be set. Once +/// it's set, the [Spanometer] updates the underlying [context] so that +/// subsequent measurements use the correct styles. +class Spanometer { + Spanometer(this.paragraph, this.context); + + final CanvasParagraph paragraph; + final html.CanvasRenderingContext2D context; + + static RulerHost _rulerHost = RulerHost(); + + static Map _rulers = + {}; + + @visibleForTesting + static Map get rulers => _rulers; + + /// Clears the cache of rulers that are used for measuring text height and + /// baseline metrics. + static void clearRulersCache() { + _rulers.forEach((TextHeightStyle style, TextHeightRuler ruler) { + ruler.dispose(); + }); + _rulers.clear(); + } + + String _cssFontString = ''; + + double? get letterSpacing => currentSpan.style.letterSpacing; + + TextHeightRuler? _currentRuler; + FlatTextSpan? _currentSpan; + + FlatTextSpan get currentSpan => _currentSpan!; + set currentSpan(FlatTextSpan? span) { + if (span == _currentSpan) { + return; + } + _currentSpan = span; + + // No need to update css font string when `span` is null. + if (span == null) { + _currentRuler = null; + return; + } + + // Update the height ruler. + // If the ruler doesn't exist in the cache, create a new one and cache it. + final TextHeightStyle heightStyle = span.style.heightStyle; + TextHeightRuler? ruler = _rulers[heightStyle]; + if (ruler == null) { + ruler = TextHeightRuler(heightStyle, _rulerHost); + _rulers[heightStyle] = ruler; + } + _currentRuler = ruler; + + // Update the font string if it's different from the previous span. + final String cssFontString = span.style.cssFontString; + if (_cssFontString != cssFontString) { + _cssFontString = cssFontString; + context.font = cssFontString; + } + } + + /// Whether the spanometer is ready to take measurements. + bool get isReady => _currentSpan != null; + + /// The distance from the top of the current span to the alphabetic baseline. + double get ascent => _currentRuler!.alphabeticBaseline; + + /// The distance from the bottom of the current span to the alphabetic baseline. + double get descent => height - ascent; + + /// The line height of the current span. + double get height => _currentRuler!.height; + + /// Measures the width of text between two line breaks. + /// + /// Doesn't include the width of any trailing white space. + double measure(LineBreakResult start, LineBreakResult end) { + return _measure(start.index, end.indexWithoutTrailingSpaces); + } + + /// Measures the width of text between two line breaks. + /// + /// Includes the width of trailing white space, if any. + double measureIncludingSpace(LineBreakResult start, LineBreakResult end) { + return _measure(start.index, end.indexWithoutTrailingNewlines); + } + + double measureText(String text) { + return measureSubstring(context, text, 0, text.length); + } + + /// In a continuous, unbreakable block of text from [start] to [end], finds + /// the point where text should be broken to fit in the given [availableWidth]. + /// + /// The [start] and [end] indices have to be within the same text span. + /// + /// When [allowEmpty] is true, the result is guaranteed to be at least one + /// character after [start]. But if [allowEmpty] is false and there isn't + /// enough [availableWidth] to fit the first character, then [start] is + /// returned. + /// + /// See also: + /// - [LineBuilder.forceBreak]. + int forceBreak( + int start, + int end, { + required double availableWidth, + required bool allowEmpty, + }) { + assert(_currentSpan != null); + + final FlatTextSpan span = currentSpan; + + // Make sure the range is within the current span. + assert(start >= span.start && start <= span.end); + assert(end >= span.start && end <= span.end); + + if (availableWidth <= 0.0) { + return allowEmpty ? start : start + 1; + } + + int low = start; + int high = end; + do { + final int mid = (low + high) ~/ 2; + final double width = _measure(start, mid); + if (width < availableWidth) { + low = mid; + } else if (width > availableWidth) { + high = mid; + } else { + low = high = mid; + } + } while (high - low > 1); + + if (low == start && !allowEmpty) { + low++; + } + return low; + } + + double _measure(int start, int end) { + assert(_currentSpan != null); + final FlatTextSpan span = currentSpan; + + // Make sure the range is within the current span. + assert(start >= span.start && start <= span.end); + assert(end >= span.start && end <= span.end); + + final String text = paragraph.toPlainText(); + return measureSubstring( + context, + text, + start, + end, + letterSpacing: letterSpacing, + ); + } +} diff --git a/lib/web_ui/lib/src/engine/text/line_break_properties.dart b/lib/web_ui/lib/src/engine/text/line_break_properties.dart index f9dbc99bef6f9..516471c32b09a 100644 --- a/lib/web_ui/lib/src/engine/text/line_break_properties.dart +++ b/lib/web_ui/lib/src/engine/text/line_break_properties.dart @@ -12,8 +12,7 @@ // # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. // # For terms of use, see http://www.unicode.org/terms_of_use.html -// @dart = 2.10 -part of engine; +import 'unicode_range.dart'; /// For an explanation of these enum values, see: /// @@ -65,10 +64,22 @@ const String _packedLineBreakProperties = '00000008A0009!B000a!C000b000cD000d!E000e000vA000w!F000x!G000y!H000z!I0010!J0011!K0012!I0013!H0014!L0015!M0016!I0017!J0018!N0019!O001a!N001b!P001c001lQ001m001nN001o001qI001r!G001s002iI002j!L002k!J002l!M002m003eI003f!L003g!B003h!R003i!I003j003oA003p!D003q004fA004g!S004h!L004i!K004j004lJ004m004qI004r!H004s!I004t!B004u004vI004w!K004x!J004y004zI0050!T00510056I0057!H0058005aI005b!L005c00jrI00js!T00jt00jvI00jw!T00jx00keI00kf!T00kg00lbI00lc00niA00nj!S00nk00nvA00nw00o2S00o300ofA00og00otI00ou!N00ov00w2I00w300w9A00wa013cI013d!N013e!B013h013iI013j!J013l014tA014u!B014v!A014w!I014x014yA014z!I01500151A0152!G0153!A015c0162U0167016aU016b016wI016x016zK01700171N01720173I0174017eA017f!G017g!A017i017jG017k018qI018r019bA019c019lQ019m!K019n019oQ019p019rI019s!A019t01cjI01ck!G01cl!I01cm01csA01ct01cuI01cv01d0A01d101d2I01d301d4A01d5!I01d601d9A01da01dbI01dc01dlQ01dm01e8I01e9!A01ea01f3I01f401fuA01fx01idI01ie01ioA01ip!I01j401jdQ01je01kaI01kb01kjA01kk01knI01ko!N01kp!G01kq!I01kt!A01ku01kvJ01kw01lhI01li01llA01lm!I01ln01lvA01lw!I01lx01lzA01m0!I01m101m5A01m801ncI01nd01nfA01ni01qfI01qr01r5A01r6!I01r701s3A01s401tlI01tm01toA01tp!I01tq01u7A01u8!I01u901ufA01ug01upI01uq01urA01us01utB01uu01v3Q01v401vkI01vl01vnA01vp01x5I01x8!A01x9!I01xa01xgA01xj01xkA01xn01xpA01xq!I01xz!A01y401y9I01ya01ybA01ye01ynQ01yo01ypI01yq01yrK01ys01ywI01yx!K01yy!I01yz!J01z001z1I01z2!A01z501z7A01z9020pI020s!A020u020yA02130214A02170219A021d!A021l021qI021y0227Q02280229A022a022cI022d!A022e!I022p022rA022t0249I024c!A024d!I024e024lA024n024pA024r024tA024w025dI025e025fA025i025rQ025s!I025t!J0261!I02620267A0269026bA026d027tI027w!A027x!I027y0284A02870288A028b028dA028l028nA028s028xI028y028zA0292029bQ029c029jI029u!A029v02bdI02bi02bmA02bq02bsA02bu02bxA02c0!I02c7!A02cm02cvQ02cw02d4I02d5!J02d6!I02dc02dgA02dh02f1I02f202f8A02fa02fcA02fe02fhA02fp02fqA02fs02g1I02g202g3A02g602gfQ02gn!T02go02gwI02gx02gzA02h0!T02h102ihI02ik!A02il!I02im02isA02iu02iwA02iy02j1A02j902jaA02ji02jlI02jm02jnA02jq02jzQ02k102k2I02kg02kjA02kk02m2I02m302m4A02m5!I02m602mcA02me02mgA02mi02mlA02mm02muI02mv!A02mw02n5I02n602n7A02na02njQ02nk02nsI02nt!K02nu02nzI02o102o3A02o502pyI02q2!A02q702qcA02qe!A02qg02qnA02qu02r3Q02r602r7A02r802t6I02tb!J02tc02trI02ts02u1Q02u202u3B02v502x9I02xc02xlQ02xo02yoI02yp02ysT02yt!I02yu02yvT02yw!S02yx02yyT02yz!B02z0!S02z102z5G02z6!S02z7!I02z8!G02z902zbI02zc02zdA02ze02zjI02zk02ztQ02zu0303I0304!B0305!A0306!I0307!A0308!I0309!A030a!L030b!R030c!L030d!R030e030fA030g031oI031t0326A0327!B0328032cA032d!B032e032fA032g032kI032l032vA032x033wA033y033zB03400345I0346!A0347034fI034g034hT034i!B034j!T034k034oI034p034qS035s037jI037k037tQ037u037vB037w039rI039s03a1Q03a203cvI03cw03fjV03fk03hjW03hk03jzX03k003tmI03tp03trA03ts!I03tt!B03tu03y5I03y8!B03y904fzI04g0!B04g104gqI04gr!L04gs!R04gw04iyI04iz04j1B04j204k1I04k204k4A04kg04kxI04ky04l0A04l104l2B04lc04ltI04lu04lvA04m804moI04mq04mrA04n404pfI04pg04phB04pi!Y04pj!I04pk!B04pl!I04pm!B04pn!J04po04ppI04ps04q1Q04q804qpI04qq04qrG04qs04qtB04qu!T04qv!I04qw04qxG04qy!I04qz04r1A04r2!S04r404rdQ04rk04ucI04ud04ueA04uf04vcI04vd!A04ve04ymI04yo04yzA04z404zfA04zk!I04zo04zpG04zq04zzQ0500053dI053k053tQ053u055iI055j055nA055q058cI058f!A058g058pQ058w0595Q059c059pI059s05a8A05c005c4A05c505dfI05dg05dwA05dx05e3I05e805ehQ05ei05ejB05ek!I05el05eoB05ep05eyI05ez05f7A05f805fgI05fk05fmA05fn05ggI05gh05gtA05gu05gvI05gw05h5Q05h605idI05ie05irA05j005k3I05k405knA05kr05kvB05kw05l5Q05l905lbI05lc05llQ05lm05mlI05mm05mnB05mo05onI05ow05oyA05oz!I05p005pkA05pl05poI05pp!A05pq05pvI05pw!A05px05pyI05pz05q1A05q205vjI05vk05x5A05x705xbA05xc06bgI06bh!T06bi!I06bk06bqB06br!S06bs06buB06bv!Z06bw!A06bx!a06by06bzA06c0!B06c1!S06c206c3B06c4!b06c506c7I06c806c9H06ca!L06cb06cdH06ce!L06cf!H06cg06cjI06ck06cmc06cn!B06co06cpD06cq06cuA06cv!S06cw06d3K06d4!I06d506d6H06d7!I06d806d9Y06da06dfI06dg!N06dh!L06di!R06dj06dlY06dm06dxI06dy!B06dz!I06e006e3B06e4!I06e506e7B06e8!d06e906ecI06ee06enA06eo06f0I06f1!L06f2!R06f306fgI06fh!L06fi!R06fk06fwI06g006g6J06g7!K06g806glJ06gm!K06gn06gqJ06gr!K06gs06gtJ06gu!K06gv06hbJ06hc06i8A06io06iqI06ir!K06is06iwI06ix!K06iy06j9I06ja!J06jb06q9I06qa06qbJ06qc06weI06wf!c06wg06x3I06x4!L06x5!R06x6!L06x7!R06x806xlI06xm06xne06xo06y0I06y1!L06y2!R06y3073jI073k073ne073o07i7I07i807ibe07ic07irI07is07ite07iu07ivI07iw!e07ix!I07iy07j0e07j1!f07j207j3e07j407jsI07jt07jve07jw07l3I07l4!e07l507lqI07lr!e07ls07ngI07nh07nse07nt07nwI07nx!e07ny!I07nz07o1e07o2!I07o307o4e07o507o7I07o807o9e07oa07obI07oc!e07od07oeI07of07ohe07oi07opI07oq!e07or07owI07ox07p1e07p2!I07p307p4e07p5!f07p6!e07p707p8I07p907pge07ph07pjI07pk07ple07pm07ppf07pq07ruI07rv07s0H07s1!I07s207s3G07s4!e07s507s7I07s8!L07s9!R07sa!L07sb!R07sc!L07sd!R07se!L07sf!R07sg!L07sh!R07si!L07sj!R07sk!L07sl!R07sm07usI07ut!L07uu!R07uv07vpI07vq!L07vr!R07vs!L07vt!R07vu!L07vv!R07vw!L07vx!R07vy!L07vz!R07w00876I0877!L0878!R0879!L087a!R087b!L087c!R087d!L087e!R087f!L087g!R087h!L087i!R087j!L087k!R087l!L087m!R087n!L087o!R087p!L087q!R087r!L087s!R087t089jI089k!L089l!R089m!L089n!R089o08ajI08ak!L08al!R08am08viI08vj08vlA08vm08vnI08vt!G08vu08vwB08vx!I08vy!G08vz!B08w008z3I08z4!B08zj!A08zk0926I09280933A0934093hH093i093pB093q!I093r!B093s!L093t!B093u093vI093w093xH093y093zI09400941H0942!L0943!R0944!L0945!R0946!L0947!R0948!L0949!R094a094dB094e!G094f!I094g094hB094i!I094j094kB094l094pI094q094rb094s094uB094v!I094w094xB094y!L094z0956B0957!I0958!B0959!I095a095bB095c095eI096o097de097f099ve09a809g5e09gw09h7e09hc!B09hd09heR09hf09hge09hh!Y09hi09hje09hk!L09hl!R09hm!L09hn!R09ho!L09hp!R09hq!L09hr!R09hs!L09ht!R09hu09hve09hw!L09hx!R09hy!L09hz!R09i0!L09i1!R09i2!L09i3!R09i4!Y09i5!L09i609i7R09i809ihe09ii09inA09io09ise09it!A09iu09iye09iz09j0Y09j109j3e09j5!Y09j6!e09j7!Y09j8!e09j9!Y09ja!e09jb!Y09jc!e09jd!Y09je09k2e09k3!Y09k409kye09kz!Y09l0!e09l1!Y09l2!e09l3!Y09l409l9e09la!Y09lb09lge09lh09liY09ll09lmA09ln09lqY09lr!e09ls09ltY09lu!e09lv!Y09lw!e09lx!Y09ly!e09lz!Y09m0!e09m1!Y09m209mqe09mr!Y09ms09nme09nn!Y09no!e09np!Y09nq!e09nr!Y09ns09nxe09ny!Y09nz09o4e09o509o6Y09o709oae09ob09oeY09of!e09ol09pre09pt09see09sg09ure09v409vjY09vk09wee09wg09xje09xk09xrI09xs0fcve0fcw0fenI0feo0vmce0vmd!Y0vme0wi4e0wi80wjqe0wk00wl9I0wla0wlbB0wlc0wssI0wst!B0wsu!G0wsv!B0wsw0wtbI0wtc0wtlQ0wtm0wviI0wvj0wvmA0wvn!I0wvo0wvxA0wvy0wwtI0wwu0wwvA0www0wz3I0wz40wz5A0wz6!I0wz70wzbB0wzk0x6pI0x6q!A0x6r0x6tI0x6u!A0x6v0x6yI0x6z!A0x700x7mI0x7n0x7rA0x7s0x7vI0x7w!A0x800x87I0x88!K0x890x9vI0x9w0x9xT0x9y0x9zG0xa80xa9A0xaa0xbnI0xbo0xc5A0xce0xcfB0xcg0xcpQ0xcw0xddA0xde0xdnI0xdo!T0xdp0xdqI0xdr!A0xds0xe1Q0xe20xetI0xeu0xf1A0xf20xf3B0xf40xfqI0xfr0xg3A0xgf!I0xgg0xh8V0xhc0xhfA0xhg0xiqI0xir0xj4A0xj50xjaI0xjb0xjdB0xje0xjjI0xjk0xjtQ0xjy0xkfI0xkg0xkpQ0xkq0xm0I0xm10xmeA0xmo0xmqI0xmr!A0xms0xmzI0xn00xn1A0xn40xndQ0xng!I0xnh0xnjB0xnk0xreI0xrf0xrjA0xrk0xrlB0xrm0xroI0xrp0xrqA0xs10xyaI0xyb0xyiA0xyj!B0xyk0xylA0xyo0xyxQ0xz4!g0xz50xzvh0xzw!g0xzx0y0nh0y0o!g0y0p0y1fh0y1g!g0y1h0y27h0y28!g0y290y2zh0y30!g0y310y3rh0y3s!g0y3t0y4jh0y4k!g0y4l0y5bh0y5c!g0y5d0y63h0y64!g0y650y6vh0y6w!g0y6x0y7nh0y7o!g0y7p0y8fh0y8g!g0y8h0y97h0y98!g0y990y9zh0ya0!g0ya10yarh0yas!g0yat0ybjh0ybk!g0ybl0ycbh0ycc!g0ycd0yd3h0yd4!g0yd50ydvh0ydw!g0ydx0yenh0yeo!g0yep0yffh0yfg!g0yfh0yg7h0yg8!g0yg90ygzh0yh0!g0yh10yhrh0yhs!g0yht0yijh0yik!g0yil0yjbh0yjc!g0yjd0yk3h0yk4!g0yk50ykvh0ykw!g0ykx0ylnh0ylo!g0ylp0ymfh0ymg!g0ymh0yn7h0yn8!g0yn90ynzh0yo0!g0yo10yorh0yos!g0yot0ypjh0ypk!g0ypl0yqbh0yqc!g0yqd0yr3h0yr4!g0yr50yrvh0yrw!g0yrx0ysnh0yso!g0ysp0ytfh0ytg!g0yth0yu7h0yu8!g0yu90yuzh0yv0!g0yv10yvrh0yvs!g0yvt0ywjh0ywk!g0ywl0yxbh0yxc!g0yxd0yy3h0yy4!g0yy50yyvh0yyw!g0yyx0yznh0yzo!g0yzp0z0fh0z0g!g0z0h0z17h0z18!g0z190z1zh0z20!g0z210z2rh0z2s!g0z2t0z3jh0z3k!g0z3l0z4bh0z4c!g0z4d0z53h0z54!g0z550z5vh0z5w!g0z5x0z6nh0z6o!g0z6p0z7fh0z7g!g0z7h0z87h0z88!g0z890z8zh0z90!g0z910z9rh0z9s!g0z9t0zajh0zak!g0zal0zbbh0zbc!g0zbd0zc3h0zc4!g0zc50zcvh0zcw!g0zcx0zdnh0zdo!g0zdp0zefh0zeg!g0zeh0zf7h0zf8!g0zf90zfzh0zg0!g0zg10zgrh0zgs!g0zgt0zhjh0zhk!g0zhl0zibh0zic!g0zid0zj3h0zj4!g0zj50zjvh0zjw!g0zjx0zknh0zko!g0zkp0zlfh0zlg!g0zlh0zm7h0zm8!g0zm90zmzh0zn0!g0zn10znrh0zns!g0znt0zojh0zok!g0zol0zpbh0zpc!g0zpd0zq3h0zq4!g0zq50zqvh0zqw!g0zqx0zrnh0zro!g0zrp0zsfh0zsg!g0zsh0zt7h0zt8!g0zt90ztzh0zu0!g0zu10zurh0zus!g0zut0zvjh0zvk!g0zvl0zwbh0zwc!g0zwd0zx3h0zx4!g0zx50zxvh0zxw!g0zxx0zynh0zyo!g0zyp0zzfh0zzg!g0zzh1007h1008!g1009100zh1010!g1011101rh101s!g101t102jh102k!g102l103bh103c!g103d1043h1044!g1045104vh104w!g104x105nh105o!g105p106fh106g!g106h1077h1078!g1079107zh1080!g1081108rh108s!g108t109jh109k!g109l10abh10ac!g10ad10b3h10b4!g10b510bvh10bw!g10bx10cnh10co!g10cp10dfh10dg!g10dh10e7h10e8!g10e910ezh10f0!g10f110frh10fs!g10ft10gjh10gk!g10gl10hbh10hc!g10hd10i3h10i4!g10i510ivh10iw!g10ix10jnh10jo!g10jp10kfh10kg!g10kh10l7h10l8!g10l910lzh10m0!g10m110mrh10ms!g10mt10njh10nk!g10nl10obh10oc!g10od10p3h10p4!g10p510pvh10pw!g10px10qnh10qo!g10qp10rfh10rg!g10rh10s7h10s8!g10s910szh10t0!g10t110trh10ts!g10tt10ujh10uk!g10ul10vbh10vc!g10vd10w3h10w4!g10w510wvh10ww!g10wx10xnh10xo!g10xp10yfh10yg!g10yh10z7h10z8!g10z910zzh1100!g1101110rh110s!g110t111jh111k!g111l112bh112c!g112d1133h1134!g1135113vh113w!g113x114nh114o!g114p115fh115g!g115h1167h1168!g1169116zh1170!g1171117rh117s!g117t118jh118k!g118l119bh119c!g119d11a3h11a4!g11a511avh11aw!g11ax11bnh11bo!g11bp11cfh11cg!g11ch11d7h11d8!g11d911dzh11e0!g11e111erh11es!g11et11fjh11fk!g11fl11gbh11gc!g11gd11h3h11h4!g11h511hvh11hw!g11hx11inh11io!g11ip11jfh11jg!g11jh11k7h11k8!g11k911kzh11l0!g11l111lrh11ls!g11lt11mjh11mk!g11ml11nbh11nc!g11nd11o3h11o4!g11o511ovh11ow!g11ox11pnh11po!g11pp11qfh11qg!g11qh11r7h11r8!g11r911rzh11s0!g11s111srh11ss!g11st11tjh11tk!g11tl11ubh11uc!g11ud11v3h11v4!g11v511vvh11vw!g11vx11wnh11wo!g11wp11xfh11xg!g11xh11y7h11y8!g11y911yzh11z0!g11z111zrh11zs!g11zt120jh120k!g120l121bh121c!g121d1223h1224!g1225122vh122w!g122x123nh123o!g123p124fh124g!g124h1257h1258!g1259125zh1260!g1261126rh126s!g126t127jh127k!g127l128bh128c!g128d1293h1294!g1295129vh129w!g129x12anh12ao!g12ap12bfh12bg!g12bh12c7h12c8!g12c912czh12d0!g12d112drh12ds!g12dt12ejh12ek!g12el12fbh12fc!g12fd12g3h12g4!g12g512gvh12gw!g12gx12hnh12ho!g12hp12ifh12ig!g12ih12j7h12j8!g12j912jzh12k0!g12k112krh12ks!g12kt12ljh12lk!g12ll12mbh12mc!g12md12n3h12n4!g12n512nvh12nw!g12nx12onh12oo!g12op12pfh12pg!g12ph12q7h12q8!g12q912qzh12r0!g12r112rrh12rs!g12rt12sjh12sk!g12sl12tbh12tc!g12td12u3h12u4!g12u512uvh12uw!g12ux12vnh12vo!g12vp12wfh12wg!g12wh12x7h12x8!g12x912xzh12y0!g12y112yrh12ys!g12yt12zjh12zk!g12zl130bh130c!g130d1313h1314!g1315131vh131w!g131x132nh132o!g132p133fh133g!g133h1347h1348!g1349134zh1350!g1351135rh135s!g135t136jh136k!g136l137bh137c!g137d1383h1384!g1385138vh138w!g138x139nh139o!g139p13afh13ag!g13ah13b7h13b8!g13b913bzh13c0!g13c113crh13cs!g13ct13djh13dk!g13dl13ebh13ec!g13ed13f3h13f4!g13f513fvh13fw!g13fx13gnh13go!g13gp13hfh13hg!g13hh13i7h13i8!g13i913izh13j0!g13j113jrh13js!g13jt13kjh13kk!g13kl13lbh13lc!g13ld13m3h13m4!g13m513mvh13mw!g13mx13nnh13no!g13np13ofh13og!g13oh13p7h13p8!g13p913pzh13q0!g13q113qrh13qs!g13qt13rjh13rk!g13rl13sbh13sc!g13sd13t3h13t4!g13t513tvh13tw!g13tx13unh13uo!g13up13vfh13vg!g13vh13w7h13w8!g13w913wzh13x0!g13x113xrh13xs!g13xt13yjh13yk!g13yl13zbh13zc!g13zd1403h1404!g1405140vh140w!g140x141nh141o!g141p142fh142g!g142h1437h1438!g1439143zh1440!g1441144rh144s!g144t145jh145k!g145l146bh146c!g146d1473h1474!g1475147vh147w!g147x148nh148o!g148p149fh149g!g149h14a7h14a8!g14a914azh14b0!g14b114brh14bs!g14bt14cjh14ck!g14cl14dbh14dc!g14dd14e3h14e4!g14e514evh14ew!g14ex14fnh14fo!g14fp14gfh14gg!g14gh14h7h14h8!g14h914hzh14i0!g14i114irh14is!g14it14jjh14jk!g14jl14kbh14kc!g14kd14l3h14l4!g14l514lvh14lw!g14lx14mnh14mo!g14mp14nfh14ng!g14nh14o7h14o8!g14o914ozh14p0!g14p114prh14ps!g14pt14qjh14qk!g14ql14rbh14rc!g14rd14s3h14s4!g14s514svh14sw!g14sx14tnh14to!g14tp14ufh14ug!g14uh14v7h14v8!g14v914vzh14w0!g14w114wrh14ws!g14wt14xjh14xk!g14xl14ybh14yc!g14yd14z3h14z4!g14z514zvh14zw!g14zx150nh150o!g150p151fh151g!g151h1527h1528!g1529152zh1530!g1531153rh153s!g153t154jh154k!g154l155bh155c!g155d1563h1564!g1565156vh156w!g156x157nh157o!g157p158fh158g!g158h1597h1598!g1599159zh15a0!g15a115arh15as!g15at15bjh15bk!g15bl15cbh15cc!g15cd15d3h15d4!g15d515dvh15dw!g15dx15enh15eo!g15ep15ffh15fg!g15fh15g7h15g8!g15g915gzh15h0!g15h115hrh15hs!g15ht15ijh15ik!g15il15jbh15jc!g15jd15k3h15k4!g15k515kvh15kw!g15kx15lnh15lo!g15lp15mfh15mg!g15mh15n7h15n8!g15n915nzh15o0!g15o115orh15os!g15ot15pjh15pk!g15pl15qbh15qc!g15qd15r3h15r4!g15r515rvh15rw!g15rx15snh15so!g15sp15tfh15tg!g15th15u7h15u8!g15u915uzh15v0!g15v115vrh15vs!g15vt15wjh15wk!g15wl15xbh15xc!g15xd15y3h15y4!g15y515yvh15yw!g15yx15znh15zo!g15zp160fh160g!g160h1617h1618!g1619161zh1620!g1621162rh162s!g162t163jh163k!g163l164bh164c!g164d1653h1654!g1655165vh165w!g165x166nh166o!g166p167fh167g!g167h1687h1688!g1689168zh1690!g1691169rh169s!g169t16ajh16ak!g16al16bbh16bc!g16bd16c3h16c4!g16c516cvh16cw!g16cx16dnh16do!g16dp16efh16eg!g16eh16f7h16f8!g16f916fzh16g0!g16g116grh16gs!g16gt16hjh16hk!g16hl16ibh16ic!g16id16j3h16j4!g16j516jvh16jw!g16jx16knh16ko!g16kp16lfh16ls16meW16mj16nvX16o01d6nI1d6o1dkve1dkw1dljI1dlp!U1dlq!A1dlr1dm0U1dm1!I1dm21dmeU1dmg1dmkU1dmm!U1dmo1dmpU1dmr1dmsU1dmu1dn3U1dn41e0tI1e0u!R1e0v!L1e1c1e63I1e64!K1e65!I1e681e6nA1e6o!N1e6p1e6qR1e6r1e6sN1e6t1e6uG1e6v!L1e6w!R1e6x!c1e741e7jA1e7k1e7oe1e7p!L1e7q!R1e7r!L1e7s!R1e7t!L1e7u!R1e7v!L1e7w!R1e7x!L1e7y!R1e7z!L1e80!R1e81!L1e82!R1e83!L1e84!R1e851e86e1e87!L1e88!R1e891e8fe1e8g!R1e8h!e1e8i!R1e8k1e8lY1e8m1e8nG1e8o!e1e8p!L1e8q!R1e8r!L1e8s!R1e8t!L1e8u!R1e8v1e92e1e94!e1e95!J1e96!K1e97!e1e9c1ed8I1edb!d1edd!G1ede1edfe1edg!J1edh!K1edi1edje1edk!L1edl!R1edm1edne1edo!R1edp!e1edq!R1edr1ee1e1ee21ee3Y1ee41ee6e1ee7!G1ee81eeye1eez!L1ef0!e1ef1!R1ef21efue1efv!L1efw!e1efx!R1efy!e1efz!L1eg01eg1R1eg2!L1eg31eg4R1eg5!Y1eg6!e1eg71eggY1egh1ehpe1ehq1ehrY1ehs1eime1eiq1eive1eiy1ej3e1ej61ejbe1eje1ejge1ejk!K1ejl!J1ejm1ejoe1ejp1ejqJ1ejs1ejyI1ek91ekbA1ekc!i1ekd1ereI1erk1ermB1err1eykI1eyl!A1f281f4gI1f4w!A1f4x1f91I1f921f96A1f9c1fa5I1fa7!B1fa81fbjI1fbk!B1fbl1fh9I1fhc1fhlQ1fhs1g7pI1g7r!B1g7s1gd7I1gdb!B1gdc1gjkI1gjl1gjnA1gjp1gjqA1gjw1gjzA1gk01gl1I1gl41gl6A1glb!A1glc1glkI1gls1glzB1gm01gpwI1gpx1gpyA1gq31gq7I1gq81gqdB1gqe!c1gqo1gs5I1gs91gsfB1gsg1h5vI1h5w1h5zA1h681h6hQ1heo1hgpI1hgr1hgsA1hgt!B1hgw1hl1I1hl21hlcA1hld1hpyI1hq81hqaA1hqb1hrrI1hrs1hs6A1hs71hs8B1hs91ht1I1ht21htbQ1htr1htuA1htv1hv3I1hv41hveA1hvf1hvhI1hvi1hvlB1hvx1hwoI1hww1hx5Q1hxc1hxeA1hxf1hyeI1hyf1hysA1hyu1hz3Q1hz41hz7B1hz8!I1hz91hzaA1hzb1i0iI1i0j!A1i0k!I1i0l!T1i0m!I1i0w1i0yA1i0z1i2aI1i2b1i2oA1i2p1i2sI1i2t1i2uB1i2v!I1i2w!B1i2x1i30A1i31!I1i321i33A1i341i3dQ1i3e!I1i3f!T1i3g!I1i3h1i3jB1i3l1i5nI1i5o1i5zA1i601i61B1i62!I1i631i64B1i65!I1i66!A1i801i94I1i95!B1i9c1iamI1ian1iayA1ib41ibdQ1ibk1ibnA1ibp1id5I1id71id8A1id9!I1ida1idgA1idj1idkA1idn1idpA1ids!I1idz!A1ie51ie9I1iea1iebA1iee1iekA1ieo1iesA1iio1ik4I1ik51ikmA1ikn1ikqI1ikr1ikuB1ikv!I1ikw1il5Q1il61il7B1il9!I1ila!A1ilb1injI1ink1io3A1io41io7I1iog1iopQ1itc1iumI1iun1iutA1iuw1iv4A1iv5!T1iv61iv7B1iv81iv9G1iva1ivcI1ivd1ivrB1ivs1ivvI1ivw1ivxA1iww1iy7I1iy81iyoA1iyp1iyqB1iyr1iysI1iz41izdQ1izk1izwT1j0g1j1mI1j1n1j1zA1j20!I1j281j2hQ1j401j57I1j5c1j5lQ1j5m1j5nI1j5o1j5qB1j5r1jcbI1jcc1jcqA1jcr1jhbI1jhc1jhlQ1jhm1jjjI1jjk1jjpA1jjr1jjsA1jjv1jjyA1jjz!I1jk0!A1jk1!I1jk21jk3A1jk41jk6B1jkg1jkpQ1jmo1jo0I1jo11jo7A1joa1jogA1joh!I1joi!T1joj!I1jok!A1jpc!I1jpd1jpmA1jpn1jqqI1jqr1jqxA1jqy!I1jqz1jr2A1jr3!T1jr4!I1jr51jr8B1jr9!T1jra!I1jrb!A1jrk!I1jrl1jrvA1jrw1jt5I1jt61jtlA1jtm1jtoB1jtp!I1jtq1jtsT1jtt1jtuB1juo1k4uI1k4v1k52A1k541k5bA1k5c!I1k5d1k5hB1k5s1k61Q1k621k6kI1k6o!T1k6p!G1k6q1k7jI1k7m1k87A1k891k8mA1kao1kc0I1kc11kc6A1kca!A1kcc1kcdA1kcf1kclA1kcm!I1kcn!A1kcw1kd5Q1kdc1kehI1kei1kemA1keo1kepA1ker1kevA1kew!I1kf41kfdQ1ko01koiI1koj1komA1kon1kv0I1kv11kv4K1kv51kvlI1kvz!B1kw01lriI1lrk1lroB1ls01oifI1oig1oiiL1oij1oilR1oim1ojlI1ojm!R1ojn1ojpI1ojq!L1ojr!R1ojs!L1ojt!R1oju1oqgI1oqh!L1oqi1oqjR1oqk1oviI1ovk1ovqS1ovr!L1ovs!R1s001sctI1scu!L1scv!R1scw1zkuI1zkw1zl5Q1zla1zlbB1zo01zotI1zow1zp0A1zp1!B1zpc1zqnI1zqo1zquA1zqv1zqxB1zqy1zr7I1zr8!B1zr9!I1zrk1zrtQ1zrv20euI20ev20ewB20ex20juI20jz!A20k0!I20k120ljA20lr20luA20lv20m7I20o020o3Y20o4!S20og20ohA20ow25fbe25fk260ve260w26dxI26f426fce2dc02djye2dlc2dleY2dlw2dlzY2dm82dx7e2fpc2ftoI2ftp2ftqA2ftr!B2fts2ftvA2jnk2jxgI2jxh2jxlA2jxm2jxoI2jxp2jyaA2jyb2jycI2jyd2jyjA2jyk2jzdI2jze2jzhA2jzi2k3lI2k3m2k3oA2k3p2l6zI2l722l8fQ2l8g2lmnI2lmo2lo6A2lo72loaI2lob2lpoA2lpp2lpwI2lpx!A2lpy2lqbI2lqc!A2lqd2lqeI2lqf2lqiB2lqj!I2lqz2lr3A2lr52lrjA2mtc2mtiA2mtk2mu0A2mu32mu9A2mub2mucA2mue2muiA2n0g2n1oI2n1s2n1yA2n1z2n25I2n282n2hQ2n2m2ne3I2ne42ne7A2ne82nehQ2nen!J2oe82ojzI2ok02ok6A2olc2on7I2on82oneA2onf!I2onk2ontQ2ony2onzL2p9t2pbfI2pbg!K2pbh2pbjI2pbk!K2pbl2prlI2pz42q67e2q682q6kI2q6l2q6ne2q6o2q98I2q992q9be2q9c2qb0I2qb12qcle2qcm2qdbj2qdc2qo4e2qo5!f2qo62qore2qos2qotI2qou2qpge2qph2qpiI2qpj2qpne2qpo!I2qpp2qpte2qpu2qpwf2qpx2qpye2qpz!f2qq02qq1e2qq22qq4f2qq52qree2qrf2qrjk2qrk2qtde2qte2qtff2qtg2qthe2qti2qtsf2qtt2qude2que2quwf2qux2quze2qv0!f2qv12qv4e2qv52qv7f2qv8!e2qv92qvbf2qvc2qvie2qvj!f2qvk!e2qvl!f2qvm2qvze2qw0!I2qw1!e2qw2!I2qw3!e2qw4!I2qw52qw9e2qwa!f2qwb2qwee2qwf!I2qwg!e2qwh2qwiI2qwj2qyne2qyo2qyuI2qyv2qzae2qzb2qzoI2qzp2r01e2r022r0pI2r0q2r1ve2r1w2r1xf2r1y2r21e2r22!f2r232r2ne2r2o!f2r2p2r2se2r2t2r2uf2r2v2r4je2r4k2r4rI2r4s2r5fe2r5g2r5lI2r5m2r7oe2r7p2r7rf2r7s2r7ue2r7v2r7zf2r802r91I2r922r94H2r952r97Y2r982r9bI2r9c2raae2rab!f2rac2rare2ras2rauf2rav2rb3e2rb4!f2rb52rbfe2rbg!f2rbh2rcve2rcw2rg3I2rg42rgfe2rgg2risI2rit2rjze2rk02rkbI2rkc2rkfe2rkg2rlzI2rm02rm7e2rm82rmhI2rmi2rmne2rmo2rnrI2rns2rnze2ro02rotI2rou2rr3e2rr42rrfI2rrg!f2rrh2rrie2rrj!f2rrk2rrre2rrs2rrzf2rs02rs5e2rs6!f2rs72rsfe2rsg2rspf2rsq2rsre2rss2rsuf2rsv2ruee2ruf!f2rug2rw4e2rw52rw6f2rw7!e2rw82rw9f2rwa!e2rwb!f2rwc2rwse2rwt2rwvf2rww!e2rwx2rx9f2rxa2ry7e2ry82s0jI2s0k2s5be2s5c2sayI2sc02sc9Q2scg2t4te2t4w47p9e47pc5m9pejny9!Ajnz4jo1rAjo5cjobzAl2ionvnhI'; -UnicodePropertyLookup lineLookup = +UnicodePropertyLookup get lineLookup => ensureLineLookupInitialized(); + +/// Initializes [lineLookup], if it's not already initialized. +/// +/// Use this function to trigger the initialization before [lineLookup] is +/// actually used. For example, triggering it before the first application +/// frame is rendered will reduce jank by moving the initialization out of +/// the frame. +UnicodePropertyLookup ensureLineLookupInitialized() { + return _lineLookup ??= UnicodePropertyLookup.fromPackedData( - _packedLineBreakProperties, - 937, - LineCharProperty.values, - LineCharProperty.AL, -); + _packedLineBreakProperties, + 937, + LineCharProperty.values, + LineCharProperty.AL, + ); +} + +UnicodePropertyLookup? _lineLookup; diff --git a/lib/web_ui/lib/src/engine/text/line_breaker.dart b/lib/web_ui/lib/src/engine/text/line_breaker.dart index 50970980bf518..11ff7e963109a 100644 --- a/lib/web_ui/lib/src/engine/text/line_breaker.dart +++ b/lib/web_ui/lib/src/engine/text/line_breaker.dart @@ -2,14 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'package:ui/ui.dart' as ui; + +import '../util.dart'; +import 'line_break_properties.dart'; +import 'unicode_range.dart'; /// Various types of line breaks as defined by the Unicode spec. enum LineBreakType { /// Indicates that a line break is possible but not mandatory. opportunity, + /// Indicates that a line break isn't possible. + prohibited, + /// Indicates that this is a hard line break that can't be skipped. mandatory, @@ -74,6 +80,9 @@ class LineBreakResult { /// to decide whether to take the line break or not. final LineBreakType type; + bool get isHard => + type == LineBreakType.mandatory || type == LineBreakType.endOfText; + @override int get hashCode => ui.hashValues( index, @@ -160,7 +169,7 @@ bool _hasEastAsianWidthFWH(int charCode) { /// /// * https://www.unicode.org/reports/tr14/tr14-45.html#Algorithm /// * https://www.unicode.org/Public/11.0.0/ucd/LineBreak.txt -LineBreakResult nextLineBreak(String text, int index) { +LineBreakResult nextLineBreak(String text, int index, {int? maxEnd}) { int? codePoint = getCodePoint(text, index); LineCharProperty curr = lineLookup.findForChar(codePoint); @@ -180,7 +189,7 @@ LineBreakResult nextLineBreak(String text, int index) { /// The index of the last character that wasn't a new line. int lastNonNewlineIndex = index; - // When the text/line starts with SP, we should treat the begining of text/line + // When the text/line starts with SP, we should treat the beginning of text/line // as if it were a WJ (word joiner). if (curr == LineCharProperty.SP) { baseOfSpaceSequence = LineCharProperty.WJ; @@ -199,6 +208,15 @@ LineBreakResult nextLineBreak(String text, int index) { // Always break at the end of text. // LB3: ! eot while (index < text.length) { + if (index == maxEnd) { + return LineBreakResult( + index, + lastNonNewlineIndex, + lastNonSpaceIndex, + LineBreakType.prohibited, + ); + } + // Keep count of the RI (regional indicator) sequence. if (curr == LineCharProperty.RI) { regionalIndicatorCount++; diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index 416f045603b07..4b1053350f649 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import '../../engine.dart' show registerHotRestartListener; +import '../dom_renderer.dart'; // TODO(yjbanov): this is a hack we use to compute ideographic baseline; this // number is the ratio ideographic/alphabetic for font Ahem, @@ -11,24 +13,13 @@ part of engine; // for any other font. We'll need to eventually fix this. That // said Flutter doesn't seem to use ideographic baseline for // anything as of this writing. -const double _baselineRatioHack = 1.1662499904632568; - -/// Signature of a function that takes a character and returns true or false. -typedef CharPredicate = bool Function(int char); - -bool _newlinePredicate(int char) { - final LineCharProperty prop = lineLookup.findForChar(char); - return prop == LineCharProperty.BK || - prop == LineCharProperty.LF || - prop == LineCharProperty.CR; -} +const double baselineRatioHack = 1.1662499904632568; -/// Manages [ParagraphRuler] instances and caches them per unique -/// [ParagraphGeometricStyle]. +/// Hosts ruler DOM elements in a hidden container under a `root` [html.Node]. /// -/// All instances of [ParagraphRuler] should be created through this class. -class RulerManager { - RulerManager({required this.rulerCacheCapacity}) { +/// The `root` [html.Node] is optional. Defaults to [domRenderer.glassPaneShadow]. +class RulerHost { + RulerHost({html.Node? root}) { _rulerHost.style ..position = 'fixed' ..visibility = 'hidden' @@ -37,12 +28,11 @@ class RulerManager { ..left = '0' ..width = '0' ..height = '0'; - html.document.body!.append(_rulerHost); + + (root ?? domRenderer.glassPaneShadow!.node).append(_rulerHost); registerHotRestartListener(dispose); } - final int rulerCacheCapacity; - /// Hosts a cache of rulers that measure text. /// /// This element exists purely for organizational purposes. Otherwise the @@ -51,604 +41,27 @@ class RulerManager { /// purpose. final html.Element _rulerHost = html.Element.tag('flt-ruler-host'); - /// The cache of rulers used to measure text. - /// - /// Each ruler is keyed by paragraph style. This allows us to setup the - /// ruler's DOM structure once during the very first measurement of a given - /// paragraph style. Subsequent measurements could reuse the same ruler and - /// only swap the text contents. This minimizes the amount of work a browser - /// needs to do when measure many pieces of text with the same style. - /// - /// What makes this cache effective is the fact that a typical application - /// only uses a limited number of text styles. Using too many text styles on - /// the same screen is considered bad for user experience. - Map get rulers => _rulers; - Map _rulers = - {}; - - bool _rulerCacheCleanupScheduled = false; - - void _scheduleRulerCacheCleanup() { - if (!_rulerCacheCleanupScheduled) { - _rulerCacheCleanupScheduled = true; - scheduleMicrotask(() { - _rulerCacheCleanupScheduled = false; - cleanUpRulerCache(); - }); - } - } - - /// Releases the resources used by this [RulerManager]. + /// Releases the resources used by this [RulerHost]. /// /// After this is called, this object is no longer usable. void dispose() { _rulerHost.remove(); } - // Evicts all rulers from the cache. - void _evictAllRulers() { - _rulers.forEach((ParagraphGeometricStyle style, ParagraphRuler ruler) { - ruler.dispose(); - }); - _rulers = {}; - } - - /// If [window._isPhysicalSizeActuallyEmpty], evicts all rulers from the cache. - /// If ruler cache size exceeds [rulerCacheCapacity], evicts those rulers that - /// were used the least. - /// - /// Resets hit counts back to zero. - @visibleForTesting - void cleanUpRulerCache() { - // Measurements performed (and cached) inside a hidden iframe (with - // display:none) are wrong. - // Evict all rulers, so text gets re-measured when the iframe becomes - // visible. - // see: https://github.com/flutter/flutter/issues/36341 - if (window.physicalSize.isEmpty) { - _evictAllRulers(); - return; - } - if (_rulers.length > rulerCacheCapacity) { - final List sortedByUsage = _rulers.values.toList(); - sortedByUsage.sort((ParagraphRuler a, ParagraphRuler b) { - return b.hitCount - a.hitCount; - }); - _rulers = {}; - for (int i = 0; i < sortedByUsage.length; i++) { - final ParagraphRuler ruler = sortedByUsage[i]; - ruler.resetHitCount(); - if (i < rulerCacheCapacity) { - // Retain this ruler. - _rulers[ruler.style] = ruler; - } else { - // This ruler did not have enough usage this frame to be retained. - ruler.dispose(); - } - } - } - } - /// Adds an element used for measuring text as a child of [_rulerHost]. - void addHostElement(html.DivElement element) { + void addElement(html.HtmlElement element) { _rulerHost.append(element); } - - /// Performs a cache lookup to find an existing [ParagraphRuler] for the given - /// [style] and if it can't find one in the cache, it would create one. - /// - /// The returned ruler is marked as hit so there's no need to do that - /// elsewhere. - @visibleForTesting - ParagraphRuler findOrCreateRuler(ParagraphGeometricStyle style) { - ParagraphRuler? ruler = _rulers[style]; - if (ruler == null) { - if (assertionsEnabled) { - domRenderer.debugRulerCacheMiss(); - } - ruler = _rulers[style] = ParagraphRuler(style, this); - _scheduleRulerCacheCleanup(); - } else { - if (assertionsEnabled) { - domRenderer.debugRulerCacheHit(); - } - } - ruler.hit(); - return ruler; - } } -/// Provides various text measurement APIs using either a dom-based approach -/// in [DomTextMeasurementService], or a canvas-based approach in -/// [CanvasTextMeasurementService]. -abstract class TextMeasurementService { - /// Whether this service uses a canvas to make the text measurements. - /// - /// If [isCanvas] is false, it indicates that this service uses DOM elements - /// to make the text measurements. - bool get isCanvas; - - /// Initializes the text measurement service with a specific - /// [rulerCacheCapacity] that gets passed to the [RulerManager]. - static void initialize({required int rulerCacheCapacity}) { - rulerManager?.dispose(); - rulerManager = null; - rulerManager = RulerManager(rulerCacheCapacity: rulerCacheCapacity); - } - - @visibleForTesting - static RulerManager? rulerManager; - - /// The DOM-based text measurement service. - @visibleForTesting - static TextMeasurementService get domInstance => - DomTextMeasurementService.instance; - - /// The canvas-based text measurement service. - @visibleForTesting - static TextMeasurementService get canvasInstance => - CanvasTextMeasurementService.instance; - - /// Gets the appropriate [TextMeasurementService] instance for the given - /// [paragraph]. - static TextMeasurementService forParagraph(ui.Paragraph paragraph) { - // TODO(flutter_web): https://github.com/flutter/flutter/issues/33523 - // When the canvas-based implementation is complete and passes all the - // tests, get rid of [_experimentalEnableCanvasImplementation]. - // We need to check [window.physicalSize.isEmpty] because some canvas - // commands don't work as expected when they run inside a hidden iframe - // (with display:none) - // Skip using canvas measurements until the iframe becomes visible. - // see: https://github.com/flutter/flutter/issues/36341 - if (!window.physicalSize.isEmpty && - WebExperiments.instance!.useCanvasText && - _canUseCanvasMeasurement(paragraph as EngineParagraph)) { - return canvasInstance; - } - return domInstance; - } - - /// Clears the cache of paragraph rulers that are used for measuring paragraph - /// metrics. - static void clearCache() { - rulerManager?._evictAllRulers(); - } - - static bool _canUseCanvasMeasurement(EngineParagraph paragraph) { - // Currently, the canvas-based approach only works on plain text that - // doesn't have any of the following styles: - // - decoration - // - word spacing - final ParagraphGeometricStyle style = paragraph._geometricStyle; - return paragraph._plainText != null && - style.decoration == null && - style.wordSpacing == null; - } - - /// Measures the paragraph and returns a [MeasurementResult] object. - MeasurementResult? measure( - EngineParagraph paragraph, - ui.ParagraphConstraints constraints, - ) { - assert(rulerManager != null); - final ParagraphGeometricStyle style = paragraph._geometricStyle; - final ParagraphRuler ruler = - TextMeasurementService.rulerManager!.findOrCreateRuler(style); - - if (assertionsEnabled) { - if (paragraph._plainText == null) { - domRenderer.debugRichTextLayout(); - } else { - domRenderer.debugPlainTextLayout(); - } - } - - MeasurementResult? result = ruler.cacheLookup(paragraph, constraints); - if (result != null) { - return result; - } - - result = _doMeasure(paragraph, constraints, ruler); - ruler.cacheMeasurement(paragraph, result); - return result; - } - - /// Measures the width of a substring of the given [paragraph] with no - /// constraints. - double measureSubstringWidth(EngineParagraph paragraph, int start, int end); - - /// Returns text position given a paragraph, constraints and offset. - ui.TextPosition getTextPositionForOffset(EngineParagraph paragraph, - ui.ParagraphConstraints? constraints, ui.Offset offset); - - /// Delegates to a [ParagraphRuler] to measure a list of text boxes that - /// enclose the given range of text. - List measureBoxesForRange( - EngineParagraph paragraph, - ui.ParagraphConstraints constraints, { - required int start, - required int end, - required double alignOffset, - required ui.TextDirection textDirection, - }) { - final ParagraphGeometricStyle style = paragraph._geometricStyle; - final ParagraphRuler ruler = - TextMeasurementService.rulerManager!.findOrCreateRuler(style); - - return ruler.measureBoxesForRange( - paragraph._plainText!, - constraints, - start: start, - end: end, - alignOffset: alignOffset, - textDirection: textDirection, - ); - } - - /// Performs the actual measurement of the following values for the given - /// paragraph: - /// - /// * isSingleLine: whether the paragraph can be rendered in a single line. - /// * height: constrained measure of the entire paragraph's height. - /// * lineHeight: the height of a single line of the paragraph. - /// * alphabeticBaseline: single line measure. - /// * ideographicBaseline: based on [alphabeticBaseline]. - /// * maxIntrinsicWidth: the width of the paragraph with no line-wrapping. - /// * minIntrinsicWidth: the min width the paragraph fits in without overflowing. - /// - /// [MeasurementResult.width] is set to the same value of [constraints.width]. - /// - /// It also optionally computes [MeasurementResult.lines] in the given - /// paragraph. When that's available, it can be used by a canvas to render - /// the text line. - MeasurementResult _doMeasure( - EngineParagraph paragraph, - ui.ParagraphConstraints constraints, - ParagraphRuler ruler, - ); -} - -/// A DOM-based text measurement implementation. -/// -/// This implementation is slower than [CanvasTextMeasurementService] but it's -/// needed for some cases that aren't yet supported in the canvas-based -/// implementation such as letter-spacing, word-spacing, etc. -class DomTextMeasurementService extends TextMeasurementService { - @override - final bool isCanvas = false; - - /// The text measurement service singleton. - static DomTextMeasurementService get instance => - _instance ??= DomTextMeasurementService(); - - static DomTextMeasurementService? _instance; - - @override - MeasurementResult _doMeasure( - EngineParagraph paragraph, - ui.ParagraphConstraints constraints, - ParagraphRuler ruler, - ) { - ruler.willMeasure(paragraph); - final String? plainText = paragraph._plainText; - - ruler.measureAll(constraints); - - MeasurementResult result; - // When the text has a new line, we should always use multi-line mode. - final bool hasNewline = plainText?.contains('\n') ?? false; - if (!hasNewline && ruler.singleLineDimensions.width <= constraints.width) { - result = _measureSingleLineParagraph(ruler, paragraph, constraints); - } else { - // Assert: If text doesn't have new line for infinite constraints we - // should have called single line measure paragraph instead. - assert(hasNewline || constraints.width != double.infinity); - result = _measureMultiLineParagraph(ruler, paragraph, constraints); - } - ruler.didMeasure(); - return result; - } - - @override - double measureSubstringWidth(EngineParagraph paragraph, int start, int end) { - assert(paragraph._plainText != null); - final ParagraphGeometricStyle style = paragraph._geometricStyle; - final ParagraphRuler ruler = - TextMeasurementService.rulerManager!.findOrCreateRuler(style); - - final String text = paragraph._plainText!.substring(start, end); - final ui.Paragraph substringParagraph = paragraph._cloneWithText(text); - - ruler.willMeasure(substringParagraph as EngineParagraph); - ruler.measureAsSingleLine(); - final TextDimensions dimensions = ruler.singleLineDimensions; - ruler.didMeasure(); - return dimensions.width; - } - - @override - ui.TextPosition getTextPositionForOffset(EngineParagraph paragraph, - ui.ParagraphConstraints? constraints, ui.Offset offset) { - assert( - paragraph._measurementResult!.lines == null, - 'should only be called when the faster lines-based approach is not possible', - ); - - final ParagraphGeometricStyle style = paragraph._geometricStyle; - final ParagraphRuler ruler = - TextMeasurementService.rulerManager!.findOrCreateRuler(style); - ruler.willMeasure(paragraph); - final int position = ruler.hitTest(constraints!, offset); - ruler.didMeasure(); - return ui.TextPosition(offset: position); - } - - /// Called when we have determined that the paragraph fits the [constraints] - /// without wrapping. - /// - /// This means that: - /// * `width == maxIntrinsicWidth` - we gave it more horizontal space than - /// it needs and so the paragraph won't expand beyond `maxIntrinsicWidth`. - /// * `height` is the height computed by `measureAsSingleLine`; giving the - /// paragraph the width constraint won't change its height as we already - /// determined that it fits within the constraint without wrapping. - /// * `alphabeticBaseline` is also final for the same reason as the `height` - /// value. - /// - /// This method still needs to measure `minIntrinsicWidth`. - MeasurementResult _measureSingleLineParagraph( - ParagraphRuler ruler, - EngineParagraph paragraph, - ui.ParagraphConstraints constraints, - ) { - final double width = constraints.width; - final double minIntrinsicWidth = ruler.minIntrinsicDimensions.width; - double maxIntrinsicWidth = ruler.singleLineDimensions.width; - final double alphabeticBaseline = ruler.alphabeticBaseline; - final double height = ruler.singleLineDimensions.height; - - maxIntrinsicWidth = - _applySubPixelRoundingHack(minIntrinsicWidth, maxIntrinsicWidth); - final double ideographicBaseline = alphabeticBaseline * _baselineRatioHack; - - final String? text = paragraph._plainText; - List? lines; - if (text != null) { - final double lineWidth = maxIntrinsicWidth; - final double alignOffset = _calculateAlignOffsetForLine( - paragraph: paragraph, - lineWidth: lineWidth, - maxWidth: width, - ); - lines = [ - EngineLineMetrics.withText( - text, - startIndex: 0, - endIndex: text.length, - endIndexWithoutNewlines: - _excludeTrailing(text, 0, text.length, _newlinePredicate), - hardBreak: true, - width: lineWidth, - widthWithTrailingSpaces: lineWidth, - left: alignOffset, - lineNumber: 0, - ), - ]; - } - - return MeasurementResult( - constraints.width, - isSingleLine: true, - width: width, - height: height, - naturalHeight: height, - lineHeight: height, - minIntrinsicWidth: minIntrinsicWidth, - maxIntrinsicWidth: maxIntrinsicWidth, - alphabeticBaseline: alphabeticBaseline, - ideographicBaseline: ideographicBaseline, - lines: lines, - placeholderBoxes: ruler.measurePlaceholderBoxes(), - textAlign: paragraph._textAlign, - textDirection: paragraph._textDirection, - ); - } - - /// Called when we have determined that the paragraph needs to wrap into - /// multiple lines to fit the [constraints], i.e. its `maxIntrinsicWidth` is - /// bigger than the available horizontal space. - /// - /// While `maxIntrinsicWidth` is still good from the call to - /// `measureAsSingleLine`, we need to re-measure with the width constraint - /// and get new values for width, height and alphabetic baseline. We also need - /// to measure `minIntrinsicWidth`. - MeasurementResult _measureMultiLineParagraph(ParagraphRuler ruler, - EngineParagraph paragraph, ui.ParagraphConstraints constraints) { - // If constraint is infinite, we must use _measureSingleLineParagraph - final double width = constraints.width; - final double minIntrinsicWidth = ruler.minIntrinsicDimensions.width; - double maxIntrinsicWidth = ruler.singleLineDimensions.width; - final double alphabeticBaseline = ruler.alphabeticBaseline; - // Natural height is the full height of text ignoring height constraints. - final double naturalHeight = ruler.constrainedDimensions.height; - - double height; - double? lineHeight; - final int? maxLines = paragraph._geometricStyle.maxLines; - if (maxLines == null) { - height = naturalHeight; - } else { - // Lazily compute [lineHeight] when [maxLines] is not null. - lineHeight = ruler.lineHeightDimensions!.height; - height = math.min(naturalHeight, maxLines * lineHeight); - } - - maxIntrinsicWidth = - _applySubPixelRoundingHack(minIntrinsicWidth, maxIntrinsicWidth); - assert(minIntrinsicWidth <= maxIntrinsicWidth); - final double ideographicBaseline = alphabeticBaseline * _baselineRatioHack; - return MeasurementResult( - constraints.width, - isSingleLine: false, - width: width, - height: height, - lineHeight: lineHeight, - naturalHeight: naturalHeight, - minIntrinsicWidth: minIntrinsicWidth, - maxIntrinsicWidth: maxIntrinsicWidth, - alphabeticBaseline: alphabeticBaseline, - ideographicBaseline: ideographicBaseline, - lines: null, - placeholderBoxes: ruler.measurePlaceholderBoxes(), - textAlign: paragraph._textAlign, - textDirection: paragraph._textDirection, - ); - } - - /// This hack is needed because `offsetWidth` rounds the value to the nearest - /// whole number. On a very rare occasion the minimum intrinsic width reported - /// by the browser is slightly bigger than the reported maximum intrinsic - /// width. If the discrepancy overlaps 0.5 then the rounding happens in - /// opposite directions. - /// - /// For example, if minIntrinsicWidth == 99.5 and maxIntrinsicWidth == 99.48, - /// then minIntrinsicWidth is rounded up to 100, and maxIntrinsicWidth is - /// rounded down to 99. - // TODO(yjbanov): remove the need for this hack. - static double _applySubPixelRoundingHack( - double minIntrinsicWidth, double maxIntrinsicWidth) { - if (minIntrinsicWidth <= maxIntrinsicWidth) { - return maxIntrinsicWidth; - } - - if (minIntrinsicWidth - maxIntrinsicWidth < 2.0) { - return minIntrinsicWidth; - } - - throw Exception('minIntrinsicWidth ($minIntrinsicWidth) is greater than ' - 'maxIntrinsicWidth ($maxIntrinsicWidth).'); - } -} - -/// A canvas-based text measurement implementation. -/// -/// This is a faster implementation than [DomTextMeasurementService] and -/// provides line breaks information that can be useful for multi-line text. -class CanvasTextMeasurementService extends TextMeasurementService { - @override - final bool isCanvas = true; - - /// The text measurement service singleton. - static CanvasTextMeasurementService get instance => - _instance ??= CanvasTextMeasurementService(); - - static CanvasTextMeasurementService? _instance; - - final html.CanvasRenderingContext2D _canvasContext = - html.CanvasElement().context2D; - - @override - MeasurementResult _doMeasure( - EngineParagraph paragraph, - ui.ParagraphConstraints constraints, - ParagraphRuler ruler, - ) { - final String text = paragraph._plainText!; - final ParagraphGeometricStyle style = paragraph._geometricStyle; - assert(text != null); // ignore: unnecessary_null_comparison - - // TODO(mdebbar): Check if the whole text can fit in a single-line. Then avoid all this ceremony. - _canvasContext.font = style.cssFontString; - final LinesCalculator linesCalculator = - LinesCalculator(_canvasContext, paragraph, constraints.width); - final MinIntrinsicCalculator minIntrinsicCalculator = - MinIntrinsicCalculator(_canvasContext, text, style); - final MaxIntrinsicCalculator maxIntrinsicCalculator = - MaxIntrinsicCalculator(_canvasContext, text, style); - - // Indicates whether we've reached the end of text or not. Even if the index - // [i] reaches the end of text, we don't want to stop looping until we hit - // [LineBreakType.endOfText] because there could be a "\n" at the end of the - // string and that would mess things up. - bool reachedEndOfText = false; - - // TODO(flutter_web): Chrome & Safari return more info from [canvasContext.measureText]. - int i = 0; - while (!reachedEndOfText) { - final LineBreakResult brk = nextLineBreak(text, i); - - linesCalculator.update(brk); - minIntrinsicCalculator.update(brk); - maxIntrinsicCalculator.update(brk); - - i = brk.index; - if (brk.type == LineBreakType.endOfText) { - reachedEndOfText = true; - } - } - - final int lineCount = linesCalculator.lines.length; - final double lineHeight = ruler.lineHeightDimensions!.height; - final double naturalHeight = lineCount * lineHeight; - final int? maxLines = style.maxLines; - final double height = maxLines == null - ? naturalHeight - : math.min(lineCount, maxLines) * lineHeight; - - final MeasurementResult result = MeasurementResult( - constraints.width, - isSingleLine: lineCount == 1, - alphabeticBaseline: ruler.alphabeticBaseline, - ideographicBaseline: ruler.alphabeticBaseline * _baselineRatioHack, - height: height, - naturalHeight: naturalHeight, - lineHeight: lineHeight, - // `minIntrinsicWidth` is the greatest width of text that can't - // be broken down into multiple lines. - minIntrinsicWidth: minIntrinsicCalculator.value, - // `maxIntrinsicWidth` is the width of the widest piece of text - // that doesn't contain mandatory line breaks. - maxIntrinsicWidth: maxIntrinsicCalculator.value, - width: constraints.width, - lines: linesCalculator.lines, - placeholderBoxes: [], - textAlign: paragraph._textAlign, - textDirection: paragraph._textDirection, - ); - return result; - } - - @override - double measureSubstringWidth(EngineParagraph paragraph, int start, int end) { - assert(paragraph._plainText != null); - final String text = paragraph._plainText!; - final ParagraphGeometricStyle style = paragraph._geometricStyle; - _canvasContext.font = style.cssFontString; - return _measureSubstring( - _canvasContext, - paragraph._geometricStyle, - text, - start, - end, - ); - } - - @override - ui.TextPosition getTextPositionForOffset(EngineParagraph paragraph, - ui.ParagraphConstraints? constraints, ui.Offset offset) { - // TODO(flutter_web): implement. - return const ui.TextPosition(offset: 0); - } -} - -// These global variables are used to memoize calls to [_measureSubstring]. They +// These global variables are used to memoize calls to [measureSubstring]. They // are used to remember the last arguments passed to it, and the last return // value. // They are being initialized so that the compiler knows they'll never be null. int _lastStart = -1; int _lastEnd = -1; String _lastText = ''; -ParagraphGeometricStyle? _lastStyle; +String _lastCssFont = ''; double _lastWidth = -1; /// Measures the width of the substring of [text] starting from the index @@ -656,13 +69,13 @@ double _lastWidth = -1; /// /// This method assumes that the correct font has already been set on /// [_canvasContext]. -double _measureSubstring( +double measureSubstring( html.CanvasRenderingContext2D _canvasContext, - ParagraphGeometricStyle style, String text, int start, - int end, -) { + int end, { + double? letterSpacing, +}) { assert(0 <= start); assert(start <= end); assert(end <= text.length); @@ -671,353 +84,43 @@ double _measureSubstring( return 0; } + final String cssFont = _canvasContext.font; + double width; + + // TODO(mdebbar): Explore caching all widths in a map, not only the last one. if (start == _lastStart && end == _lastEnd && text == _lastText && - _lastStyle == style) { - // TODO(mdebbar): Explore caching all widths in a map, not only the last one. - return _lastWidth; + cssFont == _lastCssFont) { + // Reuse the previously calculated width if all factors that affect width + // are unchanged. The only exception is letter-spacing. We always add + // letter-spacing to the width later below. + width = _lastWidth; + } else { + final String sub = + start == 0 && end == text.length ? text : text.substring(start, end); + width = _canvasContext.measureText(sub).width!.toDouble(); } + _lastStart = start; _lastEnd = end; _lastText = text; - _lastStyle = style; + _lastCssFont = cssFont; + _lastWidth = width; - final double letterSpacing = style.letterSpacing ?? 0.0; - final String sub = - start == 0 && end == text.length ? text : text.substring(start, end); - final double width = _canvasContext.measureText(sub).width!.toDouble() + - letterSpacing * sub.length; + // Now add letter spacing to the width. + letterSpacing ??= 0.0; + if (letterSpacing != 0.0) { + width += letterSpacing * (end - start); + } // What we are doing here is we are rounding to the nearest 2nd decimal // point. So 39.999423 becomes 40, and 11.243982 becomes 11.24. // The reason we are doing this is because we noticed that canvas API has a // ±0.001 error margin. - return _lastWidth = _roundWidth(width); + return _roundWidth(width); } double _roundWidth(double width) { return (width * 100).round() / 100; } - -/// From the substring defined by [text], [start] (inclusive) and [end] -/// (exclusive), exclude trailing characters that satisfy the given [predicate]. -/// -/// The return value is the new end of the substring after excluding the -/// trailing characters. -int _excludeTrailing(String text, int start, int end, CharPredicate predicate) { - assert(0 <= start); - assert(start <= end); - assert(end <= text.length); - - while (start < end && predicate(text.codeUnitAt(end - 1))) { - end--; - } - return end; -} - -/// During the text layout phase, this class splits the lines of text so that it -/// ends up fitting into the given width constraint. -/// -/// It implements the Flutter engine's behavior when it comes to handling -/// ellipsis and max lines. -class LinesCalculator { - LinesCalculator(this._canvasContext, this._paragraph, this._maxWidth); - - final html.CanvasRenderingContext2D _canvasContext; - final EngineParagraph _paragraph; - final double _maxWidth; - - String? get _text => _paragraph._plainText; - ParagraphGeometricStyle get _style => _paragraph._geometricStyle; - - /// The lines that have been consumed so far. - List lines = []; - - /// The last line break regardless of whether it was optional or mandatory, or - /// whether we took it or not. - LineBreakResult _lastBreak = - const LineBreakResult.sameIndex(0, LineBreakType.mandatory); - - /// The last line break that actually caused a new line to exist. - LineBreakResult _lastTakenBreak = - const LineBreakResult.sameIndex(0, LineBreakType.mandatory); - - int get _lineStart => _lastTakenBreak.index; - int get _chunkStart => _lastBreak.index; - bool _reachedMaxLines = false; - - double? _cachedEllipsisWidth; - double get _ellipsisWidth => _cachedEllipsisWidth ??= - _roundWidth(_canvasContext.measureText(_style.ellipsis!).width as double); - - bool get hasEllipsis => _style.ellipsis != null; - bool get unlimitedLines => _style.maxLines == null; - - /// Consumes the next line break opportunity in [_text]. - /// - /// This method should be called for every line break. As soon as it reaches - /// the maximum number of lines required - void update(LineBreakResult brk) { - final bool isHardBreak = brk.type == LineBreakType.mandatory || - brk.type == LineBreakType.endOfText; - final int chunkEnd = brk.index; - final int chunkEndWithoutNewlines = brk.indexWithoutTrailingNewlines; - final int chunkEndWithoutSpace = brk.indexWithoutTrailingSpaces; - - // A single chunk of text could be force-broken into multiple lines if it - // doesn't fit in a single line. That's why we need a loop. - while (!_reachedMaxLines) { - final double lineWidth = - measureSubstring(_lineStart, chunkEndWithoutSpace); - - // The current chunk doesn't reach the maximum width, so we stop here and - // wait for the next line break. - if (lineWidth <= _maxWidth) { - break; - } - - // If the current chunk starts at the beginning of the line and exceeds - // [maxWidth], then we will need to force-break it. - final bool isChunkTooLong = _chunkStart == _lineStart; - - // When ellipsis is set, and maxLines is null, we stop at the first line - // that exceeds [maxWidth]. - final bool isLastLine = _reachedMaxLines = - (hasEllipsis && unlimitedLines) || - lines.length + 1 == _style.maxLines; - - if (isLastLine && hasEllipsis) { - // When there's an ellipsis, truncate text to leave enough space for - // the ellipsis. - final double availableWidth = _maxWidth - _ellipsisWidth; - final int breakingPoint = forceBreakSubstring( - maxWidth: availableWidth, - start: _lineStart, - end: chunkEndWithoutSpace, - ); - final double widthOfResultingLine = - measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth; - final double alignOffset = _calculateAlignOffsetForLine( - paragraph: _paragraph, - lineWidth: widthOfResultingLine, - maxWidth: _maxWidth, - ); - lines.add(EngineLineMetrics.withText( - _text!.substring(_lineStart, breakingPoint) + _style.ellipsis!, - startIndex: _lineStart, - endIndex: chunkEnd, - endIndexWithoutNewlines: chunkEndWithoutNewlines, - hardBreak: false, - width: widthOfResultingLine, - widthWithTrailingSpaces: widthOfResultingLine, - left: alignOffset, - lineNumber: lines.length, - )); - } else if (isChunkTooLong) { - final int breakingPoint = forceBreakSubstring( - maxWidth: _maxWidth, - start: _lineStart, - end: chunkEndWithoutSpace, - ); - if (breakingPoint == chunkEndWithoutSpace) { - // We couldn't force-break the chunk any further which means we reached - // the last character and there isn't enough space for it to fit in - // its own line. Since this is the last character in the chunk, we - // don't do anything here and we rely on the next iteration (or the - // [isHardBreak] check below) to break the line. - break; - } - _addLineBreak(LineBreakResult.sameIndex( - breakingPoint, - LineBreakType.opportunity, - )); - } else { - // The control case of current line exceeding [_maxWidth], we break the - // line. - _addLineBreak(_lastBreak); - } - } - - if (_reachedMaxLines) { - return; - } - - if (isHardBreak) { - _addLineBreak(brk); - } - _lastBreak = brk; - } - - void _addLineBreak(LineBreakResult brk) { - final int lineNumber = lines.length; - final double lineWidth = - measureSubstring(_lineStart, brk.indexWithoutTrailingSpaces); - final double lineWidthWithTrailingSpaces = - measureSubstring(_lineStart, brk.indexWithoutTrailingNewlines); - final double alignOffset = _calculateAlignOffsetForLine( - paragraph: _paragraph, - lineWidth: lineWidth, - maxWidth: _maxWidth, - ); - final bool isHardBreak = brk.type == LineBreakType.mandatory || - brk.type == LineBreakType.endOfText; - - final EngineLineMetrics metrics = EngineLineMetrics.withText( - _text!.substring(_lineStart, brk.indexWithoutTrailingNewlines), - startIndex: _lineStart, - endIndex: brk.index, - endIndexWithoutNewlines: brk.indexWithoutTrailingNewlines, - hardBreak: isHardBreak, - width: lineWidth, - widthWithTrailingSpaces: lineWidthWithTrailingSpaces, - left: alignOffset, - lineNumber: lineNumber, - ); - lines.add(metrics); - _lastTakenBreak = _lastBreak = brk; - if (lines.length == _style.maxLines) { - _reachedMaxLines = true; - } - } - - /// Measures the width of a substring of [_text] starting from the index - /// [start] (inclusive) to [end] (exclusive). - /// - /// This method uses [_text], [_style] and [_canvasContext] to perform the - /// measurement. - double measureSubstring(int start, int end) { - return _measureSubstring(_canvasContext, _style, _text!, start, end); - } - - /// In a continuous block of text, finds the point where text can be broken to - /// fit in the given constraint [maxWidth]. - /// - /// This always returns at least one character even if there isn't enough - /// space for it. - int forceBreakSubstring({ - required double maxWidth, - required int start, - required int end, - }) { - assert(0 <= start); - assert(start < end); - assert(end <= _text!.length); - - // When there's no ellipsis, the breaking point should be at least one - // character away from [start]. - int low = hasEllipsis ? start : start + 1; - int high = end; - do { - final int mid = (low + high) ~/ 2; - final double width = measureSubstring(start, mid); - if (width < maxWidth) { - low = mid; - } else if (width > maxWidth) { - high = mid; - } else { - low = high = mid; - } - } while (high - low > 1); - - return low; - } -} - -/// During the text layout phase, this class takes care of calculating the -/// minimum intrinsic width of the given text. -class MinIntrinsicCalculator { - MinIntrinsicCalculator(this._canvasContext, this._text, this._style); - - final html.CanvasRenderingContext2D _canvasContext; - final String _text; - final ParagraphGeometricStyle _style; - - /// The value of minimum intrinsic width calculated so far. - double value = 0.0; - int _lastChunkEnd = 0; - - /// Consumes the next line break opportunity in [_text]. - /// - /// As this method gets called, it updates the [value] to the minimum - /// intrinsic width calculated so far. When the whole text is consumed, - /// [value] will contain the final minimum intrinsic width. - void update(LineBreakResult brk) { - final int chunkEnd = brk.index; - final double width = _measureSubstring( - _canvasContext, - _style, - _text, - _lastChunkEnd, - brk.indexWithoutTrailingSpaces, - ); - if (width > value) { - value = width; - } - _lastChunkEnd = chunkEnd; - } -} - -/// During text layout, this class is responsible for calculating the maximum -/// intrinsic width of the given text. -class MaxIntrinsicCalculator { - MaxIntrinsicCalculator(this._canvasContext, this._text, this._style); - - final html.CanvasRenderingContext2D _canvasContext; - final String _text; - final ParagraphGeometricStyle _style; - - /// The value of maximum intrinsic width calculated so far. - double value = 0.0; - int _lastHardLineEnd = 0; - - /// Consumes the next line break opportunity in [_text]. - /// - /// As this method gets called, it updates the [value] to the maximum - /// intrinsic width calculated so far. When the whole text is consumed, - /// [value] will contain the final maximum intrinsic width. - void update(LineBreakResult brk) { - if (brk.type == LineBreakType.opportunity) { - return; - } - - final double lineWidth = _measureSubstring( - _canvasContext, - _style, - _text, - _lastHardLineEnd, - brk.indexWithoutTrailingNewlines, - ); - if (lineWidth > value) { - value = lineWidth; - } - _lastHardLineEnd = brk.index; - } -} - -/// Calculates the offset necessary for the given line to be correctly aligned. -double _calculateAlignOffsetForLine({ - required EngineParagraph paragraph, - required double lineWidth, - required double maxWidth, -}) { - final double emptySpace = maxWidth - lineWidth; - // WARNING: the [paragraph] may not be laid out yet at this point. This - // function must not use layout metrics, such as [paragraph.height]. - switch (paragraph._textAlign) { - case ui.TextAlign.center: - return emptySpace / 2.0; - case ui.TextAlign.right: - return emptySpace; - case ui.TextAlign.start: - return paragraph._textDirection == ui.TextDirection.rtl - ? emptySpace - : 0.0; - case ui.TextAlign.end: - return paragraph._textDirection == ui.TextDirection.rtl - ? 0.0 - : emptySpace; - default: - return 0.0; - } -} diff --git a/lib/web_ui/lib/src/engine/text/paint_service.dart b/lib/web_ui/lib/src/engine/text/paint_service.dart new file mode 100644 index 0000000000000..423edef9ef81c --- /dev/null +++ b/lib/web_ui/lib/src/engine/text/paint_service.dart @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/ui.dart' as ui; + +import '../html/bitmap_canvas.dart'; +import '../html/painting.dart'; +import 'canvas_paragraph.dart'; +import 'layout_service.dart'; +import 'paragraph.dart'; + +/// Responsible for painting a [CanvasParagraph] on a [BitmapCanvas]. +class TextPaintService { + TextPaintService(this.paragraph); + + final CanvasParagraph paragraph; + + void paint(BitmapCanvas canvas, ui.Offset offset) { + // Loop through all the lines, for each line, loop through all the boxes and + // paint them. The boxes have enough information so they can be painted + // individually. + final List lines = paragraph.computeLineMetrics(); + + for (final EngineLineMetrics line in lines) { + for (final RangeBox box in line.boxes!) { + _paintBox(canvas, offset, line, box); + } + } + } + + void _paintBox( + BitmapCanvas canvas, + ui.Offset offset, + EngineLineMetrics line, + RangeBox box, + ) { + // Placeholder spans don't need any painting. Their boxes should remain + // empty so that their underlying widgets do their own painting. + if (box is SpanBox) { + final FlatTextSpan span = box.span; + + // Paint the background of the box, if the span has a background. + final SurfacePaint? background = span.style.background as SurfacePaint?; + if (background != null) { + canvas.drawRect( + box.toTextBox(line).toRect().shift(offset), + background.paintData, + ); + } + + // Paint the actual text. + _applySpanStyleToCanvas(span, canvas); + final double x = offset.dx + line.left + box.left; + final double y = offset.dy + line.baseline; + final String text = paragraph.toPlainText().substring( + box.start.index, + box.end.indexWithoutTrailingNewlines, + ); + final double? letterSpacing = span.style.letterSpacing; + if (letterSpacing == null || letterSpacing == 0.0) { + canvas.fillText(text, x, y, shadows: span.style.shadows); + } else { + // TODO(mdebbar): Implement letter-spacing on canvas more efficiently: + // https://github.com/flutter/flutter/issues/51234 + double charX = x; + final int len = text.length; + for (int i = 0; i < len; i++) { + final String char = text[i]; + canvas.fillText(char, charX.roundToDouble(), y, + shadows: span.style.shadows); + charX += letterSpacing + canvas.measureText(char).width!; + } + } + + // Paint the ellipsis using the same span styles. + final String? ellipsis = line.ellipsis; + if (ellipsis != null && box == line.boxes!.last) { + final double x = offset.dx + line.left + box.right; + canvas.fillText(ellipsis, x, y); + } + + canvas.tearDownPaint(); + } + } + + void _applySpanStyleToCanvas(FlatTextSpan span, BitmapCanvas canvas) { + final SurfacePaint? paint; + final ui.Paint? foreground = span.style.foreground; + if (foreground != null) { + paint = foreground as SurfacePaint; + } else { + paint = (ui.Paint()..color = span.style.color!) as SurfacePaint; + } + + canvas.setCssFont(span.style.cssFontString); + canvas.setUpPaint(paint.paintData, null); + } +} diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 55a8130e4a9d1..11e747c030cd4 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -2,12 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; +import 'dart:math' as math; -const ui.Color _defaultTextColor = ui.Color(0xFFFF0000); +import 'package:ui/ui.dart' as ui; -const String _placeholderClass = 'paragraph-placeholder'; +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../html/bitmap_canvas.dart'; +import '../util.dart'; +import 'layout_service.dart'; +import 'ruler.dart'; class EngineLineMetrics implements ui.LineMetrics { EngineLineMetrics({ @@ -21,10 +26,12 @@ class EngineLineMetrics implements ui.LineMetrics { required this.baseline, required this.lineNumber, }) : displayText = null, + ellipsis = null, startIndex = -1, endIndex = -1, endIndexWithoutNewlines = -1, - widthWithTrailingSpaces = width; + widthWithTrailingSpaces = width, + boxes = null; EngineLineMetrics.withText( String this.displayText, { @@ -36,7 +43,7 @@ class EngineLineMetrics implements ui.LineMetrics { required this.widthWithTrailingSpaces, required this.left, required this.lineNumber, - }) : assert(displayText != null), // ignore: unnecessary_null_comparison + }) : assert(displayText != null), // ignore: unnecessary_null_comparison, assert(startIndex != null), // ignore: unnecessary_null_comparison assert(endIndex != null), // ignore: unnecessary_null_comparison assert(endIndexWithoutNewlines != null), // ignore: unnecessary_null_comparison @@ -44,15 +51,41 @@ class EngineLineMetrics implements ui.LineMetrics { assert(width != null), // ignore: unnecessary_null_comparison assert(left != null), // ignore: unnecessary_null_comparison assert(lineNumber != null && lineNumber >= 0), // ignore: unnecessary_null_comparison + ellipsis = null, ascent = double.infinity, descent = double.infinity, unscaledAscent = double.infinity, height = double.infinity, - baseline = double.infinity; + baseline = double.infinity, + boxes = null; + + EngineLineMetrics.rich( + this.lineNumber, { + required this.ellipsis, + required this.startIndex, + required this.endIndex, + required this.endIndexWithoutNewlines, + required this.hardBreak, + required this.width, + required this.widthWithTrailingSpaces, + required this.left, + required this.height, + required this.baseline, + required this.ascent, + required this.descent, + required this.boxes, + }) : displayText = null, + unscaledAscent = double.infinity; /// The text to be rendered on the screen representing this line. final String? displayText; + /// The string to be displayed as an overflow indicator. + /// + /// When the value is non-null, it means this line is overflowing and the + /// [ellipsis] needs to be displayed at the end of it. + final String? ellipsis; + /// The index (inclusive) in the text where this line begins. final int startIndex; @@ -66,6 +99,10 @@ class EngineLineMetrics implements ui.LineMetrics { /// characters. final int endIndexWithoutNewlines; + /// The list of boxes representing the entire line, possibly across multiple + /// spans. + final List? boxes; + @override final bool hardBreak; @@ -105,6 +142,10 @@ class EngineLineMetrics implements ui.LineMetrics { @override final int lineNumber; + bool overlapsWith(int startIndex, int endIndex) { + return startIndex < this.endIndex && this.startIndex < endIndex; + } + @override int get hashCode => ui.hashValues( displayText, @@ -129,576 +170,109 @@ class EngineLineMetrics implements ui.LineMetrics { if (other.runtimeType != runtimeType) { return false; } - return other is EngineLineMetrics - && other.displayText == displayText - && other.startIndex == startIndex - && other.endIndex == endIndex - && other.hardBreak == hardBreak - && other.ascent == ascent - && other.descent == descent - && other.unscaledAscent == unscaledAscent - && other.height == height - && other.width == width - && other.left == left - && other.baseline == baseline - && other.lineNumber == lineNumber; + return other is EngineLineMetrics && + other.displayText == displayText && + other.startIndex == startIndex && + other.endIndex == endIndex && + other.hardBreak == hardBreak && + other.ascent == ascent && + other.descent == descent && + other.unscaledAscent == unscaledAscent && + other.height == height && + other.width == width && + other.left == left && + other.baseline == baseline && + other.lineNumber == lineNumber; } @override String toString() { if (assertionsEnabled) { return 'LineMetrics(hardBreak: $hardBreak, ' - 'ascent: $ascent, ' - 'descent: $descent, ' - 'unscaledAscent: $unscaledAscent, ' - 'height: $height, ' - 'width: $width, ' - 'left: $left, ' - 'baseline: $baseline, ' - 'lineNumber: $lineNumber)'; + 'ascent: $ascent, ' + 'descent: $descent, ' + 'unscaledAscent: $unscaledAscent, ' + 'height: $height, ' + 'width: $width, ' + 'left: $left, ' + 'baseline: $baseline, ' + 'lineNumber: $lineNumber)'; } else { return super.toString(); } } } -/// The web implementation of [ui.Paragraph]. -class EngineParagraph implements ui.Paragraph { - /// This class is created by the engine, and should not be instantiated - /// or extended directly. - /// - /// To create a [ui.Paragraph] object, use a [ui.ParagraphBuilder]. - EngineParagraph({ - required html.HtmlElement paragraphElement, - required ParagraphGeometricStyle geometricStyle, - required String? plainText, - required ui.Paint? paint, - required ui.TextAlign textAlign, - required ui.TextDirection textDirection, - required ui.Paint? background, - required this.placeholderCount, - }) : assert((plainText == null && paint == null) || - (plainText != null && paint != null)), - _paragraphElement = paragraphElement, - _geometricStyle = geometricStyle, - _plainText = plainText, - _textAlign = textAlign, - _textDirection = textDirection, - _paint = paint as SurfacePaint?, - _background = background as SurfacePaint?; - - final html.HtmlElement _paragraphElement; - final ParagraphGeometricStyle _geometricStyle; - final String? _plainText; - final SurfacePaint? _paint; - final ui.TextAlign _textAlign; - final ui.TextDirection _textDirection; - final SurfacePaint? _background; - - final int placeholderCount; - - @visibleForTesting - String? get plainText => _plainText; - - @visibleForTesting - html.HtmlElement get paragraphElement => _paragraphElement; - - @visibleForTesting - ParagraphGeometricStyle get geometricStyle => _geometricStyle; - - /// The instance of [TextMeasurementService] to be used to measure this - /// paragraph. - TextMeasurementService get _measurementService => - TextMeasurementService.forParagraph(this); - - /// The measurement result of the last layout operation. - MeasurementResult? _measurementResult; - - bool get _hasLineMetrics => _measurementResult?.lines != null; +/// Common interface for all the implementations of [ui.Paragraph] in the web +/// engine. +abstract class EngineParagraph implements ui.Paragraph { + /// Whether this paragraph has been laid out or not. + bool get isLaidOut; - @override - double get width => _measurementResult?.width ?? -1; + /// Whether this paragraph can be drawn on a bitmap canvas. + bool get drawOnCanvas; - @override - double get height => _measurementResult?.height ?? 0; + /// Whether this paragraph is doing arbitrary paint operations that require + /// a bitmap canvas, and can't be expressed in a DOM canvas. + bool get hasArbitraryPaint; - /// {@template dart.ui.paragraph.naturalHeight} - /// The amount of vertical space the paragraph occupies while ignoring the - /// [ParagraphGeometricStyle.maxLines] constraint. - /// {@endtemplate} - /// - /// Valid only after [layout] has been called. - double get _naturalHeight => _measurementResult?.naturalHeight ?? 0; + void paint(BitmapCanvas canvas, ui.Offset offset); - /// The amount of vertical space one line of this paragraph occupies. - /// - /// Valid only after [layout] has been called. - double get _lineHeight => _measurementResult?.lineHeight ?? 0; + /// Generates a flat string computed from all the spans of the paragraph. + String toPlainText(); - @override - double get longestLine { - if (_hasLineMetrics) { - double maxWidth = 0.0; - for (ui.LineMetrics metrics in _measurementResult!.lines!) { - if (maxWidth < metrics.width) { - maxWidth = metrics.width; - } - } - return maxWidth; - } - - // If we don't have any line metrics information, there's no way to know the - // longest line in a multi-line paragraph. - return 0.0; - } - - @override - double get minIntrinsicWidth => _measurementResult?.minIntrinsicWidth ?? 0; - - @override - double get maxIntrinsicWidth => _measurementResult?.maxIntrinsicWidth ?? 0; - - @override - double get alphabeticBaseline => _measurementResult?.alphabeticBaseline ?? -1; - - @override - double get ideographicBaseline => - _measurementResult?.ideographicBaseline ?? -1; - - @override - bool get didExceedMaxLines => _didExceedMaxLines; - bool _didExceedMaxLines = false; - - ui.ParagraphConstraints? _lastUsedConstraints; - - /// Returns horizontal alignment offset for single line text when rendering - /// directly into a canvas without css text alignment styling. - double _alignOffset = 0.0; - - @override - void layout(ui.ParagraphConstraints constraints) { - // When constraint width has a decimal place, we floor it to avoid getting - // a layout width that's higher than the constraint width. - // - // For example, if constraint width is `30.8` and the text has a width of - // `30.5` then the TextPainter in the framework will ceil the `30.5` width - // which will result in a width of `40.0` that's higher than the constraint - // width. - constraints = ui.ParagraphConstraints( - width: constraints.width.floorToDouble(), - ); - - if (constraints == _lastUsedConstraints) { - return; - } - - late Stopwatch stopwatch; - if (Profiler.isBenchmarkMode) { - stopwatch = Stopwatch()..start(); - } - _measurementResult = _measurementService.measure(this, constraints); - if (Profiler.isBenchmarkMode) { - stopwatch.stop(); - Profiler.instance.benchmark('text_layout', stopwatch.elapsedMicroseconds.toDouble()); - } - - _lastUsedConstraints = constraints; - - if (_geometricStyle.maxLines != null) { - _didExceedMaxLines = _naturalHeight > height; - } else { - _didExceedMaxLines = false; - } - - if (_measurementResult!.isSingleLine) { - switch (_textAlign) { - case ui.TextAlign.center: - _alignOffset = (constraints.width - maxIntrinsicWidth) / 2.0; - break; - case ui.TextAlign.right: - _alignOffset = constraints.width - maxIntrinsicWidth; - break; - case ui.TextAlign.start: - _alignOffset = _textDirection == ui.TextDirection.rtl - ? constraints.width - maxIntrinsicWidth - : 0.0; - break; - case ui.TextAlign.end: - _alignOffset = _textDirection == ui.TextDirection.ltr - ? constraints.width - maxIntrinsicWidth - : 0.0; - break; - default: - _alignOffset = 0.0; - break; - } - } - } - - @override - List getBoxesForPlaceholders() { - assert(_isLaidOut); - return _measurementResult!.placeholderBoxes; - } - - /// Returns `true` if this paragraph can be directly painted to the canvas. - /// - /// - /// Examples of paragraphs that can't be drawn directly on the canvas: - /// - /// - Rich text where there are multiple pieces of text that have different - /// styles. - /// - Paragraphs that contain decorations. - /// - Paragraphs that have a non-null word-spacing. - /// - Paragraphs with a background. - bool get _drawOnCanvas { - if (!_hasLineMetrics) { - return false; - } - - bool canDrawTextOnCanvas; - if (_measurementService.isCanvas) { - canDrawTextOnCanvas = true; - } else { - canDrawTextOnCanvas = _geometricStyle.ellipsis == null; - } - - return canDrawTextOnCanvas && - _geometricStyle.decoration == null && - _geometricStyle.wordSpacing == null && - _geometricStyle.shadows == null; - } - - /// Whether this paragraph has been laid out. - bool get _isLaidOut => _measurementResult != null; - - /// Asserts that the properties used to measure paragraph layout are the same - /// as the properties of this paragraphs root style. + /// Returns a DOM element that represents the entire paragraph and its + /// children. /// - /// Ignores properties that do not affect layout, such as - /// [ParagraphStyle.textAlign]. - bool _debugHasSameRootStyle(ParagraphGeometricStyle style) { - assert(() { - if (style != _geometricStyle) { - throw Exception('Attempted to measure a paragraph whose style is ' - 'different from the style of the ruler used to measure it.'); - } - return true; - }()); - return true; - } - - @override - List getBoxesForRange( - int start, - int end, { - ui.BoxHeightStyle boxHeightStyle = ui.BoxHeightStyle.tight, - ui.BoxWidthStyle boxWidthStyle = ui.BoxWidthStyle.tight, - }) { - assert(boxHeightStyle != null); // ignore: unnecessary_null_comparison - assert(boxWidthStyle != null); // ignore: unnecessary_null_comparison - // Zero-length ranges and invalid ranges return an empty list. - if (start == end || start < 0 || end < 0) { - return []; - } - - // For rich text, we can't measure the boxes. So for now, we'll just return - // a placeholder box to stop exceptions from being thrown in the framework. - // https://github.com/flutter/flutter/issues/55587 - if (_plainText == null) { - return [ - ui.TextBox.fromLTRBD(0, 0, 0, _lineHeight, _textDirection), - ]; - } - - final int length = _plainText!.length; - // Ranges that are out of bounds should return an empty list. - if (start > length || end > length) { - return []; - } - - // Fallback to the old, DOM-based box measurements when there's no line - // metrics. - if (!_hasLineMetrics) { - return _measurementService.measureBoxesForRange( - this, - _lastUsedConstraints!, - start: start, - end: end, - alignOffset: _alignOffset, - textDirection: _textDirection, - ); - } - - final List lines = _measurementResult!.lines!; - if (start >= lines.last.endIndex) { - return []; - } - - final EngineLineMetrics startLine = _getLineForIndex(start); - EngineLineMetrics endLine = _getLineForIndex(end); - - // If the range end is exactly at the beginning of a line, we shouldn't - // include any boxes from that line. - if (end == endLine.startIndex) { - endLine = lines[endLine.lineNumber - 1]; - } - - final List boxes = []; - for (int i = startLine.lineNumber; i <= endLine.lineNumber; i++) { - boxes.add(_getBoxForLine(lines[i], start, end)); - } - return boxes; - } - - ui.TextBox _getBoxForLine(EngineLineMetrics line, int start, int end) { - final double widthBeforeBox = start <= line.startIndex - ? 0.0 - : _measurementService.measureSubstringWidth( - this, line.startIndex, start); - final double widthAfterBox = end >= line.endIndexWithoutNewlines - ? 0.0 - : _measurementService.measureSubstringWidth( - this, end, line.endIndexWithoutNewlines); - - final double top = line.lineNumber * _lineHeight; - - // |<------------------ line.width ------------------>| - // |-------------|------------------|-------------|-----------------| - // |<-line.left->|<-widthBeforeBox->|<-box width->|<-widthAfterBox->| - // |-------------|------------------|-------------|-----------------| - // - // ^^^^^^^^^^^^^ - // This is the box we want to return. - return ui.TextBox.fromLTRBD( - line.left + widthBeforeBox, - top, - line.left + line.widthWithTrailingSpaces - widthAfterBox, - top + _lineHeight, - _textDirection, - ); - } - - ui.Paragraph _cloneWithText(String plainText) { - return EngineParagraph( - plainText: plainText, - paragraphElement: _paragraphElement.clone(true) as html.HtmlElement, - geometricStyle: _geometricStyle, - paint: _paint, - textAlign: _textAlign, - textDirection: _textDirection, - background: _background, - placeholderCount: placeholderCount, - ); - } - - @override - ui.TextPosition getPositionForOffset(ui.Offset offset) { - final List? lines = _measurementResult!.lines; - if (!_hasLineMetrics) { - return getPositionForMultiSpanOffset(offset); - } - - // [offset] is above all the lines. - if (offset.dy < 0) { - return ui.TextPosition( - offset: 0, - affinity: ui.TextAffinity.downstream, - ); - } - - final int lineNumber = offset.dy ~/ _measurementResult!.lineHeight!; - - // [offset] is below all the lines. - if (lineNumber >= lines!.length) { - return ui.TextPosition( - offset: _plainText!.length, - affinity: ui.TextAffinity.upstream, - ); - } - - final EngineLineMetrics lineMetrics = lines[lineNumber]; - final double lineLeft = lineMetrics.left; - final double lineRight = lineLeft + lineMetrics.width; - - // [offset] is to the left of the line. - if (offset.dx <= lineLeft) { - return ui.TextPosition( - offset: lineMetrics.startIndex, - affinity: ui.TextAffinity.downstream, - ); - } - - // [offset] is to the right of the line. - if (offset.dx >= lineRight) { - return ui.TextPosition( - offset: lineMetrics.endIndexWithoutNewlines, - affinity: ui.TextAffinity.upstream, - ); - } - - // If we reach here, it means the [offset] is somewhere within the line. The - // code below will do a binary search to find where exactly the [offset] - // falls within the line. - - final double dx = offset.dx - lineMetrics.left; - final TextMeasurementService instance = _measurementService; - - int low = lineMetrics.startIndex; - int high = lineMetrics.endIndexWithoutNewlines; - do { - final int current = (low + high) ~/ 2; - final double width = - instance.measureSubstringWidth(this, lineMetrics.startIndex, current); - if (width < dx) { - low = current; - } else if (width > dx) { - high = current; - } else { - low = high = current; - } - } while (high - low > 1); - - if (low == high) { - // The offset falls exactly in between the two letters. - return ui.TextPosition(offset: high, affinity: ui.TextAffinity.upstream); - } - - final double lowWidth = - instance.measureSubstringWidth(this, lineMetrics.startIndex, low); - final double highWidth = - instance.measureSubstringWidth(this, lineMetrics.startIndex, high); - - if (dx - lowWidth < highWidth - dx) { - // The offset is closer to the low index. - return ui.TextPosition(offset: low, affinity: ui.TextAffinity.downstream); - } else { - // The offset is closer to high index. - return ui.TextPosition(offset: high, affinity: ui.TextAffinity.upstream); - } - } - - ui.TextPosition getPositionForMultiSpanOffset(ui.Offset offset) { - assert(_lastUsedConstraints != null, - 'missing call to paragraph layout before reading text position'); - final TextMeasurementService instance = _measurementService; - return instance.getTextPositionForOffset( - this, _lastUsedConstraints, offset); - } - - @override - ui.TextRange getWordBoundary(ui.TextPosition position) { - ui.TextPosition textPosition = position; - final String? text = _plainText; - if (text == null) { - return ui.TextRange(start: textPosition.offset, end: textPosition.offset); - } - - final int start = WordBreaker.prevBreakIndex(text, textPosition.offset + 1); - final int end = WordBreaker.nextBreakIndex(text, textPosition.offset); - return ui.TextRange(start: start, end: end); - } - - EngineLineMetrics _getLineForIndex(int index) { - assert(_hasLineMetrics); - final List lines = _measurementResult!.lines!; - assert(index >= 0); - - for (int i = 0; i < lines.length; i++) { - final EngineLineMetrics line = lines[i]; - if (index >= line.startIndex && index < line.endIndex) { - return line; - } - } - - return lines.last; - } - - @override - ui.TextRange getLineBoundary(ui.TextPosition position) { - if (_hasLineMetrics) { - final EngineLineMetrics line = _getLineForIndex(position.offset); - return ui.TextRange(start: line.startIndex, end: line.endIndex); - } - return ui.TextRange.empty; - } - - @override - List computeLineMetrics() { - return _measurementResult!.lines!; - } + /// Generates a new DOM element on every invocation. + html.HtmlElement toDomElement(); } /// The web implementation of [ui.ParagraphStyle]. class EngineParagraphStyle implements ui.ParagraphStyle { /// Creates a new instance of [EngineParagraphStyle]. EngineParagraphStyle({ - ui.TextAlign? textAlign, - ui.TextDirection? textDirection, - int? maxLines, - String? fontFamily, - double? fontSize, - double? height, + this.textAlign, + this.textDirection, + this.maxLines, + this.fontFamily, + this.fontSize, + this.height, ui.TextHeightBehavior? textHeightBehavior, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, + this.fontWeight, + this.fontStyle, ui.StrutStyle? strutStyle, - String? ellipsis, - ui.Locale? locale, - }) : _textAlign = textAlign, - _textDirection = textDirection, - _fontWeight = fontWeight, - _fontStyle = fontStyle, - _maxLines = maxLines, - _fontFamily = fontFamily, - _fontSize = fontSize, - _height = height, - _textHeightBehavior = textHeightBehavior, - // TODO(b/128317744): add support for strut style. - _strutStyle = strutStyle as EngineStrutStyle?, - _ellipsis = ellipsis, - _locale = locale; - - final ui.TextAlign? _textAlign; - final ui.TextDirection? _textDirection; - final ui.FontWeight? _fontWeight; - final ui.FontStyle? _fontStyle; - final int? _maxLines; - final String? _fontFamily; - final double? _fontSize; - final double? _height; + this.ellipsis, + this.locale, + }) : _textHeightBehavior = textHeightBehavior, + // TODO(mdebbar): add support for strut style., b/128317744 + _strutStyle = strutStyle as EngineStrutStyle?; + + final ui.TextAlign? textAlign; + final ui.TextDirection? textDirection; + final ui.FontWeight? fontWeight; + final ui.FontStyle? fontStyle; + final int? maxLines; + final String? fontFamily; + final double? fontSize; + final double? height; final ui.TextHeightBehavior? _textHeightBehavior; final EngineStrutStyle? _strutStyle; - final String? _ellipsis; - final ui.Locale? _locale; + final String? ellipsis; + final ui.Locale? locale; // The effective style attributes should be consistent with paragraph_style.h. - ui.TextAlign get _effectiveTextAlign => _textAlign ?? ui.TextAlign.start; - ui.TextDirection get _effectiveTextDirection => _textDirection ?? ui.TextDirection.ltr; - - String get _effectiveFontFamily { - if (assertionsEnabled) { - // In the flutter tester environment, we use a predictable-size font - // "Ahem". This makes widget tests predictable and less flaky. - if (ui.debugEmulateFlutterTesterEnvironment) { - return 'Ahem'; - } - } - final String? fontFamily = _fontFamily; - if (fontFamily == null || fontFamily.isEmpty) { - return DomRenderer.defaultFontFamily; - } - return fontFamily; - } + ui.TextAlign get effectiveTextAlign => textAlign ?? ui.TextAlign.start; + ui.TextDirection get effectiveTextDirection => textDirection ?? ui.TextDirection.ltr; - double? get _lineHeight { + double? get lineHeight { // TODO(mdebbar): Implement proper support for strut styles. // https://github.com/flutter/flutter/issues/32243 if (_strutStyle == null || _strutStyle!._height == null || _strutStyle!._height == 0) { // When there's no strut height, always use paragraph style height. - return _height; + return height; } if (_strutStyle!._forceStrutHeight == true) { // When strut height is forced, ignore paragraph style height. @@ -706,7 +280,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { } // In this case, strut height acts as a minimum height for all parts of the // paragraph. So we take the max of strut height and paragraph style height. - return math.max(_strutStyle!._height!, _height ?? 0.0); + return math.max(_strutStyle!._height!, height ?? 0.0); } @override @@ -717,51 +291,51 @@ class EngineParagraphStyle implements ui.ParagraphStyle { if (other.runtimeType != runtimeType) { return false; } - return other is EngineParagraphStyle - && other._textAlign == _textAlign - && other._textDirection == _textDirection - && other._fontWeight == _fontWeight - && other._fontStyle == _fontStyle - && other._maxLines == _maxLines - && other._fontFamily == _fontFamily - && other._fontSize == _fontSize - && other._height == _height - && other._textHeightBehavior == _textHeightBehavior - && other._ellipsis == _ellipsis - && other._locale == _locale; + return other is EngineParagraphStyle && + other.textAlign == textAlign && + other.textDirection == textDirection && + other.fontWeight == fontWeight && + other.fontStyle == fontStyle && + other.maxLines == maxLines && + other.fontFamily == fontFamily && + other.fontSize == fontSize && + other.height == height && + other._textHeightBehavior == _textHeightBehavior && + other.ellipsis == ellipsis && + other.locale == locale; } @override int get hashCode { return ui.hashValues( - _textAlign, - _textDirection, - _fontWeight, - _fontStyle, - _maxLines, - _fontFamily, - _fontSize, - _height, + textAlign, + textDirection, + fontWeight, + fontStyle, + maxLines, + fontFamily, + fontSize, + height, _textHeightBehavior, - _ellipsis, - _locale); + ellipsis, + locale); } @override String toString() { if (assertionsEnabled) { return 'ParagraphStyle(' - 'textAlign: ${_textAlign ?? "unspecified"}, ' - 'textDirection: ${_textDirection ?? "unspecified"}, ' - 'fontWeight: ${_fontWeight ?? "unspecified"}, ' - 'fontStyle: ${_fontStyle ?? "unspecified"}, ' - 'maxLines: ${_maxLines ?? "unspecified"}, ' + 'textAlign: ${textAlign ?? "unspecified"}, ' + 'textDirection: ${textDirection ?? "unspecified"}, ' + 'fontWeight: ${fontWeight ?? "unspecified"}, ' + 'fontStyle: ${fontStyle ?? "unspecified"}, ' + 'maxLines: ${maxLines ?? "unspecified"}, ' 'textHeightBehavior: ${_textHeightBehavior ?? "unspecified"}, ' - 'fontFamily: ${_fontFamily ?? "unspecified"}, ' - 'fontSize: ${_fontSize != null ? _fontSize!.toStringAsFixed(1) : "unspecified"}, ' - 'height: ${_height != null ? "${_height!.toStringAsFixed(1)}x" : "unspecified"}, ' - 'ellipsis: ${_ellipsis != null ? "\"$_ellipsis\"" : "unspecified"}, ' - 'locale: ${_locale ?? "unspecified"}' + 'fontFamily: ${fontFamily ?? "unspecified"}, ' + 'fontSize: ${fontSize != null ? fontSize!.toStringAsFixed(1) : "unspecified"}, ' + 'height: ${height != null ? "${height!.toStringAsFixed(1)}x" : "unspecified"}, ' + 'ellipsis: ${ellipsis != null ? "\"$ellipsis\"" : "unspecified"}, ' + 'locale: ${locale ?? "unspecified"}' ')'; } else { return super.toString(); @@ -771,74 +345,101 @@ class EngineParagraphStyle implements ui.ParagraphStyle { /// The web implementation of [ui.TextStyle]. class EngineTextStyle implements ui.TextStyle { - EngineTextStyle({ - ui.Color? color, - ui.TextDecoration? decoration, - ui.Color? decorationColor, - ui.TextDecorationStyle? decorationStyle, - double? decorationThickness, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.TextBaseline? textBaseline, + /// Constructs an [EngineTextStyle] with all properties being required. + /// + /// This is good for call sites that need to be updated whenever a new + /// property is added to [EngineTextStyle]. Non-updated call sites will fail + /// the build otherwise. + factory EngineTextStyle({ + required ui.Color? color, + required ui.TextDecoration? decoration, + required ui.Color? decorationColor, + required ui.TextDecorationStyle? decorationStyle, + required double? decorationThickness, + required ui.FontWeight? fontWeight, + required ui.FontStyle? fontStyle, + required ui.TextBaseline? textBaseline, + required String? fontFamily, + required List? fontFamilyFallback, + required double? fontSize, + required double? letterSpacing, + required double? wordSpacing, + required double? height, + required ui.Locale? locale, + required ui.Paint? background, + required ui.Paint? foreground, + required List? shadows, + required List? fontFeatures, + }) = EngineTextStyle.only; + + /// Constructs an [EngineTextStyle] with only the given properties. + /// + /// This constructor should be used sparingly in tests, for example. Or when + /// we know for sure that not all properties are needed. + EngineTextStyle.only({ + this.color, + this.decoration, + this.decorationColor, + this.decorationStyle, + this.decorationThickness, + this.fontWeight, + this.fontStyle, + this.textBaseline, String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? letterSpacing, - double? wordSpacing, - double? height, - ui.Locale? locale, - ui.Paint? background, - ui.Paint? foreground, - List? shadows, - List? fontFeatures, + this.fontFamilyFallback, + this.fontSize, + this.letterSpacing, + this.wordSpacing, + this.height, + this.locale, + this.background, + this.foreground, + this.shadows, + this.fontFeatures, }) : assert( color == null || foreground == null, 'Cannot provide both a color and a foreground\n' 'The color argument is just a shorthand for "foreground: new Paint()..color = color".'), - _color = color, - _decoration = decoration, - _decorationColor = decorationColor, - _decorationStyle = decorationStyle, - _decorationThickness = decorationThickness, - _fontWeight = fontWeight, - _fontStyle = fontStyle, - _textBaseline = textBaseline, - _isFontFamilyProvided = fontFamily != null, - _fontFamily = fontFamily ?? '', - _fontFamilyFallback = fontFamilyFallback, - // TODO: https://github.com/flutter/flutter/issues/56707 - _fontFeatures = fontFeatures, - _fontSize = fontSize, - _letterSpacing = letterSpacing, - _wordSpacing = wordSpacing, - _height = height, - _locale = locale, - _background = background, - _foreground = foreground, - _shadows = shadows; - - final ui.Color? _color; - final ui.TextDecoration? _decoration; - final ui.Color? _decorationColor; - final ui.TextDecorationStyle? _decorationStyle; - final double? _decorationThickness; - final ui.FontWeight? _fontWeight; - final ui.FontStyle? _fontStyle; - final ui.TextBaseline? _textBaseline; - final bool _isFontFamilyProvided; - final String _fontFamily; - final List? _fontFamilyFallback; - final List? _fontFeatures; - final double? _fontSize; - final double? _letterSpacing; - final double? _wordSpacing; - final double? _height; - final ui.Locale? _locale; - final ui.Paint? _background; - final ui.Paint? _foreground; - final List? _shadows; + isFontFamilyProvided = fontFamily != null, + fontFamily = fontFamily ?? ''; + + /// Constructs an [EngineTextStyle] by reading properties from an + /// [EngineParagraphStyle]. + factory EngineTextStyle.fromParagraphStyle( + EngineParagraphStyle paragraphStyle, + ) { + return EngineTextStyle.only( + fontWeight: paragraphStyle.fontWeight, + fontStyle: paragraphStyle.fontStyle, + fontFamily: paragraphStyle.fontFamily, + fontSize: paragraphStyle.fontSize, + height: paragraphStyle.height, + locale: paragraphStyle.locale, + ); + } - String get _effectiveFontFamily { + final ui.Color? color; + final ui.TextDecoration? decoration; + final ui.Color? decorationColor; + final ui.TextDecorationStyle? decorationStyle; + final double? decorationThickness; + final ui.FontWeight? fontWeight; + final ui.FontStyle? fontStyle; + final ui.TextBaseline? textBaseline; + final bool isFontFamilyProvided; + final String fontFamily; + final List? fontFamilyFallback; + final List? fontFeatures; + final double? fontSize; + final double? letterSpacing; + final double? wordSpacing; + final double? height; + final ui.Locale? locale; + final ui.Paint? background; + final ui.Paint? foreground; + final List? shadows; + + String get effectiveFontFamily { if (assertionsEnabled) { // In the flutter tester environment, we use a predictable-size font // "Ahem". This makes widget tests predictable and less flaky. @@ -846,10 +447,37 @@ class EngineTextStyle implements ui.TextStyle { return 'Ahem'; } } - if (_fontFamily.isEmpty) { + if (fontFamily.isEmpty) { return DomRenderer.defaultFontFamily; } - return _fontFamily; + return fontFamily; + } + + String? _cssFontString; + + /// Font string to be used in CSS. + /// + /// See . + String get cssFontString { + return _cssFontString ??= buildCssFontString( + fontStyle: fontStyle, + fontWeight: fontWeight, + fontSize: fontSize, + fontFamily: effectiveFontFamily, + ); + } + + late final TextHeightStyle heightStyle = _createHeightStyle(); + + TextHeightStyle _createHeightStyle() { + return TextHeightStyle( + fontFamily: effectiveFontFamily, + fontSize: fontSize ?? DomRenderer.defaultFontSize, + height: height, + // TODO(mdebbar): Pass the actual value when font features become supported + // https://github.com/flutter/flutter/issues/64595 + fontFeatures: null, + ); } @override @@ -860,71 +488,71 @@ class EngineTextStyle implements ui.TextStyle { if (other.runtimeType != runtimeType) { return false; } - return other is EngineTextStyle - && other._color == _color - && other._decoration == _decoration - && other._decorationColor == _decorationColor - && other._decorationStyle == _decorationStyle - && other._fontWeight == _fontWeight - && other._fontStyle == _fontStyle - && other._textBaseline == _textBaseline - && other._fontFamily == _fontFamily - && other._fontSize == _fontSize - && other._letterSpacing == _letterSpacing - && other._wordSpacing == _wordSpacing - && other._height == _height - && other._locale == _locale - && other._background == _background - && other._foreground == _foreground - && _listEquals(other._shadows, _shadows) - && _listEquals(other._fontFamilyFallback, _fontFamilyFallback); + return other is EngineTextStyle && + other.color == color && + other.decoration == decoration && + other.decorationColor == decorationColor && + other.decorationStyle == decorationStyle && + other.fontWeight == fontWeight && + other.fontStyle == fontStyle && + other.textBaseline == textBaseline && + other.fontFamily == fontFamily && + other.fontSize == fontSize && + other.letterSpacing == letterSpacing && + other.wordSpacing == wordSpacing && + other.height == height && + other.locale == locale && + other.background == background && + other.foreground == foreground && + listEquals(other.shadows, shadows) && + listEquals(other.fontFamilyFallback, fontFamilyFallback); } @override int get hashCode => ui.hashValues( - _color, - _decoration, - _decorationColor, - _decorationStyle, - _decorationThickness, - _fontWeight, - _fontStyle, - _textBaseline, - _fontFamily, - _fontFamilyFallback, - _fontSize, - _letterSpacing, - _wordSpacing, - _height, - _locale, - _background, - _foreground, - _shadows, + color, + decoration, + decorationColor, + decorationStyle, + decorationThickness, + fontWeight, + fontStyle, + textBaseline, + fontFamily, + fontFamilyFallback, + fontSize, + letterSpacing, + wordSpacing, + height, + locale, + background, + foreground, + shadows, ); @override String toString() { if (assertionsEnabled) { return 'TextStyle(' - 'color: ${_color != null ? _color : "unspecified"}, ' - 'decoration: ${_decoration ?? "unspecified"}, ' - 'decorationColor: ${_decorationColor ?? "unspecified"}, ' - 'decorationStyle: ${_decorationStyle ?? "unspecified"}, ' - 'decorationThickness: ${_decorationThickness ?? "unspecified"}, ' - 'fontWeight: ${_fontWeight ?? "unspecified"}, ' - 'fontStyle: ${_fontStyle ?? "unspecified"}, ' - 'textBaseline: ${_textBaseline ?? "unspecified"}, ' - 'fontFamily: ${_isFontFamilyProvided && _fontFamily != '' ? _fontFamily : "unspecified"}, ' - 'fontFamilyFallback: ${_isFontFamilyProvided && _fontFamilyFallback != null && _fontFamilyFallback!.isNotEmpty ? _fontFamilyFallback : "unspecified"}, ' - 'fontSize: ${_fontSize != null ? _fontSize!.toStringAsFixed(1) : "unspecified"}, ' - 'letterSpacing: ${_letterSpacing != null ? "${_letterSpacing}x" : "unspecified"}, ' - 'wordSpacing: ${_wordSpacing != null ? "${_wordSpacing}x" : "unspecified"}, ' - 'height: ${_height != null ? "${_height!.toStringAsFixed(1)}x" : "unspecified"}, ' - 'locale: ${_locale ?? "unspecified"}, ' - 'background: ${_background ?? "unspecified"}, ' - 'foreground: ${_foreground ?? "unspecified"}, ' - 'shadows: ${_shadows ?? "unspecified"}, ' - 'fontFeatures: ${_fontFeatures ?? "unspecified"}' + 'color: ${color ?? "unspecified"}, ' + 'decoration: ${decoration ?? "unspecified"}, ' + 'decorationColor: ${decorationColor ?? "unspecified"}, ' + 'decorationStyle: ${decorationStyle ?? "unspecified"}, ' + 'decorationThickness: ${decorationThickness ?? "unspecified"}, ' + 'fontWeight: ${fontWeight ?? "unspecified"}, ' + 'fontStyle: ${fontStyle ?? "unspecified"}, ' + 'textBaseline: ${textBaseline ?? "unspecified"}, ' + 'fontFamily: ${isFontFamilyProvided && fontFamily != '' ? fontFamily : "unspecified"}, ' + 'fontFamilyFallback: ${isFontFamilyProvided && fontFamilyFallback != null && fontFamilyFallback!.isNotEmpty ? fontFamilyFallback : "unspecified"}, ' + 'fontSize: ${fontSize != null ? fontSize!.toStringAsFixed(1) : "unspecified"}, ' + 'letterSpacing: ${letterSpacing != null ? "${letterSpacing}x" : "unspecified"}, ' + 'wordSpacing: ${wordSpacing != null ? "${wordSpacing}x" : "unspecified"}, ' + 'height: ${height != null ? "${height!.toStringAsFixed(1)}x" : "unspecified"}, ' + 'locale: ${locale ?? "unspecified"}, ' + 'background: ${background ?? "unspecified"}, ' + 'foreground: ${foreground ?? "unspecified"}, ' + 'shadows: ${shadows ?? "unspecified"}, ' + 'fontFeatures: ${fontFeatures ?? "unspecified"}' ')'; } else { return super.toString(); @@ -934,46 +562,12 @@ class EngineTextStyle implements ui.TextStyle { /// The web implementation of [ui.StrutStyle]. class EngineStrutStyle implements ui.StrutStyle { - /// Creates a new StrutStyle object. - /// - /// * `fontFamily`: The name of the font to use when painting the text (e.g., - /// Roboto). - /// - /// * `fontFamilyFallback`: An ordered list of font family names that will be searched for when - /// the font in `fontFamily` cannot be found. - /// - /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting - /// the text. - /// - /// * `lineHeight`: The minimum height of the line boxes, as a multiple of the - /// font size. The lines of the paragraph will be at least - /// `(lineHeight + leading) * fontSize` tall when fontSize - /// is not null. When fontSize is null, there is no minimum line height. Tall - /// glyphs due to baseline alignment or large [TextStyle.fontSize] may cause - /// the actual line height after layout to be taller than specified here. - /// [fontSize] must be provided for this property to take effect. - /// - /// * `leading`: The minimum amount of leading between lines as a multiple of - /// the font size. [fontSize] must be provided for this property to take effect. - /// - /// * `fontWeight`: The typeface thickness to use when painting the text - /// (e.g., bold). - /// - /// * `fontStyle`: The typeface variant to use when drawing the letters (e.g., - /// italics). - /// - /// * `forceStrutHeight`: When true, the paragraph will force all lines to be exactly - /// `(lineHeight + leading) * fontSize` tall from baseline to baseline. - /// [TextStyle] is no longer able to influence the line height, and any tall - /// glyphs may overlap with lines above. If a [fontFamily] is specified, the - /// total ascent of the first line will be the min of the `Ascent + half-leading` - /// of the [fontFamily] and `(lineHeight + leading) * fontSize`. Otherwise, it - /// will be determined by the Ascent + half-leading of the first text. EngineStrutStyle({ String? fontFamily, List? fontFamilyFallback, double? fontSize, double? height, + ui.TextLeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, @@ -982,6 +576,7 @@ class EngineStrutStyle implements ui.StrutStyle { _fontFamilyFallback = fontFamilyFallback, _fontSize = fontSize, _height = height, + _leadingDistribution = leadingDistribution, _leading = leading, _fontWeight = fontWeight, _fontStyle = fontStyle, @@ -995,6 +590,7 @@ class EngineStrutStyle implements ui.StrutStyle { final ui.FontWeight? _fontWeight; final ui.FontStyle? _fontStyle; final bool? _forceStrutHeight; + final ui.TextLeadingDistribution? _leadingDistribution; @override bool operator ==(Object other) { @@ -1004,15 +600,16 @@ class EngineStrutStyle implements ui.StrutStyle { if (other.runtimeType != runtimeType) { return false; } - return other is EngineStrutStyle - && other._fontFamily == _fontFamily - && other._fontSize == _fontSize - && other._height == _height - && other._leading == _leading - && other._fontWeight == _fontWeight - && other._fontStyle == _fontStyle - && other._forceStrutHeight == _forceStrutHeight - && _listEquals(other._fontFamilyFallback, _fontFamilyFallback); + return other is EngineStrutStyle && + other._fontFamily == _fontFamily && + other._fontSize == _fontSize && + other._height == _height && + other._leading == _leading && + other._leadingDistribution == _leadingDistribution && + other._fontWeight == _fontWeight && + other._fontStyle == _fontStyle && + other._forceStrutHeight == _forceStrutHeight && + listEquals(other._fontFamilyFallback, _fontFamilyFallback); } @override @@ -1022,372 +619,13 @@ class EngineStrutStyle implements ui.StrutStyle { _fontSize, _height, _leading, + _leadingDistribution, _fontWeight, _fontStyle, _forceStrutHeight, ); } -/// The web implementation of [ui.ParagraphBuilder]. -class EngineParagraphBuilder implements ui.ParagraphBuilder { - /// Marks a call to the [pop] method in the [_ops] list. - static final Object _paragraphBuilderPop = Object(); - - final html.HtmlElement _paragraphElement = domRenderer.createElement('p') as html.HtmlElement; - final EngineParagraphStyle _paragraphStyle; - final List _ops = []; - - /// Creates an [EngineParagraphBuilder] object, which is used to create a - /// [EngineParagraph]. - EngineParagraphBuilder(EngineParagraphStyle style) : _paragraphStyle = style { - // TODO(b/128317744): Implement support for strut font families. - List strutFontFamilies; - if (style._strutStyle != null) { - strutFontFamilies = []; - if (style._strutStyle!._fontFamily != null) { - strutFontFamilies.add(style._strutStyle!._fontFamily); - } - if (style._strutStyle!._fontFamilyFallback != null) { - strutFontFamilies.addAll(style._strutStyle!._fontFamilyFallback!); - } - } - _applyParagraphStyleToElement( - element: _paragraphElement, style: _paragraphStyle); - } - - /// Applies the given style to the added text until [pop] is called. - /// - /// See [pop] for details. - @override - void pushStyle(ui.TextStyle style) { - _ops.add(style); - } - - @override - int get placeholderCount => _placeholderCount; - int _placeholderCount = 0; - - @override - List get placeholderScales => _placeholderScales; - final List _placeholderScales = []; - - @override - void addPlaceholder( - double width, - double height, - ui.PlaceholderAlignment alignment, { - double scale = 1.0, - double? baselineOffset, - ui.TextBaseline? baseline, - }) { - // Require a baseline to be specified if using a baseline-based alignment. - assert((alignment == ui.PlaceholderAlignment.aboveBaseline || - alignment == ui.PlaceholderAlignment.belowBaseline || - alignment == ui.PlaceholderAlignment.baseline) ? baseline != null : true); - - _placeholderCount++; - _placeholderScales.add(scale); - _ops.add(ParagraphPlaceholder( - width * scale, - height * scale, - alignment, - baselineOffset: (baselineOffset ?? height) * scale, - baseline: baseline ?? ui.TextBaseline.alphabetic, - )); - } - - // TODO(yjbanov): do we need to do this? -// static String _encodeLocale(Locale locale) => locale?.toString() ?? ''; - - /// Ends the effect of the most recent call to [pushStyle]. - /// - /// Internally, the paragraph builder maintains a stack of text styles. Text - /// added to the paragraph is affected by all the styles in the stack. Calling - /// [pop] removes the topmost style in the stack, leaving the remaining styles - /// in effect. - @override - void pop() { - _ops.add(_paragraphBuilderPop); - } - - /// Adds the given text to the paragraph. - /// - /// The text will be styled according to the current stack of text styles. - @override - void addText(String text) { - _ops.add(text); - } - - /// Applies the given paragraph style and returns a [Paragraph] containing the - /// added text and associated styling. - /// - /// After calling this function, the paragraph builder object is invalid and - /// cannot be used further. - @override - EngineParagraph build() { - return _tryBuildPlainText() ?? _buildRichText(); - } - - /// Attempts to build a [Paragraph] assuming it is plain text. - /// - /// A paragraph is considered plain if it is built using the following - /// sequence of ops: - /// - /// * Zero-or-more calls to [pushStyle]. - /// * One-or-more calls to [addText]. - /// * Zero-or-more calls to [pop]. - /// - /// Any other sequence will result in `null` and should be treated as rich - /// text. - /// - /// Plain text is not the same as not having style. The text may be styled - /// arbitrarily. However, it may not mix multiple styles in the same - /// paragraph. Plain text is more efficient to lay out and measure than rich - /// text. - EngineParagraph? _tryBuildPlainText() { - ui.Color color = _defaultTextColor; - ui.TextDecoration? decoration; - ui.Color? decorationColor; - ui.TextDecorationStyle? decorationStyle; - ui.FontWeight? fontWeight = _paragraphStyle._fontWeight; - ui.FontStyle? fontStyle = _paragraphStyle._fontStyle; - ui.TextBaseline? textBaseline; - String fontFamily = _paragraphStyle._fontFamily ?? DomRenderer.defaultFontFamily; - double fontSize = _paragraphStyle._fontSize ?? DomRenderer.defaultFontSize; - final ui.TextAlign textAlign = _paragraphStyle._effectiveTextAlign; - final ui.TextDirection textDirection = _paragraphStyle._effectiveTextDirection; - double? letterSpacing; - double? wordSpacing; - double? height; - ui.Locale? locale = _paragraphStyle._locale; - ui.Paint? background; - ui.Paint? foreground; - List? shadows; - - int i = 0; - - // This loop looks expensive. However, in reality most of plain text - // paragraphs will have no calls to [pushStyle], skipping this loop - // entirely. Occasionally there will be one [pushStyle], which causes this - // loop to run once then move on to aggregating text. - while (i < _ops.length && _ops[i] is EngineTextStyle) { - final EngineTextStyle style = _ops[i]; - if (style._color != null) { - color = style._color!; - } - if (style._decoration != null) { - decoration = style._decoration; - } - if (style._decorationColor != null) { - decorationColor = style._decorationColor; - } - if (style._decorationStyle != null) { - decorationStyle = style._decorationStyle; - } - if (style._fontWeight != null) { - fontWeight = style._fontWeight; - } - if (style._fontStyle != null) { - fontStyle = style._fontStyle; - } - if (style._textBaseline != null) { - textBaseline = style._textBaseline; - } - fontFamily = style._fontFamily; - if (style._fontSize != null) { - fontSize = style._fontSize!; - } - if (style._letterSpacing != null) { - letterSpacing = style._letterSpacing; - } - if (style._wordSpacing != null) { - wordSpacing = style._wordSpacing; - } - if (style._height != null) { - height = style._height; - } - if (style._locale != null) { - locale = style._locale; - } - if (style._background != null) { - background = style._background; - } - if (style._foreground != null) { - foreground = style._foreground; - } - if (style._shadows != null) { - shadows = style._shadows; - } - i++; - } - - final EngineTextStyle cumulativeStyle = EngineTextStyle( - color: color, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, - fontWeight: fontWeight, - fontStyle: fontStyle, - textBaseline: textBaseline, - fontFamily: fontFamily, - fontSize: fontSize, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - height: height, - locale: locale, - background: background, - foreground: foreground, - shadows: shadows, - ); - - ui.Paint paint; - if (foreground != null) { - paint = foreground; - } else { - paint = ui.Paint(); - paint.color = color; - } - - if (i >= _ops.length) { - // Empty paragraph. - _applyTextStyleToElement( - element: _paragraphElement, style: cumulativeStyle); - return EngineParagraph( - paragraphElement: _paragraphElement, - geometricStyle: ParagraphGeometricStyle( - textDirection: _paragraphStyle._effectiveTextDirection, - textAlign: _paragraphStyle._effectiveTextAlign, - fontFamily: fontFamily, - fontWeight: fontWeight, - fontStyle: fontStyle, - fontSize: fontSize, - lineHeight: height, - maxLines: _paragraphStyle._maxLines, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - decoration: _textDecorationToCssString(decoration, decorationStyle), - ellipsis: _paragraphStyle._ellipsis, - shadows: shadows, - ), - plainText: '', - paint: paint, - textAlign: textAlign, - textDirection: textDirection, - background: cumulativeStyle._background, - placeholderCount: placeholderCount, - ); - } - - if (_ops[i] is! String) { - // After a series of [EngineTextStyle] ops there must be at least one text op. - // Otherwise, treat it as rich text. - return null; - } - - // Accumulate text into one contiguous string. - final StringBuffer plainTextBuffer = StringBuffer(); - while (i < _ops.length && _ops[i] is String) { - plainTextBuffer.write(_ops[i]); - i++; - } - - // After a series of [addText] ops there should only be a tail of [pop]s and - // nothing else. Otherwise it's rich text and we return null; - for (; i < _ops.length; i++) { - if (_ops[i] != _paragraphBuilderPop) { - return null; - } - } - - final String plainText = plainTextBuffer.toString(); - domRenderer.appendText(_paragraphElement, plainText); - _applyTextStyleToElement( - element: _paragraphElement, style: cumulativeStyle); - // Since this is a plain paragraph apply background color to paragraph tag - // instead of individual spans. - if (cumulativeStyle._background != null) { - _applyTextBackgroundToElement( - element: _paragraphElement, style: cumulativeStyle); - } - return EngineParagraph( - paragraphElement: _paragraphElement, - geometricStyle: ParagraphGeometricStyle( - textDirection: _paragraphStyle._effectiveTextDirection, - textAlign: _paragraphStyle._effectiveTextAlign, - fontFamily: fontFamily, - fontWeight: fontWeight, - fontStyle: fontStyle, - fontSize: fontSize, - lineHeight: height, - maxLines: _paragraphStyle._maxLines, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - decoration: _textDecorationToCssString(decoration, decorationStyle), - ellipsis: _paragraphStyle._ellipsis, - shadows: shadows, - ), - plainText: plainText, - paint: paint, - textAlign: textAlign, - textDirection: textDirection, - background: cumulativeStyle._background, - placeholderCount: placeholderCount, - ); - } - - /// Builds a [Paragraph] as rich text. - EngineParagraph _buildRichText() { - final List elementStack = []; - dynamic currentElement() => - elementStack.isNotEmpty ? elementStack.last : _paragraphElement; - - for (int i = 0; i < _ops.length; i++) { - final dynamic op = _ops[i]; - if (op is EngineTextStyle) { - final html.SpanElement span = domRenderer.createElement('span') as html.SpanElement; - _applyTextStyleToElement(element: span, style: op, isSpan: true); - if (op._background != null) { - _applyTextBackgroundToElement(element: span, style: op); - } - domRenderer.append(currentElement(), span); - elementStack.add(span); - } else if (op is String) { - domRenderer.appendText(currentElement(), op); - } else if (op is ParagraphPlaceholder) { - domRenderer.append( - currentElement(), - _createPlaceholderElement(placeholder: op), - ); - } else if (identical(op, _paragraphBuilderPop)) { - elementStack.removeLast(); - } else { - throw UnsupportedError('Unsupported ParagraphBuilder operation: $op'); - } - } - - return EngineParagraph( - paragraphElement: _paragraphElement, - geometricStyle: ParagraphGeometricStyle( - textDirection: _paragraphStyle._effectiveTextDirection, - textAlign: _paragraphStyle._effectiveTextAlign, - fontFamily: _paragraphStyle._fontFamily, - fontWeight: _paragraphStyle._fontWeight, - fontStyle: _paragraphStyle._fontStyle, - fontSize: _paragraphStyle._fontSize, - lineHeight: _paragraphStyle._height, - maxLines: _paragraphStyle._maxLines, - ellipsis: _paragraphStyle._ellipsis, - ), - plainText: null, - paint: null, - textAlign: _paragraphStyle._effectiveTextAlign, - textDirection: _paragraphStyle._effectiveTextDirection, - background: null, - placeholderCount: placeholderCount, - ); - } -} - /// Holds information for a placeholder in a paragraph. /// /// [width], [height] and [baselineOffset] are expected to be already scaled. @@ -1458,186 +696,91 @@ String fontWeightIndexToCss({int fontWeightIndex = 3}) { return ''; } -/// Applies a paragraph [style] to an [element], translating the properties to -/// their corresponding CSS equivalents. -/// -/// If [previousStyle] is not null, updates only the mismatching attributes. -void _applyParagraphStyleToElement({ - required html.HtmlElement element, - required EngineParagraphStyle style, - EngineParagraphStyle? previousStyle, -}) { - assert(element != null); // ignore: unnecessary_null_comparison - assert(style != null); // ignore: unnecessary_null_comparison - // TODO(yjbanov): What do we do about ParagraphStyle._locale and ellipsis? - final html.CssStyleDeclaration cssStyle = element.style; - if (previousStyle == null) { - if (style._textAlign != null) { - cssStyle.textAlign = textAlignToCssValue( - style._textAlign, style._textDirection ?? ui.TextDirection.ltr); - } - if (style._lineHeight != null) { - cssStyle.lineHeight = '${style._lineHeight}'; - } - if (style._textDirection != null) { - cssStyle.direction = _textDirectionToCss(style._textDirection); - } - if (style._fontSize != null) { - cssStyle.fontSize = '${style._fontSize!.floor()}px'; - } - if (style._fontWeight != null) { - cssStyle.fontWeight = fontWeightToCss(style._fontWeight); - } - if (style._fontStyle != null) { - cssStyle.fontStyle = - style._fontStyle == ui.FontStyle.normal ? 'normal' : 'italic'; - } - cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily); - } else { - if (style._textAlign != previousStyle._textAlign) { - cssStyle.textAlign = textAlignToCssValue( - style._textAlign, style._textDirection ?? ui.TextDirection.ltr); - } - if (style._lineHeight != previousStyle._lineHeight) { - cssStyle.lineHeight = '${style._lineHeight}'; - } - if (style._textDirection != previousStyle._textDirection) { - cssStyle.direction = _textDirectionToCss(style._textDirection); - } - if (style._fontSize != previousStyle._fontSize) { - cssStyle.fontSize = - style._fontSize != null ? '${style._fontSize!.floor()}px' : null; - } - if (style._fontWeight != previousStyle._fontWeight) { - cssStyle.fontWeight = fontWeightToCss(style._fontWeight); - } - if (style._fontStyle != previousStyle._fontStyle) { - cssStyle.fontStyle = style._fontStyle != null - ? (style._fontStyle == ui.FontStyle.normal ? 'normal' : 'italic') - : null; - } - if (style._fontFamily != previousStyle._fontFamily) { - cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); - } - } -} - /// Applies a text [style] to an [element], translating the properties to their /// corresponding CSS equivalents. /// -/// If [previousStyle] is not null, updates only the mismatching attributes. /// If [isSpan] is true, the text element is a span within richtext and /// should not assign effectiveFontFamily if fontFamily was not specified. -void _applyTextStyleToElement({ +void applyTextStyleToElement({ required html.HtmlElement element, required EngineTextStyle style, - EngineTextStyle? previousStyle, bool isSpan = false, }) { assert(element != null); // ignore: unnecessary_null_comparison assert(style != null); // ignore: unnecessary_null_comparison bool updateDecoration = false; final html.CssStyleDeclaration cssStyle = element.style; - if (previousStyle == null) { - final ui.Color? color = style._foreground?.color ?? style._color; - if (color != null) { - cssStyle.color = colorToCssString(color); - } - if (style._fontSize != null) { - cssStyle.fontSize = '${style._fontSize!.floor()}px'; - } - if (style._fontWeight != null) { - cssStyle.fontWeight = fontWeightToCss(style._fontWeight); - } - if (style._fontStyle != null) { - cssStyle.fontStyle = - style._fontStyle == ui.FontStyle.normal ? 'normal' : 'italic'; - } - // For test environment use effectiveFontFamily since we need to - // consistently use Ahem font. - if (isSpan && !ui.debugEmulateFlutterTesterEnvironment) { - cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); - } else { - cssStyle.fontFamily = - canonicalizeFontFamily(style._effectiveFontFamily); - } - if (style._letterSpacing != null) { - cssStyle.letterSpacing = '${style._letterSpacing}px'; - } - if (style._wordSpacing != null) { - cssStyle.wordSpacing = '${style._wordSpacing}px'; - } - if (style._decoration != null) { - updateDecoration = true; - } - if (style._shadows != null) { - cssStyle.textShadow = _shadowListToCss(style._shadows!); - } - } else { - if (style._color != previousStyle._color || - style._foreground != previousStyle._foreground) { - final ui.Color? color = style._foreground?.color ?? style._color; - cssStyle.color = colorToCssString(color); - } - - if (style._fontSize != previousStyle._fontSize) { - cssStyle.fontSize = - style._fontSize != null ? '${style._fontSize!.floor()}px' : null; - } - - if (style._fontWeight != previousStyle._fontWeight) { - cssStyle.fontWeight = fontWeightToCss(style._fontWeight); - } - if (style._fontStyle != previousStyle._fontStyle) { - cssStyle.fontStyle = style._fontStyle != null - ? style._fontStyle == ui.FontStyle.normal ? 'normal' : 'italic' - : null; - } - if (style._fontFamily != previousStyle._fontFamily) { - cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily); - } - if (style._letterSpacing != previousStyle._letterSpacing) { - cssStyle.letterSpacing = '${style._letterSpacing}px'; - } - if (style._wordSpacing != previousStyle._wordSpacing) { - cssStyle.wordSpacing = '${style._wordSpacing}px'; - } - if (style._decoration != previousStyle._decoration || - style._decorationStyle != previousStyle._decorationStyle || - style._decorationColor != previousStyle._decorationColor) { - updateDecoration = true; - } - if (style._shadows != previousStyle._shadows) { - cssStyle.textShadow = _shadowListToCss(style._shadows!); - } + final ui.Color? color = style.foreground?.color ?? style.color; + if (color != null) { + cssStyle.color = colorToCssString(color); + } + final ui.Color? background = style.background?.color; + if (background != null) { + cssStyle.backgroundColor = colorToCssString(background); + } + if (style.height != null) { + cssStyle.lineHeight = '${style.height}'; + } + if (style.fontSize != null) { + cssStyle.fontSize = '${style.fontSize!.floor()}px'; + } + if (style.fontWeight != null) { + cssStyle.fontWeight = fontWeightToCss(style.fontWeight); + } + if (style.fontStyle != null) { + cssStyle.fontStyle = + style.fontStyle == ui.FontStyle.normal ? 'normal' : 'italic'; + } + // For test environment use effectiveFontFamily since we need to + // consistently use Ahem font. + if (isSpan && !ui.debugEmulateFlutterTesterEnvironment) { + cssStyle.fontFamily = canonicalizeFontFamily(style.fontFamily); + } else { + cssStyle.fontFamily = canonicalizeFontFamily(style.effectiveFontFamily); + } + if (style.letterSpacing != null) { + cssStyle.letterSpacing = '${style.letterSpacing}px'; + } + if (style.wordSpacing != null) { + cssStyle.wordSpacing = '${style.wordSpacing}px'; + } + if (style.decoration != null) { + updateDecoration = true; + } + if (style.shadows != null) { + cssStyle.textShadow = _shadowListToCss(style.shadows!); } if (updateDecoration) { - if (style._decoration != null) { + if (style.decoration != null) { final String? textDecoration = - _textDecorationToCssString(style._decoration, style._decorationStyle); + _textDecorationToCssString(style.decoration, style.decorationStyle); if (textDecoration != null) { if (browserEngine == BrowserEngine.webkit) { - domRenderer.setElementStyle( + DomRenderer.setElementStyle( element, '-webkit-text-decoration', textDecoration); } else { cssStyle.textDecoration = textDecoration; } - final ui.Color? decorationColor = style._decorationColor; + final ui.Color? decorationColor = style.decorationColor; if (decorationColor != null) { cssStyle.textDecorationColor = colorToCssString(decorationColor)!; } } } } + + final List? fontFeatures = style.fontFeatures; + if (fontFeatures != null && fontFeatures.isNotEmpty) { + cssStyle.fontFeatureSettings = _fontFeatureListToCss(fontFeatures); + } } -html.Element _createPlaceholderElement({ +html.Element createPlaceholderElement({ required ParagraphPlaceholder placeholder, }) { final html.Element element = domRenderer.createElement('span'); - element.className = _placeholderClass; final html.CssStyleDeclaration style = element.style; style ..display = 'inline-block' @@ -1688,37 +831,34 @@ String _shadowListToCss(List shadows) { // Shadows are applied front-to-back with first shadow on top. // Color is optional. offsetx,y are required. blur-radius is optional as well // and defaults to 0. - StringBuffer sb = new StringBuffer(); - for (int i = 0, len = shadows.length; i < len; i++) { + final StringBuffer sb = StringBuffer(); + final int len = shadows.length; + for (int i = 0; i < len; i++) { if (i != 0) { sb.write(','); } - ui.Shadow shadow = shadows[i]; + final ui.Shadow shadow = shadows[i]; sb.write('${shadow.offset.dx}px ${shadow.offset.dy}px ' '${shadow.blurRadius}px ${colorToCssString(shadow.color)}'); } return sb.toString(); } -/// Applies background color properties in text style to paragraph or span -/// elements. -void _applyTextBackgroundToElement({ - required html.HtmlElement element, - required EngineTextStyle style, - EngineTextStyle? previousStyle, -}) { - final ui.Paint? newBackground = style._background; - if (previousStyle == null) { - if (newBackground != null) { - domRenderer.setElementStyle( - element, 'background-color', colorToCssString(newBackground.color)); - } - } else { - if (newBackground != previousStyle._background) { - domRenderer.setElementStyle( - element, 'background-color', colorToCssString(newBackground!.color)); +String _fontFeatureListToCss(List fontFeatures) { + assert(fontFeatures.isNotEmpty); + + // For more details, see: + // * https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings + final StringBuffer sb = StringBuffer(); + final int len = fontFeatures.length; + for (int i = 0; i < len; i++) { + if (i != 0) { + sb.write(','); } + final ui.FontFeature fontFeature = fontFeatures[i]; + sb.write('"${fontFeature.feature}" ${fontFeature.value}'); } + return sb.toString(); } /// Converts text decoration style to CSS text-decoration-style value. @@ -1766,7 +906,7 @@ String? _decorationStyleToCssString(ui.TextDecorationStyle decorationStyle) { /// ```css /// direction: rtl; /// ``` -String? _textDirectionToCss(ui.TextDirection? textDirection) { +String? textDirectionToCss(ui.TextDirection? textDirection) { if (textDirection == null) { return null; } @@ -1797,7 +937,8 @@ String? textDirectionIndexToCss(int textDirectionIndex) { /// ```css /// text-align: right; /// ``` -String textAlignToCssValue(ui.TextAlign? align, ui.TextDirection textDirection) { +String textAlignToCssValue( + ui.TextAlign? align, ui.TextDirection textDirection) { switch (align) { case ui.TextAlign.left: return 'left'; @@ -1826,23 +967,3 @@ String textAlignToCssValue(ui.TextAlign? align, ui.TextDirection textDirection) return ''; } } - -/// Determines if lists [a] and [b] are deep equivalent. -/// -/// Returns true if the lists are both null, or if they are both non-null, have -/// the same length, and contain the same elements in the same order. Returns -/// false otherwise. -bool _listEquals(List? a, List? b) { - if (a == null) { - return b == null; - } - if (b == null || a.length != b.length) { - return false; - } - for (int index = 0; index < a.length; index += 1) { - if (a[index] != b[index]) { - return false; - } - } - return true; -} diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index 93d8bb6b2701c..79d1d2d20445c 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -2,8 +2,50 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../util.dart'; +import 'measurement.dart'; +import 'paragraph.dart'; + +String buildCssFontString({ + required ui.FontStyle? fontStyle, + required ui.FontWeight? fontWeight, + required double? fontSize, + required String fontFamily, +}) { + final StringBuffer result = StringBuffer(); + + // Font style + if (fontStyle != null) { + result.write(fontStyle == ui.FontStyle.normal ? 'normal' : 'italic'); + } else { + result.write(DomRenderer.defaultFontStyle); + } + result.write(' '); + + // Font weight. + if (fontWeight != null) { + result.write(fontWeightToCss(fontWeight)); + } else { + result.write(DomRenderer.defaultFontWeight); + } + result.write(' '); + + if (fontSize != null) { + result.write(fontSize.floor()); + } else { + result.write(DomRenderer.defaultFontSize); + } + result.write('px '); + result.write(canonicalizeFontFamily(fontFamily)); + + return result.toString(); +} /// Contains the subset of [ui.ParagraphStyle] properties that affect layout. class ParagraphGeometricStyle { @@ -46,7 +88,7 @@ class ParagraphGeometricStyle { /// /// - Always returns "Ahem" in tests. /// - Provides correct defaults when [fontFamily] doesn't have a value. - String? get effectiveFontFamily { + String get effectiveFontFamily { if (assertionsEnabled) { // In widget tests we use a predictable-size font "Ahem". This makes // widget tests predictable and less flaky. @@ -54,10 +96,11 @@ class ParagraphGeometricStyle { return 'Ahem'; } } - if (fontFamily == null || fontFamily!.isEmpty) { + final String? localFontFamily = fontFamily; + if (localFontFamily == null || localFontFamily.isEmpty) { return DomRenderer.defaultFontFamily; } - return fontFamily; + return localFontFamily; } String? _cssFontString; @@ -65,36 +108,31 @@ class ParagraphGeometricStyle { /// Cached font string that can be used in CSS. /// /// See . - String get cssFontString => _cssFontString ??= _buildCssFontString(); - - String _buildCssFontString() { - final StringBuffer result = StringBuffer(); - - // Font style - if (fontStyle != null) { - result.write(fontStyle == ui.FontStyle.normal ? 'normal' : 'italic'); - } else { - result.write(DomRenderer.defaultFontStyle); - } - result.write(' '); + String get cssFontString { + return _cssFontString ??= buildCssFontString( + fontStyle: fontStyle, + fontWeight: fontWeight, + fontSize: fontSize, + fontFamily: effectiveFontFamily, + ); + } - // Font weight. - if (fontWeight != null) { - result.write(fontWeightToCss(fontWeight)); - } else { - result.write(DomRenderer.defaultFontWeight); - } - result.write(' '); + TextHeightStyle? _cachedHeightStyle; - if (fontSize != null) { - result.write(fontSize!.floor()); - } else { - result.write(DomRenderer.defaultFontSize); + TextHeightStyle get textHeightStyle { + TextHeightStyle? style = _cachedHeightStyle; + if (style == null) { + style = TextHeightStyle( + fontFamily: effectiveFontFamily, + fontSize: fontSize ?? DomRenderer.defaultFontSize, + height: lineHeight, + // TODO(mdebbar): Pass the actual value when font features become supported + // https://github.com/flutter/flutter/issues/64595 + fontFeatures: null, + ); + _cachedHeightStyle = style; } - result.write('px '); - result.write(canonicalizeFontFamily(effectiveFontFamily)); - - return result.toString(); + return style; } @override @@ -156,6 +194,40 @@ class ParagraphGeometricStyle { } } +/// Contains all styles that have an effect on the height of text. +/// +/// This is useful as a cache key for [TextHeightRuler]. It's more efficient +/// than using the entire [ParagraphGeometricStyle] as a cache key. +class TextHeightStyle { + TextHeightStyle({ + required this.fontFamily, + required this.fontSize, + required this.height, + required this.fontFeatures, + }); + + final String fontFamily; + final double fontSize; + final double? height; + final List? fontFeatures; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + return other is TextHeightStyle && other.hashCode == hashCode; + } + + @override + late final int hashCode = ui.hashValues( + fontFamily, + fontSize, + height, + ui.hashList(fontFeatures), + ); +} + /// Provides text dimensions found on [_element]. The idea behind this class is /// to allow the [ParagraphRuler] to mutate multiple dom elements and allow /// consumers to lazily read the measurements. @@ -174,73 +246,6 @@ class TextDimensions { final html.HtmlElement _element; html.Rectangle? _cachedBoundingClientRect; - /// Attempts to efficiently copy text from [from]. - /// - /// The primary efficiency gain is from rare occurrence of rich text in - /// typical apps. - void updateText(EngineParagraph from, ParagraphGeometricStyle style) { - assert(from != null); // ignore: unnecessary_null_comparison - assert(_element != null); // ignore: unnecessary_null_comparison - assert(from._debugHasSameRootStyle(style)); - assert(() { - final bool wasEmptyOrPlainText = _element.childNodes.isEmpty || - (_element.childNodes.length == 1 && - _element.childNodes.first is html.Text); - if (!wasEmptyOrPlainText) { - throw Exception( - 'Failed to copy text into the paragraph measuring element. The ' - 'element already contains rich text "${_element.innerHtml}". It is ' - 'likely that a previous measurement did not clean up after ' - 'itself.'); - } - return true; - }()); - - _invalidateBoundsCache(); - final String? plainText = from._plainText; - if (plainText != null) { - // Plain text: just set the string. The paragraph's style is assumed to - // match the style set on the `element`. Setting text as plain string is - // faster because it doesn't change the DOM structure or CSS attributes, - // and therefore doesn't trigger style recalculations in the browser. - if (plainText.endsWith('\n')) { - // On the web the last newline is ignored. To be consistent with - // native engine implementation we add extra newline to get correct - // height measurement. - _element.text = '$plainText\n'; - } else { - _element.text = plainText; - } - } else { - // Rich text: deeply copy contents. This is the slow case that should be - // avoided if fast layout performance is desired. - final html.Element copy = from._paragraphElement.clone(true) as html.Element; - _element.nodes.addAll(copy.nodes); - } - } - - /// Updated element style width. - void updateConstraintWidth(double width, String? ellipsis) { - _invalidateBoundsCache(); - - if (width.isInfinite) { - _element.style - ..width = null - ..whiteSpace = 'pre'; - } else if (ellipsis != null) { - // Width is finite, but we don't want to let the text soft-wrap when - // ellipsis overflow is enabled. - _element.style - ..width = '${width}px' - ..whiteSpace = 'pre'; - } else { - // Width is finite and there's no ellipsis overflow. - _element.style - ..width = '${width}px' - ..whiteSpace = 'pre-wrap'; - } - } - void _invalidateBoundsCache() { _cachedBoundingClientRect = null; } @@ -251,37 +256,22 @@ class TextDimensions { _element.text = ' '; } - /// Applies geometric style properties to the [element]. - void applyStyle(ParagraphGeometricStyle style) { - final html.CssStyleDeclaration elementStyle = _element.style; - elementStyle - ..direction = _textDirectionToCss(style.textDirection) - ..textAlign = textAlignToCssValue(style.textAlign, style.textDirection) - ..fontSize = style.fontSize != null ? '${style.fontSize!.floor()}px' : null - ..fontFamily = canonicalizeFontFamily(style.effectiveFontFamily) - ..fontWeight = - style.fontWeight != null ? fontWeightToCss(style.fontWeight) : null - ..fontStyle = style.fontStyle != null - ? style.fontStyle == ui.FontStyle.normal ? 'normal' : 'italic' - : null - ..letterSpacing = - style.letterSpacing != null ? '${style.letterSpacing}px' : null - ..wordSpacing = - style.wordSpacing != null ? '${style.wordSpacing}px' : null; - final String? decoration = style.decoration; - if (browserEngine == BrowserEngine.webkit) { - domRenderer.setElementStyle( - _element, '-webkit-text-decoration', decoration); - } else { - elementStyle.textDecoration = decoration; - } - if (style.lineHeight != null) { - elementStyle.lineHeight = style.lineHeight!.toString(); + void applyHeightStyle(TextHeightStyle textHeightStyle) { + final String fontFamily = textHeightStyle.fontFamily; + final double fontSize = textHeightStyle.fontSize; + final html.CssStyleDeclaration style = _element.style; + style + ..fontSize = '${fontSize.floor()}px' + ..fontFamily = canonicalizeFontFamily(fontFamily); + + final double? height = textHeightStyle.height; + if (height != null) { + style.lineHeight = height.toString(); } _invalidateBoundsCache(); } - /// Appends element and probe to hostElement that is setup for a specific + /// Appends element and probe to hostElement that is set up for a specific /// TextStyle. void appendToHost(html.HtmlElement hostElement) { hostElement.append(_element); @@ -291,9 +281,6 @@ class TextDimensions { html.Rectangle _readAndCacheMetrics() => _cachedBoundingClientRect ??= _element.getBoundingClientRect(); - /// The width of the paragraph being measured. - double get width => _readAndCacheMetrics().width as double; - /// The height of the paragraph being measured. double get height { double cachedHeight = _readAndCacheMetrics().height as double; @@ -311,230 +298,37 @@ class TextDimensions { } } -/// Performs 4 types of measurements: -/// -/// 1. Single line: can be prepared by calling [measureAsSingleLine]. -/// Measurement values will be available at [singleLineDimensions]. -/// -/// 2. Minimum intrinsic width: can be prepared by calling -/// [measureMinIntrinsicWidth]. Measurement values will be available at -/// [minIntrinsicDimensions]. -/// -/// 3. Constrained: can be prepared by calling [measureWithConstraints] and -/// passing the constraints. Measurement values will be available at -/// [constrainedDimensions]. -/// -/// 4. Boxes: within a paragraph, it measures a list of text boxes that enclose -/// a given range of text. -/// -/// For performance reasons, it's advised to use [measureAll] and then reading -/// whatever measurements are needed. This causes the browser to only reflow -/// once instead of many times. -/// -/// The [measureAll] method performs the first 3 stateful measurements but not -/// the 4th one. +/// Performs height measurement for the given [textHeightStyle]. /// -/// This class is both reusable and stateful. Use it carefully. The correct -/// usage is as follows: +/// The two results of this ruler's measurement are: /// -/// * First, call [willMeasure] passing it the paragraph to be measured. -/// * Call any of the [measureAsSingleLine], [measureMinIntrinsicWidth], -/// [measureWithConstraints], or [measureAll], to prepare the respective -/// measurement. These methods can be called any number of times. -/// * Call [didMeasure] to indicate that you are done with the paragraph passed -/// to the [willMeasure] method. -/// -/// It is safe to reuse this object as long as paragraphs passed to the -/// [measure] method have the same style. -/// -/// The only stateless method provided by this class is [measureBoxesForRange] -/// that doesn't rely on [willMeasure] and [didMeasure] lifecycle methods. -/// -/// This class optimizes for plain text paragraphs, which should constitute the -/// majority of paragraphs in typical apps. -class ParagraphRuler { - /// The only style that this [ParagraphRuler] measures text. - final ParagraphGeometricStyle style; - - /// A [RulerManager] owns the host DOM element that this ruler can add - /// elements to. - /// - /// The [rulerManager] keeps a cache of multiple [ParagraphRuler] instances, - /// but a [ParagraphRuler] can only belong to one [RulerManager]. - final RulerManager rulerManager; - - /// Probe to use for measuring alphabetic base line. - final html.HtmlElement _probe = html.DivElement(); - - /// Cached value of alphabetic base line. - double? _cachedAlphabeticBaseline; - - ParagraphRuler(this.style, this.rulerManager) { - _configureSingleLineHostElements(); - // Since alphabeticbaseline will be same regardless of constraints. - // We can measure it using a probe on the single line dimensions - // host. - _singleLineHost.append(_probe); - _configureMinIntrinsicHostElements(); - _configureConstrainedHostElements(); - } - - /// The alphabetic baseline of the paragraph being measured. - double get alphabeticBaseline => - (_cachedAlphabeticBaseline ??= _probe.getBoundingClientRect().bottom as double?)!; - - // Elements used to measure single-line metrics. - final html.DivElement _singleLineHost = html.DivElement(); - final TextDimensions singleLineDimensions = - TextDimensions(html.ParagraphElement()); +/// 1. [alphabeticBaseline]. +/// 2. [height]. +class TextHeightRuler { + TextHeightRuler(this.textHeightStyle, this.rulerHost); - // Elements used to measure minIntrinsicWidth. - final html.DivElement _minIntrinsicHost = html.DivElement(); - TextDimensions minIntrinsicDimensions = - TextDimensions(html.ParagraphElement()); - - // Elements used to measure metrics under a width constraint. - final html.DivElement _constrainedHost = html.DivElement(); - TextDimensions constrainedDimensions = - TextDimensions(html.ParagraphElement()); + final TextHeightStyle textHeightStyle; + final RulerHost rulerHost; // Elements used to measure the line-height metric. - html.DivElement? _lineHeightHost; - TextDimensions? _lineHeightDimensions; - TextDimensions? get lineHeightDimensions { - // Lazily create the elements for line-height measurement since they are not - // always needed. - if (_lineHeightDimensions == null) { - _lineHeightHost = html.DivElement(); - _lineHeightDimensions = TextDimensions(html.ParagraphElement()); - _configureLineHeightHostElements(); - _lineHeightHost!.append(_probe); - } - return _lineHeightDimensions; - } - - /// The number of times this ruler was used this frame. - /// - /// This value is used to determine which rulers are rarely used and should be - /// evicted from the ruler cache. - int get hitCount => _hitCount; - int _hitCount = 0; - - /// This method should be called whenever this ruler is being used to perform - /// measurements. - /// - /// It increases the hit count of this ruler which is used when clearing the - /// [rulerManager]'s cache to find the least used rulers. - void hit() { - _hitCount++; - } - - /// Resets the hit count back to zero. - void resetHitCount() { - _hitCount = 0; - } - - /// Makes sure this ruler is not used again after it has been disposed of, - /// which would indicate a bug. - @visibleForTesting - bool get debugIsDisposed => _debugIsDisposed; - bool _debugIsDisposed = false; - - void _configureSingleLineHostElements() { - _singleLineHost.style - ..visibility = 'hidden' - ..position = 'absolute' - ..top = '0' // this is important as baseline == probe.bottom - ..left = '0' - ..display = 'flex' - ..flexDirection = 'row' - ..alignItems = 'baseline' - ..margin = '0' - ..border = '0' - ..padding = '0'; - - if (assertionsEnabled) { - _singleLineHost.setAttribute('data-ruler', 'single-line'); - } - - singleLineDimensions.applyStyle(style); - - // Force single-line (even if wider than screen) and preserve whitespaces. - singleLineDimensions._element.style.whiteSpace = 'pre'; - - singleLineDimensions.appendToHost(_singleLineHost); - rulerManager.addHostElement(_singleLineHost); - } - - void _configureMinIntrinsicHostElements() { - // Configure min intrinsic host elements. - _minIntrinsicHost.style - ..visibility = 'hidden' - ..position = 'absolute' - ..top = '0' // this is important as baseline == probe.bottom - ..left = '0' - ..display = 'flex' - ..flexDirection = 'row' - ..margin = '0' - ..border = '0' - ..padding = '0'; - - if (assertionsEnabled) { - _minIntrinsicHost.setAttribute('data-ruler', 'min-intrinsic'); - } + late final html.HtmlElement _probe = _createProbe(); + late final html.HtmlElement _host = _createHost(); + final TextDimensions _dimensions = TextDimensions(html.ParagraphElement()); - minIntrinsicDimensions.applyStyle(style); + /// The alphabetic baseline for this ruler's [textHeightStyle]. + late final double alphabeticBaseline = _probe.getBoundingClientRect().bottom.toDouble(); - // "flex: 0" causes the paragraph element to shrink horizontally, exposing - // its minimum intrinsic width. - minIntrinsicDimensions._element.style - ..flex = '0' - ..display = 'inline' - // Preserve newlines, wrap text, remove end of line spaces. - // Not using pre-wrap here since end of line space hang measurement - // changed in Chrome 77 Beta. - ..whiteSpace = 'pre-line'; + /// The height for this ruler's [textHeightStyle]. + late final double height = _dimensions.height; - _minIntrinsicHost.append(minIntrinsicDimensions._element); - rulerManager.addHostElement(_minIntrinsicHost); - } - - void _configureConstrainedHostElements() { - _constrainedHost.style - ..visibility = 'hidden' - ..position = 'absolute' - ..top = '0' // this is important as baseline == probe.bottom - ..left = '0' - ..display = 'flex' - ..flexDirection = 'row' - ..alignItems = 'baseline' - ..margin = '0' - ..border = '0' - ..padding = '0'; - - if (assertionsEnabled) { - _constrainedHost.setAttribute('data-ruler', 'constrained'); - } - - constrainedDimensions.applyStyle(style); - final html.CssStyleDeclaration elementStyle = - constrainedDimensions._element.style; - elementStyle - ..display = 'block' - ..overflowWrap = 'break-word'; - - if (style.ellipsis != null) { - elementStyle - ..overflow = 'hidden' - ..textOverflow = 'ellipsis'; - } - - constrainedDimensions.appendToHost(_constrainedHost); - rulerManager.addHostElement(_constrainedHost); + /// Disposes of this ruler and detaches it from the DOM tree. + void dispose() { + _host.remove(); } - void _configureLineHeightHostElements() { - _lineHeightHost!.style + html.HtmlElement _createHost() { + final html.DivElement host = html.DivElement(); + host.style ..visibility = 'hidden' ..position = 'absolute' ..top = '0' @@ -547,433 +341,25 @@ class ParagraphRuler { ..padding = '0'; if (assertionsEnabled) { - _lineHeightHost!.setAttribute('data-ruler', 'line-height'); + host.setAttribute('data-ruler', 'line-height'); } - lineHeightDimensions!.applyStyle(style); + _dimensions.applyHeightStyle(textHeightStyle); // Force single-line (even if wider than screen) and preserve whitespaces. - lineHeightDimensions!._element.style.whiteSpace = 'pre'; + _dimensions._element.style.whiteSpace = 'pre'; // To measure line-height, all we need is a whitespace. - lineHeightDimensions!.updateTextToSpace(); - - lineHeightDimensions!.appendToHost(_lineHeightHost!); - rulerManager.addHostElement(_lineHeightHost!); - } - - /// The paragraph being measured. - EngineParagraph? _paragraph; - - /// Prepares this ruler for measuring the given [paragraph]. - /// - /// This method must be called before calling any of the `measure*` methods. - void willMeasure(EngineParagraph paragraph) { - assert(paragraph != null); // ignore: unnecessary_null_comparison - assert(() { - if (_paragraph != null) { - throw Exception( - 'Attempted to reuse a $ParagraphRuler but it is currently ' - 'measuring another paragraph ($_paragraph). It is possible that '); - } - return true; - }()); - assert(paragraph._debugHasSameRootStyle(style)); - _paragraph = paragraph; - } - - /// Prepares all 3 measurements: - /// 1. single line. - /// 2. minimum intrinsic width. - /// 3. constrained. - void measureAll(ui.ParagraphConstraints constraints) { - measureAsSingleLine(); - measureMinIntrinsicWidth(); - measureWithConstraints(constraints); - } - - /// Lays out the paragraph in a single line, giving it infinite amount of - /// horizontal space. - /// - /// Measures [width], [height], and [alphabeticBaseline]. - void measureAsSingleLine() { - assert(!_debugIsDisposed); - assert(_paragraph != null); - - // HACK(mdebbar): TextField uses an empty string to measure the line height, - // which doesn't work. So we need to replace it with a whitespace. The - // correct fix would be to do line height and baseline measurements and - // cache them separately. - if (_paragraph!._plainText == '') { - singleLineDimensions.updateTextToSpace(); - } else { - singleLineDimensions.updateText(_paragraph!, style); - } - } - - /// Lays out the paragraph inside a flex row and sets "flex: 0", which - /// squeezes the paragraph, forcing it to occupy minimum intrinsic width. - /// - /// Measures [width] and [height]. - void measureMinIntrinsicWidth() { - assert(!_debugIsDisposed); - assert(_paragraph != null); - - minIntrinsicDimensions.updateText(_paragraph!, style); - } - - /// Lays out the paragraph giving it a width constraint. - /// - /// Measures [width], [height], and [alphabeticBaseline]. - void measureWithConstraints(ui.ParagraphConstraints constraints) { - assert(!_debugIsDisposed); - assert(_paragraph != null); - - constrainedDimensions.updateText(_paragraph!, style); - - // The extra 0.5 is because sometimes the browser needs slightly more space - // than the size it reports back. When that happens the text may be wrap - // when we thought it didn't. - constrainedDimensions.updateConstraintWidth( - constraints.width + 0.5, - style.ellipsis, - ); - } - - List measurePlaceholderBoxes() { - assert(!_debugIsDisposed); - assert(_paragraph != null); - - if (_paragraph!.placeholderCount == 0) { - return const []; - } - - final List placeholderElements = - constrainedDimensions._element.querySelectorAll('.$_placeholderClass'); - final List boxes = []; - - for (final html.Element element in placeholderElements) { - final html.Rectangle rect = element.getBoundingClientRect(); - boxes.add(ui.TextBox.fromLTRBD( - rect.left as double, - rect.top as double, - rect.right as double, - rect.bottom as double, - _paragraph!._textDirection, - )); - } - return boxes; - } - - /// Returns text position in a paragraph that contains multiple - /// nested spans given an offset. - int hitTest(ui.ParagraphConstraints constraints, ui.Offset offset) { - measureWithConstraints(constraints); - // Get paragraph element root used to measure constrainedDimensions. - final html.HtmlElement el = constrainedDimensions._element; - final List textNodes = []; - // Collect all text nodes (breadth first traversal). - // Since there is no api to get bounds of text nodes directly we work - // upwards and measure span elements and finally the paragraph. - _collectTextNodes(el.childNodes, textNodes); - // Hit test spans starting from leaf nodes up (backwards). - for (int i = textNodes.length - 1; i >= 0; i--) { - final html.Node node = textNodes[i]; - // Check if offset is within client rect bounds of text node's - // parent element. - final html.Element parent = node.parentNode as html.Element; - final html.Rectangle bounds = parent.getBoundingClientRect(); - final double dx = offset.dx; - final double dy = offset.dy; - if (dx >= bounds.left && - dx < bounds.right && - dy >= bounds.top && - dy < bounds.bottom) { - // We found the element bounds that contains offset. - // Calculate text position for this node. - return _countTextPosition(el.childNodes, textNodes[i]); - } - } - return 0; - } - - void _collectTextNodes(Iterable nodes, List textNodes) { - if (nodes.isEmpty) { - return; - } - final List childNodes = []; - for (html.Node node in nodes) { - if (node.nodeType == html.Node.TEXT_NODE) { - textNodes.add(node); - } - childNodes.addAll(node.childNodes); - } - _collectTextNodes(childNodes, textNodes); - } - - int _countTextPosition(List nodes, html.Node endNode) { - int position = 0; - final List stack = nodes.reversed.toList(); - while (true) { - var node = stack.removeLast(); - stack.addAll(node.childNodes.reversed); - if (node == endNode) { - break; - } - if (node.nodeType == html.Node.TEXT_NODE) { - position += node.text!.length; - } - } - return position; - } - - /// Performs clean-up after a measurement is done, preparing this ruler for - /// a future reuse. - /// - /// Call this method immediately after calling `measure*` methods for a - /// particular [paragraph]. This ruler is not reusable until [didMeasure] is - /// called. - void didMeasure() { - assert(_paragraph != null); - // Remove any rich text we set during layout for the following reasons: - // - there won't be any text for the browser to lay out when we commit the - // current frame. - // - this keeps the cost of removing content together with the measurement - // in the profile. Otherwise, the cost of removing will be paid by a - // random next paragraph measured in the future, and make the performance - // profile hard to understand. - // - // We do not do this for plain text, because replacing plain text is more - // expensive than paying the cost of the DOM mutation to clean it. - if (_paragraph!._plainText == null) { - domRenderer - ..clearDom(singleLineDimensions._element) - ..clearDom(minIntrinsicDimensions._element) - ..clearDom(constrainedDimensions._element); - } - _paragraph = null; - } - - /// Performs stateless measurement of text boxes for a given range of text. - /// - /// This method doesn't depend on [willMeasure] and [didMeasure] lifecycle - /// methods. - List measureBoxesForRange( - String plainText, - ui.ParagraphConstraints constraints, { - required int start, - required int end, - required double alignOffset, - required ui.TextDirection textDirection, - }) { - assert(!_debugIsDisposed); - assert(start >= 0 && start <= plainText.length); - assert(end >= 0 && end <= plainText.length); - assert(start <= end); - - final String before = plainText.substring(0, start); - final String rangeText = plainText.substring(start, end); - final String after = plainText.substring(end); - - final html.SpanElement rangeSpan = html.SpanElement()..text = rangeText; - - // Setup the [ruler.constrainedDimensions] element to be used for measurement. - domRenderer.clearDom(constrainedDimensions._element); - constrainedDimensions._element - ..appendText(before) - ..append(rangeSpan) - ..appendText(after); - constrainedDimensions.updateConstraintWidth(constraints.width, null); - - // Measure the rects of [rangeSpan]. - final List> clientRects = rangeSpan.getClientRects(); - final List boxes = []; - - final double maxLinesLimit = style.maxLines == null - ? double.infinity - : style.maxLines! * lineHeightDimensions!.height; - - html.Rectangle? previousRect; - for (html.Rectangle rect in clientRects) { - // If [rect] is an empty box on the same line as the previous box, don't - // include it in the result. - if (rect.top == previousRect?.top && rect.left == rect.right) { - continue; - } - // As soon as we go beyond [maxLines], stop adding boxes. - if (rect.top >= maxLinesLimit) { - break; - } + _dimensions.updateTextToSpace(); - boxes.add(ui.TextBox.fromLTRBD( - rect.left.toDouble() + alignOffset, - rect.top as double, - rect.right.toDouble() + alignOffset, - rect.bottom as double, - textDirection, - )); - previousRect = rect; - } - - // Cleanup after measuring the boxes. - domRenderer.clearDom(constrainedDimensions._element); - return boxes; - } - - /// Detaches this ruler from the DOM and makes it unusable for future - /// measurements. - /// - /// Disposed rulers should be garbage collected after calling this method. - void dispose() { - assert(() { - if (_paragraph != null) { - throw Exception('Attempted to dispose of a ruler in the middle of ' - 'measurement. This is likely a bug in the framework.'); - } - return true; - }()); - _singleLineHost.remove(); - _minIntrinsicHost.remove(); - _constrainedHost.remove(); - _lineHeightHost?.remove(); - assert(() { - _debugIsDisposed = true; - return true; - }()); + _dimensions.appendToHost(host); + rulerHost.addElement(host); + return host; } - // Bounded cache for text measurement for a particular width constraint. - Map> _measurementCache = - >{}; - // Mru list for cache. - final List _mruList = []; - static const int _cacheLimit = 2400; - // Number of items to evict when cache limit is reached. - static const int _cacheBlockFactor = 100; - // Number of constraint results per unique text item. - // This limit prevents growth during animation where the size of a container - // is changing. - static const int _constraintCacheSize = 8; - - void cacheMeasurement(EngineParagraph paragraph, MeasurementResult? item) { - final String? plainText = paragraph._plainText; - final List constraintCache = - _measurementCache[plainText] ??= []; - constraintCache.add(item); - if (constraintCache.length > _constraintCacheSize) { - constraintCache.removeAt(0); - } - _mruList.add(plainText); - if (_mruList.length > _cacheLimit) { - // Evict a range. - for (int i = 0; i < _cacheBlockFactor; i++) { - _measurementCache.remove(_mruList[i]); - } - _mruList.removeRange(0, _cacheBlockFactor); - } + html.HtmlElement _createProbe() { + final html.HtmlElement probe = html.DivElement(); + _host.append(probe); + return probe; } - - MeasurementResult? cacheLookup( - EngineParagraph paragraph, ui.ParagraphConstraints constraints) { - final String? plainText = paragraph._plainText; - if (plainText == null) { - // Multi span paragraph, do not use cache item. - return null; - } - final List? constraintCache = - _measurementCache[plainText]; - if (constraintCache == null) { - return null; - } - final int len = constraintCache.length; - for (int i = 0; i < len; i++) { - final MeasurementResult item = constraintCache[i]!; - if (item.constraintWidth == constraints.width && - item.textAlign == paragraph._textAlign && - item.textDirection == paragraph._textDirection) { - return item; - } - } - return null; - } -} - -/// The result that contains all measurements of a paragraph at the given -/// constraint width. -@immutable -class MeasurementResult { - /// The width that was given as a constraint when the paragraph was laid out. - final double constraintWidth; - - /// Whether the paragraph can fit in a single line given [constraintWidth]. - final bool isSingleLine; - - /// The amount of horizontal space the paragraph occupies. - final double width; - - /// The amount of vertical space the paragraph occupies. - final double height; - - /// {@macro dart.ui.paragraph.naturalHeight} - /// - /// When [ParagraphGeometricStyle.maxLines] is null, [naturalHeight] and - /// [height] should be equal. - final double naturalHeight; - - /// The amount of vertical space each line of the paragraph occupies. - /// - /// In some cases, measuring [lineHeight] is unnecessary, so it's nullable. If - /// present, it should be equal to [height] when [isSingleLine] is true. - final double? lineHeight; - - /// {@macro dart.ui.paragraph.minIntrinsicWidth} - final double minIntrinsicWidth; - - /// {@macro dart.ui.paragraph.maxIntrinsicWidth} - final double maxIntrinsicWidth; - - /// {@macro dart.ui.paragraph.alphabeticBaseline} - final double alphabeticBaseline; - - /// {@macro dart.ui.paragraph.ideographicBaseline} - final double ideographicBaseline; - - /// The full list of [EngineLineMetrics] that describe in detail the various metrics - /// of each laid out line. - final List? lines; - - final List placeholderBoxes; - - /// The text align value of the paragraph. - final ui.TextAlign textAlign; - - /// The text direction of the paragraph. - final ui.TextDirection textDirection; - - MeasurementResult( - this.constraintWidth, { - required this.isSingleLine, - required this.width, - required this.height, - required this.naturalHeight, - required this.lineHeight, - required this.minIntrinsicWidth, - required this.maxIntrinsicWidth, - required this.alphabeticBaseline, - required this.ideographicBaseline, - required this.lines, - required this.placeholderBoxes, - required ui.TextAlign? textAlign, - required ui.TextDirection? textDirection, - }) : assert(constraintWidth != null), // ignore: unnecessary_null_comparison - assert(isSingleLine != null), // ignore: unnecessary_null_comparison - assert(width != null), // ignore: unnecessary_null_comparison - assert(height != null), // ignore: unnecessary_null_comparison - assert(naturalHeight != null), // ignore: unnecessary_null_comparison - assert(minIntrinsicWidth != null), // ignore: unnecessary_null_comparison - assert(maxIntrinsicWidth != null), // ignore: unnecessary_null_comparison - assert(alphabeticBaseline != null), // ignore: unnecessary_null_comparison - assert(ideographicBaseline != null), // ignore: unnecessary_null_comparison - this.textAlign = textAlign ?? ui.TextAlign.start, - this.textDirection = textDirection ?? ui.TextDirection.ltr; } diff --git a/lib/web_ui/lib/src/engine/text/text_direction.dart b/lib/web_ui/lib/src/engine/text/text_direction.dart new file mode 100644 index 0000000000000..782b0b6681c4f --- /dev/null +++ b/lib/web_ui/lib/src/engine/text/text_direction.dart @@ -0,0 +1,118 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/ui.dart' as ui; + +import 'line_breaker.dart'; +import 'unicode_range.dart'; + +// This data was taken from the source code of the Closure library: +// +// - https://github.com/google/closure-library/blob/9d24a6c1809a671c2e54c328897ebeae15a6d172/closure/goog/i18n/bidi.js#L203-L234 +final UnicodePropertyLookup _textDirectionLookup = UnicodePropertyLookup( + >[ + // LTR + const UnicodeRange(kChar_A, kChar_Z, ui.TextDirection.ltr), + const UnicodeRange(kChar_a, kChar_z, ui.TextDirection.ltr), + const UnicodeRange(0x00C0, 0x00D6, ui.TextDirection.ltr), + const UnicodeRange(0x00D8, 0x00F6, ui.TextDirection.ltr), + const UnicodeRange(0x00F8, 0x02B8, ui.TextDirection.ltr), + const UnicodeRange(0x0300, 0x0590, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0x0591, 0x06EF, ui.TextDirection.rtl), + const UnicodeRange(0x06FA, 0x08FF, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0x0900, 0x1FFF, ui.TextDirection.ltr), + const UnicodeRange(0x200E, 0x200E, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0x200F, 0x200F, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0x2C00, 0xD801, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xD802, 0xD803, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xD804, 0xD839, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xD83A, 0xD83B, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xD83C, 0xDBFF, ui.TextDirection.ltr), + const UnicodeRange(0xF900, 0xFB1C, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xFB1D, 0xFDFF, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xFE00, 0xFE6F, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xFE70, 0xFEFC, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xFEFD, 0xFFFF, ui.TextDirection.ltr), + ], + null, +); + +/// Represents a block of text with a certain [ui.TextDirection]. +class DirectionalPosition { + const DirectionalPosition(this.lineBreak, this.textDirection, this.isSpaceOnly); + + final LineBreakResult lineBreak; + + final ui.TextDirection? textDirection; + + final bool isSpaceOnly; + + LineBreakType get type => lineBreak.type; + + /// Creates a copy of this [DirectionalPosition] with a different [index]. + /// + /// The type of the returned [DirectionalPosition] is set to + /// [LineBreakType.prohibited]. + DirectionalPosition copyWithIndex(int index) { + return DirectionalPosition( + LineBreakResult.sameIndex(index, LineBreakType.prohibited), + textDirection, + isSpaceOnly, + ); + } +} + +/// Finds the end of the directional block of text that starts at [start] up +/// until [end]. +/// +/// If the block goes beyond [end], the part after [end] is ignored. +DirectionalPosition getDirectionalBlockEnd( + String text, + LineBreakResult start, + LineBreakResult end, +) { + if (start.index == end.index) { + return DirectionalPosition(end, null, false); + } + + // Check if we are in a space-only block. + if (start.index == end.indexWithoutTrailingSpaces) { + return DirectionalPosition(end, null, true); + } + + final ui.TextDirection? blockDirection = _textDirectionLookup.find(text, start.index); + int i = start.index + 1; + + while (i < end.indexWithoutTrailingSpaces) { + final ui.TextDirection? direction = _textDirectionLookup.find(text, i); + if (direction != blockDirection) { + // Reached the next block. + break; + } + i++; + } + + if (i == end.indexWithoutTrailingNewlines) { + // If all that remains before [end] is new lines, let's include them in the + // block. + return DirectionalPosition(end, blockDirection, false); + } + return DirectionalPosition( + LineBreakResult.sameIndex(i, LineBreakType.prohibited), + blockDirection, + false, + ); +} diff --git a/lib/web_ui/lib/src/engine/text/unicode_range.dart b/lib/web_ui/lib/src/engine/text/unicode_range.dart index a2148fdcee35c..81d14edc402a7 100644 --- a/lib/web_ui/lib/src/engine/text/unicode_range.dart +++ b/lib/web_ui/lib/src/engine/text/unicode_range.dart @@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - -const int _kChar_0 = 48; -const int _kChar_9 = 57; -const int _kChar_A = 65; -const int _kChar_Z = 90; -const int _kChar_a = 97; -const int _kChar_z = 122; -const int _kCharBang = 33; +const int kChar_0 = 48; +const int kChar_9 = 57; +const int kChar_A = 65; +const int kChar_Z = 90; +const int kChar_a = 97; +const int kChar_z = 122; +const int kCharBang = 33; enum _ComparisonResult { inside, @@ -75,9 +72,9 @@ int combineSurrogatePair(String text, int index) { final int hi = text.codeUnitAt(index); final int lo = text.codeUnitAt(index + 1); - int x = (hi & ((1 << 6) - 1)) << 10 | lo & ((1 << 10) - 1); - int w = (hi >> 6) & ((1 << 5) - 1); - int u = w + 1; + final int x = (hi & ((1 << 6) - 1)) << 10 | lo & ((1 << 10) - 1); + final int w = (hi >> 6) & ((1 << 5) - 1); + final int u = w + 1; return u << 16 | x; } @@ -213,7 +210,7 @@ List> _unpackProperties

( i += 4; int rangeEnd; - if (packedData.codeUnitAt(i) == _kCharBang) { + if (packedData.codeUnitAt(i) == kCharBang) { rangeEnd = rangeStart; i++; } else { @@ -234,15 +231,15 @@ int _getEnumIndexFromPackedValue(int charCode) { // This has to stay in sync with [EnumValue.serialized] in // `tool/unicode_sync_script.dart`. - assert((charCode >= _kChar_A && charCode <= _kChar_Z) || - (charCode >= _kChar_a && charCode <= _kChar_z)); + assert((charCode >= kChar_A && charCode <= kChar_Z) || + (charCode >= kChar_a && charCode <= kChar_z)); // Uppercase letters were assigned to the first 26 enum values. - if (charCode <= _kChar_Z) { - return charCode - _kChar_A; + if (charCode <= kChar_Z) { + return charCode - kChar_A; } // Lowercase letters were assigned to enum values above 26. - return 26 + charCode - _kChar_a; + return 26 + charCode - kChar_a; } int _consumeInt(String packedData, int index) { @@ -264,12 +261,12 @@ int _consumeInt(String packedData, int index) { /// Does the same thing as [int.parse(str, 36)] but takes only a single /// character as a [charCode] integer. int _getIntFromCharCode(int charCode) { - assert((charCode >= _kChar_0 && charCode <= _kChar_9) || - (charCode >= _kChar_a && charCode <= _kChar_z)); + assert((charCode >= kChar_0 && charCode <= kChar_9) || + (charCode >= kChar_a && charCode <= kChar_z)); - if (charCode <= _kChar_9) { - return charCode - _kChar_0; + if (charCode <= kChar_9) { + return charCode - kChar_0; } // "a" starts from 10 and remaining letters go up from there. - return charCode - _kChar_a + 10; + return charCode - kChar_a + 10; } diff --git a/lib/web_ui/lib/src/engine/text/word_break_properties.dart b/lib/web_ui/lib/src/engine/text/word_break_properties.dart index 53acf3a267b8b..6c94e513ece2e 100644 --- a/lib/web_ui/lib/src/engine/text/word_break_properties.dart +++ b/lib/web_ui/lib/src/engine/text/word_break_properties.dart @@ -12,8 +12,7 @@ // # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. // # For terms of use, see http://www.unicode.org/terms_of_use.html -// @dart = 2.10 -part of engine; +import 'unicode_range.dart'; /// For an explanation of these enum values, see: /// diff --git a/lib/web_ui/lib/src/engine/text/word_breaker.dart b/lib/web_ui/lib/src/engine/text/word_breaker.dart index 1904b89ca8c15..603ff428a5999 100644 --- a/lib/web_ui/lib/src/engine/text/word_breaker.dart +++ b/lib/web_ui/lib/src/engine/text/word_breaker.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import '../util.dart'; +import 'word_break_properties.dart'; class _FindBreakDirection { static const _FindBreakDirection forward = _FindBreakDirection(step: 1); @@ -16,6 +16,10 @@ class _FindBreakDirection { /// [WordBreaker] exposes static methods to identify word boundaries. abstract class WordBreaker { + // This class is not meant to be instantiated or extended; this constructor + // prevents instantiation and extension. + WordBreaker._(); + /// It starts from [index] and tries to find the next word boundary in [text]. static int nextBreakIndex(String text, int index) => _findBreakIndex(_FindBreakDirection.forward, text, index); @@ -55,8 +59,8 @@ abstract class WordBreaker { return false; } - final WordCharProperty? immediateRight = wordLookup.find(text, index); - WordCharProperty? immediateLeft = wordLookup.find(text, index - 1); + final WordCharProperty immediateRight = wordLookup.find(text, index); + WordCharProperty immediateLeft = wordLookup.find(text, index - 1); // Do not break within CRLF. // WB3: CR × LF @@ -85,7 +89,7 @@ abstract class WordBreaker { } // WB3c: ZWJ × \p{Extended_Pictographic} - // TODO(flutter_web): What's the right way to implement this? + // TODO(mdebbar): What's the right way to implement this? // Keep horizontal whitespace together. // WB3d: WSegSpace × WSegSpace diff --git a/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart b/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart index 0ce6f3b426698..3059f63ee110a 100644 --- a/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart +++ b/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; - /// Maps AutofillHints from the framework to the autofill hints that is used for /// browsers. /// See: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/autofill.dart @@ -19,7 +16,7 @@ class BrowserAutofillHints { final Map _flutterToEngineMap; BrowserAutofillHints._() - : _flutterToEngineMap = { + : _flutterToEngineMap = { 'birthday': 'bday', 'birthdayDay': 'bday-day', 'birthdayMonth': 'bday-month', diff --git a/lib/web_ui/lib/src/engine/text_editing/input_type.dart b/lib/web_ui/lib/src/engine/text_editing/input_type.dart index af65b91a38a41..23fb82d16f8ff 100644 --- a/lib/web_ui/lib/src/engine/text_editing/input_type.dart +++ b/lib/web_ui/lib/src/engine/text_editing/input_type.dart @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import '../browser_detection.dart'; /// Various types of inputs used in text fields. /// /// These types are coming from Flutter's [TextInputType]. Currently, we don't /// support all the types. We fallback to [EngineInputType.text] when Flutter /// sends a type that isn't supported. -// TODO(flutter_web): Support more types. +// TODO(mdebbar): Support more types. abstract class EngineInputType { const EngineInputType(); @@ -26,12 +27,17 @@ abstract class EngineInputType { return url; case 'TextInputType.multiline': return multiline; + case 'TextInputType.none': + return none; case 'TextInputType.text': default: return text; } } + /// No text input. + static const NoTextInputType none = NoTextInputType(); + /// Single-line text input type. static const TextInputType text = TextInputType(); @@ -77,18 +83,27 @@ abstract class EngineInputType { // Only apply `inputmode` in mobile browsers so that the right virtual // keyboard shows up. if (operatingSystem == OperatingSystem.iOs || - operatingSystem == OperatingSystem.android) { + operatingSystem == OperatingSystem.android || + inputmodeAttribute == EngineInputType.none.inputmodeAttribute) { domElement.setAttribute('inputmode', inputmodeAttribute!); } } } +/// No text input. +class NoTextInputType extends EngineInputType { + const NoTextInputType(); + + @override + String get inputmodeAttribute => 'none'; +} + /// Single-line text input type. class TextInputType extends EngineInputType { const TextInputType(); @override - final String inputmodeAttribute = 'text'; + String get inputmodeAttribute => 'text'; } /// Numeric input type. @@ -98,18 +113,18 @@ class NumberInputType extends EngineInputType { const NumberInputType(); @override - final String inputmodeAttribute = 'numeric'; + String get inputmodeAttribute => 'numeric'; } /// Decimal input type. /// /// Input keyboard with containing the digits 0–9 and a decimal separator. -/// Seperator can be `.`, `,` depending on the locale. +/// Separator can be `.`, `,` depending on the locale. class DecimalInputType extends EngineInputType { const DecimalInputType(); @override - final String inputmodeAttribute = 'decimal'; + String get inputmodeAttribute => 'decimal'; } /// Phone number input type. @@ -117,7 +132,7 @@ class PhoneInputType extends EngineInputType { const PhoneInputType(); @override - final String inputmodeAttribute = 'tel'; + String get inputmodeAttribute => 'tel'; } /// Email address input type. @@ -125,7 +140,7 @@ class EmailInputType extends EngineInputType { const EmailInputType(); @override - final String inputmodeAttribute = 'email'; + String get inputmodeAttribute => 'email'; } /// URL input type. @@ -133,7 +148,7 @@ class UrlInputType extends EngineInputType { const UrlInputType(); @override - final String inputmodeAttribute = 'url'; + String get inputmodeAttribute => 'url'; } /// Multi-line text input type. @@ -141,7 +156,7 @@ class MultilineInputType extends EngineInputType { const MultilineInputType(); @override - final String? inputmodeAttribute = null; + String? get inputmodeAttribute => null; @override bool get submitActionOnEnter => false; diff --git a/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart b/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart index a9f04137bb0bb..12d854ab80b2a 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:html' as html; + +import '../browser_detection.dart'; /// Controls the capitalization of the text. /// @@ -38,7 +39,7 @@ class TextCapitalizationConfig { : textCapitalization = TextCapitalization.none; const TextCapitalizationConfig.fromInputConfiguration(String inputConfiguration) - : this.textCapitalization = + : textCapitalization = inputConfiguration == 'TextCapitalization.words' ? TextCapitalization.words : inputConfiguration == 'TextCapitalization.characters' @@ -61,7 +62,7 @@ class TextCapitalizationConfig { String autocapitalize = ''; switch (textCapitalization) { case TextCapitalization.words: - // TODO: There is a bug for `words` level capitalization in IOS now. + // TODO(mdebbar): There is a bug for `words` level capitalization in IOS now. // For now go back to default. Remove the check after bug is resolved. // https://bugs.webkit.org/show_bug.cgi?id=148504 if (browserEngine == BrowserEngine.webkit) { @@ -82,10 +83,10 @@ class TextCapitalizationConfig { break; } if (domElement is html.InputElement) { - html.InputElement element = domElement; + final html.InputElement element = domElement; element.setAttribute('autocapitalize', autocapitalize); } else if (domElement is html.TextAreaElement) { - html.TextAreaElement element = domElement; + final html.TextAreaElement element = domElement; element.setAttribute('autocapitalize', autocapitalize); } } diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 5d298bed3df66..99860c3a02f67 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -2,17 +2,52 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import '../browser_detection.dart'; +import '../dom_renderer.dart'; +import '../host_node.dart'; +import '../platform_dispatcher.dart'; +import '../semantics.dart'; +import '../services.dart'; +import '../text/paragraph.dart'; +import '../util.dart'; +import 'autofill_hint.dart'; +import 'input_type.dart'; +import 'text_capitalization.dart'; /// Make the content editable span visible to facilitate debugging. bool _debugVisibleTextEditing = false; +/// Set this to `true` to print when text input commands are scheduled and run. +bool _debugPrintTextInputCommands = false; + /// The `keyCode` of the "Enter" key. const int _kReturnKeyCode = 13; +/// Blink and Webkit engines, bring an overlay on top of the text field when it +/// is autofilled. +bool browserHasAutofillOverlay() => + browserEngine == BrowserEngine.blink || + browserEngine == BrowserEngine.samsung || + browserEngine == BrowserEngine.webkit; + +/// `transparentTextEditing` class is configured to make the autofill overlay +/// transparent. +const String transparentTextEditingClass = 'transparentTextEditing'; + void _emptyCallback(dynamic _) {} +/// The default [HostNode] that hosts all DOM required for text editing when a11y is not enabled. +@visibleForTesting +HostNode get defaultTextEditingRoot => domRenderer.glassPaneShadow!; + /// These style attributes are constant throughout the life time of an input /// element. /// @@ -39,7 +74,11 @@ void _setStaticStyleAttributes(html.HtmlElement domElement) { ..overflow = 'hidden' ..transformOrigin = '0 0 0'; - /// This property makes the input's blinking cursor transparent. + if (browserHasAutofillOverlay()) { + domElement.classes.add(transparentTextEditingClass); + } + + // This property makes the input's blinking cursor transparent. elementStyle.setProperty('caret-color', 'transparent'); if (_debugVisibleTextEditing) { @@ -80,6 +119,10 @@ void _hideAutofillElements(html.HtmlElement domElement, ..left = '-9999px'; } + if (browserHasAutofillOverlay()) { + domElement.classes.add(transparentTextEditingClass); + } + /// This property makes the input's blinking cursor transparent. elementStyle.setProperty('caret-color', 'transparent'); } @@ -131,7 +174,7 @@ class EngineAutofillForm { formElement.noValidate = true; formElement.method = 'post'; formElement.action = '#'; - formElement.addEventListener('submit', (e) { + formElement.addEventListener('submit', (html.Event e) { e.preventDefault(); }); @@ -139,27 +182,31 @@ class EngineAutofillForm { // We keep the ids in a list then sort them later, in case the text fields' // locations are re-ordered on the framework side. - final List ids = List.empty(growable: true); + final List ids = List.empty(growable: true); // The focused text editing element will not be created here. final AutofillInfo focusedElement = AutofillInfo.fromFrameworkMessage(focusedElementAutofill); if (fields != null) { - for (Map field in fields.cast>()) { - final Map autofillInfo = field['autofill']; + for (final Map field in + fields.cast>()) { + final Map autofillInfo = field.readJson('autofill'); final AutofillInfo autofill = AutofillInfo.fromFrameworkMessage( - autofillInfo, - textCapitalization: TextCapitalizationConfig.fromInputConfiguration( - field['textCapitalization'])); + autofillInfo, + textCapitalization: TextCapitalizationConfig.fromInputConfiguration( + field.readString('textCapitalization'), + ), + ); ids.add(autofill.uniqueIdentifier); if (autofill.uniqueIdentifier != focusedElement.uniqueIdentifier) { - EngineInputType engineInputType = - EngineInputType.fromName(field['inputType']['name']); + final EngineInputType engineInputType = EngineInputType.fromName( + field.readJson('inputType').readString('name'), + ); - html.HtmlElement htmlElement = engineInputType.createDomElement(); + final html.HtmlElement htmlElement = engineInputType.createDomElement(); autofill.editingState.applyToDomElement(htmlElement); autofill.applyToDomElement(htmlElement); _hideAutofillElements(htmlElement); @@ -177,7 +224,7 @@ class EngineAutofillForm { ids.sort(); final StringBuffer idBuffer = StringBuffer(); - // Add a seperator between element identifiers. + // Add a separator between element identifiers. for (final String id in ids) { if (idBuffer.length > 0) { idBuffer.write('*'); @@ -189,7 +236,7 @@ class EngineAutofillForm { // If a form with the same Autofill elements is already on the dom, remove // it from DOM. - html.FormElement? form = formsOnTheDom[formIdentifier]; + final html.FormElement? form = formsOnTheDom[formIdentifier]; form?.remove(); // In order to submit the form when Framework sends a `TextInput.commit` @@ -211,11 +258,11 @@ class EngineAutofillForm { void placeForm(html.HtmlElement mainTextEditingElement) { formElement.append(mainTextEditingElement); - domRenderer.glassPaneElement!.append(formElement); + defaultTextEditingRoot.append(formElement); } void storeForm() { - formsOnTheDom[formIdentifier] = this.formElement; + formsOnTheDom[formIdentifier] = formElement; _hideAutofillElements(formElement, isOffScreen: true); } @@ -224,55 +271,57 @@ class EngineAutofillForm { /// Registering to the listeners could have been done in the constructor. /// On the other hand, overall for text editing there is already a lifecycle /// for subscriptions: All the subscriptions of the DOM elements are to the - /// `_subscriptions` property of [DefaultTextEditingStrategy]. + /// `subscriptions` property of [DefaultTextEditingStrategy]. /// [TextEditingStrategy] manages all subscription lifecyle. All /// listeners with no exceptions are added during /// [TextEditingStrategy.addEventHandlers] method call and all /// listeners are removed during [TextEditingStrategy.disable] method call. List> addInputEventListeners() { - Iterable keys = elements!.keys; - List> subscriptions = + final Iterable keys = elements!.keys; + final List> subscriptions = >[]; - keys.forEach((String key) { - final html.Element element = elements![key]!; - subscriptions.add(element.onInput.listen((html.Event e) { - if (items![key] == null) { - throw StateError( - 'Autofill would not work withuot Autofill value set'); - } else { - final AutofillInfo autofillInfo = items![key]!; - _handleChange(element, autofillInfo); - } - })); - }); + + void addSubscriptionForKey(String key) { + final html.Element element = elements![key]!; + subscriptions.add( + element.onInput.listen((html.Event e) { + if (items![key] == null) { + throw StateError( + 'Autofill would not work withuot Autofill value set'); + } else { + final AutofillInfo autofillInfo = items![key]!; + handleChange(element, autofillInfo); + } + }) + ); + } + + keys.forEach(addSubscriptionForKey); return subscriptions; } - void _handleChange(html.Element domElement, AutofillInfo autofillInfo) { - EditingState newEditingState = EditingState.fromDomElement( - domElement as html.HtmlElement?, - textCapitalization: autofillInfo.textCapitalization); + void handleChange(html.Element domElement, AutofillInfo autofillInfo) { + final EditingState newEditingState = EditingState.fromDomElement( + domElement as html.HtmlElement?); _sendAutofillEditingState(autofillInfo.uniqueIdentifier, newEditingState); } /// Sends the 'TextInputClient.updateEditingStateWithTag' message to the framework. void _sendAutofillEditingState(String? tag, EditingState editingState) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/textinput', - const JSONMethodCodec().encodeMethodCall( - MethodCall( - 'TextInputClient.updateEditingStateWithTag', - [ - 0, - {tag: editingState.toFlutter()} - ], - ), + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/textinput', + const JSONMethodCodec().encodeMethodCall( + MethodCall( + 'TextInputClient.updateEditingStateWithTag', + [ + 0, + {tag: editingState.toFlutter()} + ], ), - _emptyCallback, - ); - } + ), + _emptyCallback, + ); } } @@ -303,7 +352,7 @@ class AutofillInfo { /// to upper case. /// /// This value is not necessary for autofilling the focused element since - /// [DefaultTextEditingStrategy._inputConfiguration] already has this + /// [DefaultTextEditingStrategy.inputConfiguration] already has this /// information. /// /// On the other hand for the multi element forms, for the input elements @@ -321,22 +370,23 @@ class AutofillInfo { {TextCapitalizationConfig textCapitalization = const TextCapitalizationConfig.defaultCapitalization()}) { assert(autofill != null); // ignore: unnecessary_null_comparison - final String uniqueIdentifier = autofill['uniqueIdentifier']!; - final List hintsList = autofill['hints']; + final String uniqueIdentifier = autofill.readString('uniqueIdentifier'); + final List hintsList = autofill.readList('hints'); final EditingState editingState = - EditingState.fromFrameworkMessage(autofill['editingValue']); + EditingState.fromFrameworkMessage(autofill.readJson('editingValue')); return AutofillInfo( - uniqueIdentifier: uniqueIdentifier, - hint: BrowserAutofillHints.instance.flutterToEngine(hintsList[0]), - editingState: editingState, - textCapitalization: textCapitalization); + uniqueIdentifier: uniqueIdentifier, + hint: BrowserAutofillHints.instance.flutterToEngine(hintsList[0] as String), + editingState: editingState, + textCapitalization: textCapitalization, + ); } void applyToDomElement(html.HtmlElement domElement, {bool focusedElement = false}) { domElement.id = hint; if (domElement is html.InputElement) { - html.InputElement element = domElement; + final html.InputElement element = domElement; element.name = hint; element.id = hint; element.autocomplete = hint; @@ -346,7 +396,7 @@ class AutofillInfo { element.type = 'text'; } } else if (domElement is html.TextAreaElement) { - html.TextAreaElement element = domElement; + final html.TextAreaElement element = domElement; element.name = hint; element.id = hint; element.setAttribute('autocomplete', hint); @@ -355,9 +405,12 @@ class AutofillInfo { } /// The current text and selection state of a text field. -@visibleForTesting class EditingState { - EditingState({this.text, this.baseOffset = 0, this.extentOffset = 0}); + EditingState({this.text, int? baseOffset, int? extentOffset}) : + // Don't allow negative numbers. Pick the smallest selection index for base. + baseOffset = math.max(0, math.min(baseOffset ?? 0, extentOffset ?? 0)), + // Don't allow negative numbers. Pick the greatest selection index for extent. + extentOffset = math.max(0, math.max(baseOffset ?? 0, extentOffset ?? 0)); /// Creates an [EditingState] instance using values from an editing state Map /// coming from Flutter. @@ -380,14 +433,15 @@ class EditingState { /// valid selection range for input DOM elements. factory EditingState.fromFrameworkMessage( Map flutterEditingState) { - final int selectionBase = flutterEditingState['selectionBase']; - final int selectionExtent = flutterEditingState['selectionExtent']; - final String? text = flutterEditingState['text']; + final int selectionBase = flutterEditingState.readInt('selectionBase'); + final int selectionExtent = flutterEditingState.readInt('selectionExtent'); + final String? text = flutterEditingState.tryString('text'); return EditingState( - text: text, - baseOffset: math.max(0, selectionBase), - extentOffset: math.max(0, selectionExtent)); + text: text, + baseOffset: selectionBase, + extentOffset: selectionExtent, + ); } /// Creates an [EditingState] instance using values from the editing element @@ -395,17 +449,15 @@ class EditingState { /// /// [domElement] can be a [InputElement] or a [TextAreaElement] depending on /// the [InputType] of the text field. - factory EditingState.fromDomElement(html.HtmlElement? domElement, - {TextCapitalizationConfig textCapitalization = - const TextCapitalizationConfig.defaultCapitalization()}) { + factory EditingState.fromDomElement(html.HtmlElement? domElement) { if (domElement is html.InputElement) { - html.InputElement element = domElement; + final html.InputElement element = domElement; return EditingState( text: element.value, baseOffset: element.selectionStart, extentOffset: element.selectionEnd); } else if (domElement is html.TextAreaElement) { - html.TextAreaElement element = domElement; + final html.TextAreaElement element = domElement; return EditingState( text: element.value, baseOffset: element.selectionStart, @@ -464,15 +516,42 @@ class EditingState { /// /// [domElement] can be a [InputElement] or a [TextAreaElement] depending on /// the [InputType] of the text field. + /// + /// This should only be used by focused elements only, because only focused + /// elements can have their text selection range set. Attempting to set + /// selection range on a non-focused element will cause it to request focus. + /// + /// See also: + /// + /// * [applyTextToDomElement], which is used for non-focused elements. void applyToDomElement(html.HtmlElement? domElement) { if (domElement is html.InputElement) { - html.InputElement element = domElement; + final html.InputElement element = domElement; element.value = text; element.setSelectionRange(baseOffset!, extentOffset!); } else if (domElement is html.TextAreaElement) { - html.TextAreaElement element = domElement; + final html.TextAreaElement element = domElement; element.value = text; element.setSelectionRange(baseOffset!, extentOffset!); + } else { + throw UnsupportedError('Unsupported DOM element type: <${domElement?.tagName}> (${domElement.runtimeType})'); + } + } + + /// Applies the [text] to the [domElement]. + /// + /// This is used by non-focused elements. + /// + /// See also: + /// + /// * [applyToDomElement], which is used for focused elements. + void applyTextToDomElement(html.HtmlElement? domElement) { + if (domElement is html.InputElement) { + final html.InputElement element = domElement; + element.value = text; + } else if (domElement is html.TextAreaElement) { + final html.TextAreaElement element = domElement; + element.value = text; } else { throw UnsupportedError('Unsupported DOM element type'); } @@ -501,24 +580,25 @@ class InputConfiguration { InputConfiguration.fromFrameworkMessage( Map flutterInputConfiguration) : inputType = EngineInputType.fromName( - flutterInputConfiguration['inputType']['name'], - isDecimal: flutterInputConfiguration['inputType']['decimal'] ?? false, + flutterInputConfiguration.readJson('inputType').readString('name'), + isDecimal: flutterInputConfiguration.readJson('inputType').tryBool('decimal') ?? false, ), inputAction = - flutterInputConfiguration['inputAction'] ?? 'TextInputAction.done', - obscureText = flutterInputConfiguration['obscureText'] ?? false, - readOnly = flutterInputConfiguration['readOnly'] ?? false, - autocorrect = flutterInputConfiguration['autocorrect'] ?? true, + flutterInputConfiguration.tryString('inputAction') ?? 'TextInputAction.done', + obscureText = flutterInputConfiguration.tryBool('obscureText') ?? false, + readOnly = flutterInputConfiguration.tryBool('readOnly') ?? false, + autocorrect = flutterInputConfiguration.tryBool('autocorrect') ?? true, textCapitalization = TextCapitalizationConfig.fromInputConfiguration( - flutterInputConfiguration['textCapitalization'], + flutterInputConfiguration.readString('textCapitalization'), ), autofill = flutterInputConfiguration.containsKey('autofill') ? AutofillInfo.fromFrameworkMessage( - flutterInputConfiguration['autofill']) + flutterInputConfiguration.readJson('autofill')) : null, autofillGroup = EngineAutofillForm.fromFrameworkMessage( - flutterInputConfiguration['autofill'], - flutterInputConfiguration['fields']); + flutterInputConfiguration.tryJson('autofill'), + flutterInputConfiguration.tryList('fields'), + ); /// The type of information being edited in the input control. final EngineInputType inputType; @@ -550,8 +630,8 @@ class InputConfiguration { final TextCapitalizationConfig textCapitalization; } -typedef _OnChangeCallback = void Function(EditingState? editingState); -typedef _OnActionCallback = void Function(String? inputAction); +typedef OnChangeCallback = void Function(EditingState? editingState); +typedef OnActionCallback = void Function(String? inputAction); /// Provides HTML DOM functionality for editable text. /// @@ -560,8 +640,8 @@ typedef _OnActionCallback = void Function(String? inputAction); abstract class TextEditingStrategy { void initializeTextEditing( InputConfiguration inputConfig, { - required _OnChangeCallback onChange, - required _OnActionCallback onAction, + required OnChangeCallback onChange, + required OnActionCallback onAction, }); /// Sets the initial placement of the DOM element on the UI. @@ -612,13 +692,13 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy { @override void placeElement() { if (hasAutofillGroup) { - _geometry?.applyToDomElement(focusedFormElement!); + geometry?.applyToDomElement(focusedFormElement!); placeForm(); // Set the last editing state if it exists, this is critical for a // users ongoing work to continue uninterrupted when there is an update to // the transform. - if (_lastEditingState != null) { - _lastEditingState!.applyToDomElement(domElement); + if (lastEditingState != null) { + lastEditingState!.applyToDomElement(domElement); } // On Chrome, when a form is focused, it opens an autofill menu // immediately. @@ -631,9 +711,9 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy { // does not appear on top-left of the page. // Refocus on the elements after applying the geometry. focusedFormElement!.focus(); - domElement.focus(); + activeDomElement.focus(); } else { - _geometry?.applyToDomElement(domElement); + geometry?.applyToDomElement(activeDomElement); } } } @@ -664,7 +744,7 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy { /// Making an extra `focus` request causes flickering in Safari. @override void placeElement() { - _geometry?.applyToDomElement(domElement); + geometry?.applyToDomElement(activeDomElement); if (hasAutofillGroup) { placeForm(); // On Safari Desktop, when a form is focused, it opens an autofill menu @@ -681,16 +761,16 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy { // users ongoing work to continue uninterrupted when there is an update to // the transform. // If domElement is not focused cursor location will not be correct. - domElement.focus(); - if (_lastEditingState != null) { - _lastEditingState!.applyToDomElement(domElement); + activeDomElement.focus(); + if (lastEditingState != null) { + lastEditingState!.applyToDomElement(activeDomElement); } } } @override void initializeElementPlacement() { - domElement.focus(); + activeDomElement.focus(); } } @@ -720,87 +800,98 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { DefaultTextEditingStrategy(this.owner); - @visibleForTesting bool isEnabled = false; - html.HtmlElement get domElement => _domElement!; - set domElement(html.HtmlElement element) { - _domElement = element; - } + /// The DOM element used for editing, if any. + html.HtmlElement? domElement; - html.HtmlElement? _domElement; + /// Same as [domElement] but null-checked. + /// + /// This must only be called in places that know for sure that a DOM element + /// is currently available for editing. + html.HtmlElement get activeDomElement { + assert( + domElement != null, + 'The DOM element of this text editing strategy is not currently active.', + ); + return domElement!; + } - late InputConfiguration _inputConfiguration; - EditingState? _lastEditingState; + late InputConfiguration inputConfiguration; + EditingState? lastEditingState; /// Styles associated with the editable text. - EditableTextStyle? _style; + EditableTextStyle? style; /// Size and transform of the editable text on the page. - EditableTextGeometry? _geometry; + EditableTextGeometry? geometry; - _OnChangeCallback? _onChange; - _OnActionCallback? _onAction; + OnChangeCallback? onChange; + OnActionCallback? onAction; - final List> _subscriptions = + final List> subscriptions = >[]; - bool get hasAutofillGroup => _inputConfiguration.autofillGroup != null; + bool get hasAutofillGroup => inputConfiguration.autofillGroup != null; /// Whether the focused input element is part of a form. bool get appendedToForm => _appendedToForm; bool _appendedToForm = false; html.FormElement? get focusedFormElement => - _inputConfiguration.autofillGroup?.formElement; + inputConfiguration.autofillGroup?.formElement; @override void initializeTextEditing( InputConfiguration inputConfig, { - required _OnChangeCallback onChange, - required _OnActionCallback onAction, + required OnChangeCallback onChange, + required OnActionCallback onAction, }) { assert(!isEnabled); - _domElement = inputConfig.inputType.createDomElement(); - _applyConfiguration(inputConfig); + domElement = inputConfig.inputType.createDomElement(); + applyConfiguration(inputConfig); - _setStaticStyleAttributes(domElement); - _style?.applyToDomElement(domElement); + _setStaticStyleAttributes(activeDomElement); + style?.applyToDomElement(activeDomElement); if (!hasAutofillGroup) { // If there is an Autofill Group the `FormElement`, it will be appended to the // DOM later, when the first location information arrived. // Otherwise, on Blink based Desktop browsers, the autofill menu appears // on top left of the screen. - domRenderer.glassPaneElement!.append(domElement); + defaultTextEditingRoot.append(activeDomElement); _appendedToForm = false; } initializeElementPlacement(); isEnabled = true; - _onChange = onChange; - _onAction = onAction; + this.onChange = onChange; + this.onAction = onAction; } - void _applyConfiguration(InputConfiguration config) { - _inputConfiguration = config; + void applyConfiguration(InputConfiguration config) { + inputConfiguration = config; if (config.readOnly) { - domElement.setAttribute('readonly', 'readonly'); + activeDomElement.setAttribute('readonly', 'readonly'); } else { - domElement.removeAttribute('readonly'); + activeDomElement.removeAttribute('readonly'); } if (config.obscureText) { - domElement.setAttribute('type', 'password'); + activeDomElement.setAttribute('type', 'password'); } - config.autofill?.applyToDomElement(domElement, focusedElement: true); + if (config.inputType == EngineInputType.none) { + activeDomElement.setAttribute('inputmode', 'none'); + } + + config.autofill?.applyToDomElement(activeDomElement, focusedElement: true); final String autocorrectValue = config.autocorrect ? 'on' : 'off'; - domElement.setAttribute('autocorrect', autocorrectValue); + activeDomElement.setAttribute('autocorrect', autocorrectValue); } @override @@ -810,41 +901,40 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { @override void addEventHandlers() { - if (_inputConfiguration.autofillGroup != null) { - _subscriptions - .addAll(_inputConfiguration.autofillGroup!.addInputEventListeners()); + if (inputConfiguration.autofillGroup != null) { + subscriptions + .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - _subscriptions.add(domElement.onInput.listen(_handleChange)); + subscriptions.add(activeDomElement.onInput.listen(handleChange)); - _subscriptions.add(domElement.onKeyDown.listen(_maybeSendAction)); + subscriptions.add(activeDomElement.onKeyDown.listen(maybeSendAction)); - _subscriptions.add(html.document.onSelectionChange.listen(_handleChange)); + subscriptions.add(html.document.onSelectionChange.listen(handleChange)); - // Refocus on the domElement after blur, so that user can keep editing the + // Refocus on the activeDomElement after blur, so that user can keep editing the // text field. - _subscriptions.add(domElement.onBlur.listen((_) { - domElement.focus(); + subscriptions.add(activeDomElement.onBlur.listen((_) { + activeDomElement.focus(); })); preventDefaultForMouseEvents(); } @override - void updateElementPlacement(EditableTextGeometry geometry) { - _geometry = geometry; + void updateElementPlacement(EditableTextGeometry textGeometry) { + geometry = textGeometry; if (isEnabled) { placeElement(); } } - @mustCallSuper @override - void updateElementStyle(EditableTextStyle style) { - _style = style; + void updateElementStyle(EditableTextStyle textStyle) { + style = textStyle; if (isEnabled) { - _style!.applyToDomElement(domElement); + textStyle.applyToDomElement(activeDomElement); } } @@ -853,66 +943,64 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { assert(isEnabled); isEnabled = false; - _lastEditingState = null; - _style = null; - _geometry = null; + lastEditingState = null; + style = null; + geometry = null; - for (int i = 0; i < _subscriptions.length; i++) { - _subscriptions[i].cancel(); + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].cancel(); } - _subscriptions.clear(); + subscriptions.clear(); // If focused element is a part of a form, it needs to stay on the DOM // until the autofill context of the form is finalized. // More details on `TextInput.finishAutofillContext` call. if (_appendedToForm && - _inputConfiguration.autofillGroup?.formElement != null) { + inputConfiguration.autofillGroup?.formElement != null) { // Subscriptions are removed, listeners won't be triggered. - domElement.blur(); - _hideAutofillElements(domElement, isOffScreen: true); - _inputConfiguration.autofillGroup?.storeForm(); + activeDomElement.blur(); + _hideAutofillElements(activeDomElement, isOffScreen: true); + inputConfiguration.autofillGroup?.storeForm(); } else { - domElement.remove(); + activeDomElement.remove(); } - _domElement = null; + domElement = null; } - @mustCallSuper @override void setEditingState(EditingState? editingState) { - _lastEditingState = editingState; + lastEditingState = editingState; if (!isEnabled || !editingState!.isValid) { return; } - _lastEditingState!.applyToDomElement(domElement); + lastEditingState!.applyToDomElement(domElement); } void placeElement() { - domElement.focus(); + activeDomElement.focus(); } void placeForm() { - _inputConfiguration.autofillGroup!.placeForm(domElement); + inputConfiguration.autofillGroup!.placeForm(activeDomElement); _appendedToForm = true; } - void _handleChange(html.Event event) { + void handleChange(html.Event event) { assert(isEnabled); - EditingState newEditingState = EditingState.fromDomElement(domElement, - textCapitalization: _inputConfiguration.textCapitalization); + final EditingState newEditingState = EditingState.fromDomElement(activeDomElement); - if (newEditingState != _lastEditingState) { - _lastEditingState = newEditingState; - _onChange!(_lastEditingState); + if (newEditingState != lastEditingState) { + lastEditingState = newEditingState; + onChange!(lastEditingState); } } - void _maybeSendAction(html.Event event) { + void maybeSendAction(html.Event event) { if (event is html.KeyboardEvent) { - if (_inputConfiguration.inputType.submitActionOnEnter && + if (inputConfiguration.inputType.submitActionOnEnter && event.keyCode == _kReturnKeyCode) { event.preventDefault(); - _onAction!(_inputConfiguration.inputAction); + onAction!(inputConfiguration.inputAction); } } } @@ -927,8 +1015,8 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { /// - Selection changes. void enable( InputConfiguration inputConfig, { - required _OnChangeCallback onChange, - required _OnActionCallback onAction, + required OnChangeCallback onChange, + required OnActionCallback onAction, }) { assert(!isEnabled); @@ -936,12 +1024,12 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { addEventHandlers(); - if (_lastEditingState != null) { - setEditingState(this._lastEditingState); + if (lastEditingState != null) { + setEditingState(lastEditingState); } // Re-focuses after setting editing state. - domElement.focus(); + activeDomElement.focus(); } /// Prevent default behavior for mouse down, up and move. @@ -950,15 +1038,15 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy { /// selection conflicts with selection sent from the framework, which creates /// flickering during selection by mouse. void preventDefaultForMouseEvents() { - _subscriptions.add(domElement.onMouseDown.listen((_) { + subscriptions.add(activeDomElement.onMouseDown.listen((_) { _.preventDefault(); })); - _subscriptions.add(domElement.onMouseUp.listen((_) { + subscriptions.add(activeDomElement.onMouseUp.listen((_) { _.preventDefault(); })); - _subscriptions.add(domElement.onMouseMove.listen((_) { + subscriptions.add(activeDomElement.onMouseMove.listen((_) { _.preventDefault(); })); } @@ -993,8 +1081,7 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { /// in iOS is set to correct place, 100ms after focus. We use this timer for /// timing this delay. Timer? _positionInputElementTimer; - static const Duration _delayBeforePlacement = - const Duration(milliseconds: 100); + static const Duration _delayBeforePlacement = Duration(milliseconds: 100); /// Whether or not the input element can be positioned at this point in time. /// @@ -1012,16 +1099,16 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void initializeTextEditing( InputConfiguration inputConfig, { - required _OnChangeCallback onChange, - required _OnActionCallback onAction, + required OnChangeCallback onChange, + required OnActionCallback onAction, }) { super.initializeTextEditing(inputConfig, onChange: onChange, onAction: onAction); - inputConfig.inputType.configureInputMode(domElement); + inputConfig.inputType.configureInputMode(activeDomElement); if (hasAutofillGroup) { placeForm(); } - inputConfig.textCapitalization.setAutocapitalizeAttribute(domElement); + inputConfig.textCapitalization.setAutocapitalizeAttribute(activeDomElement); } @override @@ -1029,46 +1116,56 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { /// Position the element outside of the page before focusing on it. This is /// useful for not triggering a scroll when iOS virtual keyboard is /// coming up. - domElement.style.transform = 'translate(-9999px, -9999px)'; + activeDomElement.style.transform = 'translate(-9999px, -9999px)'; _canPosition = false; } @override void addEventHandlers() { - if (_inputConfiguration.autofillGroup != null) { - _subscriptions - .addAll(_inputConfiguration.autofillGroup!.addInputEventListeners()); + if (inputConfiguration.autofillGroup != null) { + subscriptions + .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - _subscriptions.add(domElement.onInput.listen(_handleChange)); + subscriptions.add(activeDomElement.onInput.listen(handleChange)); - _subscriptions.add(domElement.onKeyDown.listen(_maybeSendAction)); + subscriptions.add(activeDomElement.onKeyDown.listen(maybeSendAction)); - _subscriptions.add(html.document.onSelectionChange.listen(_handleChange)); + subscriptions.add(html.document.onSelectionChange.listen(handleChange)); // Position the DOM element after it is focused. - _subscriptions.add(domElement.onFocus.listen((_) { + subscriptions.add(activeDomElement.onFocus.listen((_) { // Cancel previous timer if exists. _schedulePlacement(); })); _addTapListener(); - // On iOS, blur is trigerred if the virtual keyboard is closed or the - // browser is sent to background or the browser tab is changed. + // On iOS, blur is trigerred in the following cases: // - // Since in all these cases, the connection needs to be closed, - // [domRenderer.windowHasFocus] is not checked in [IOSTextEditingStrategy]. - _subscriptions.add(domElement.onBlur.listen((_) { - owner.sendTextConnectionClosedToFrameworkIfAny(); + // 1. The browser app is sent to the background (or the tab is changed). In + // this case, the window loses focus (see [domRenderer.windowHasFocus]), + // so we close the input connection with the framework. + // 2. The user taps on another focusable element. In this case, we refocus + // the input field and wait for the framework to manage the focus change. + // 3. The virtual keyboard is closed by tapping "done". We can't detect this + // programmatically, so we end up refocusing the input field. This is + // okay because the virtual keyboard will hide, and as soon as the user + // taps the text field again, the virtual keyboard will come up. + subscriptions.add(activeDomElement.onBlur.listen((_) { + if (domRenderer.windowHasFocus) { + activeDomElement.focus(); + } else { + owner.sendTextConnectionClosedToFrameworkIfAny(); + } })); } @override - void updateElementPlacement(EditableTextGeometry geometry) { - _geometry = geometry; + void updateElementPlacement(EditableTextGeometry textGeometry) { + geometry = textGeometry; if (isEnabled && _canPosition) { placeElement(); } @@ -1099,7 +1196,7 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { /// [_positionInputElementTimer] timer is restarted. The element will be /// placed to its correct position after [_delayBeforePlacement]. void _addTapListener() { - _subscriptions.add(domElement.onClick.listen((_) { + subscriptions.add(activeDomElement.onClick.listen((_) { // Check if the element is already positioned. If not this does not fall // under `The user was using the long press, now they want to enter text // via keyboard` journey. @@ -1123,8 +1220,8 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void placeElement() { - domElement.focus(); - _geometry?.applyToDomElement(domElement); + activeDomElement.focus(); + geometry?.applyToDomElement(activeDomElement); } } @@ -1141,41 +1238,41 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void initializeTextEditing( InputConfiguration inputConfig, { - required _OnChangeCallback onChange, - required _OnActionCallback onAction, + required OnChangeCallback onChange, + required OnActionCallback onAction, }) { super.initializeTextEditing(inputConfig, onChange: onChange, onAction: onAction); - inputConfig.inputType.configureInputMode(domElement); + inputConfig.inputType.configureInputMode(activeDomElement); if (hasAutofillGroup) { placeForm(); } else { - domRenderer.glassPaneElement!.append(domElement); + defaultTextEditingRoot.append(activeDomElement); } - inputConfig.textCapitalization.setAutocapitalizeAttribute(domElement); + inputConfig.textCapitalization.setAutocapitalizeAttribute(activeDomElement); } @override void addEventHandlers() { - if (_inputConfiguration.autofillGroup != null) { - _subscriptions - .addAll(_inputConfiguration.autofillGroup!.addInputEventListeners()); + if (inputConfiguration.autofillGroup != null) { + subscriptions + .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - _subscriptions.add(domElement.onInput.listen(_handleChange)); + subscriptions.add(activeDomElement.onInput.listen(handleChange)); - _subscriptions.add(domElement.onKeyDown.listen(_maybeSendAction)); + subscriptions.add(activeDomElement.onKeyDown.listen(maybeSendAction)); - _subscriptions.add(html.document.onSelectionChange.listen(_handleChange)); + subscriptions.add(html.document.onSelectionChange.listen(handleChange)); - _subscriptions.add(domElement.onBlur.listen((_) { - if (domRenderer.windowHasFocus!) { + subscriptions.add(activeDomElement.onBlur.listen((_) { + if (domRenderer.windowHasFocus) { // Chrome on Android will hide the onscreen keyboard when you tap outside // the text box. Instead, we want the framework to tell us to hide the // keyboard via `TextInput.clearClient` or `TextInput.hide`. Therefore // refocus as long as [domRenderer.windowHasFocus] is true. - domElement.focus(); + activeDomElement.focus(); } else { owner.sendTextConnectionClosedToFrameworkIfAny(); } @@ -1184,8 +1281,8 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void placeElement() { - domElement.focus(); - _geometry?.applyToDomElement(domElement); + activeDomElement.focus(); + geometry?.applyToDomElement(activeDomElement); } } @@ -1199,8 +1296,8 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void initializeTextEditing( InputConfiguration inputConfig, { - required _OnChangeCallback onChange, - required _OnActionCallback onAction, + required OnChangeCallback onChange, + required OnActionCallback onAction, }) { super.initializeTextEditing(inputConfig, onChange: onChange, onAction: onAction); @@ -1211,15 +1308,15 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void addEventHandlers() { - if (_inputConfiguration.autofillGroup != null) { - _subscriptions - .addAll(_inputConfiguration.autofillGroup!.addInputEventListeners()); + if (inputConfiguration.autofillGroup != null) { + subscriptions + .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - _subscriptions.add(domElement.onInput.listen(_handleChange)); + subscriptions.add(activeDomElement.onInput.listen(handleChange)); - _subscriptions.add(domElement.onKeyDown.listen(_maybeSendAction)); + subscriptions.add(activeDomElement.onKeyDown.listen(maybeSendAction)); // Detects changes in text selection. // @@ -1234,37 +1331,258 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { // // After each keyup, the start/end values of the selection is compared to // the previously saved editing state. - _subscriptions.add(domElement.onKeyUp.listen((event) { - _handleChange(event); + subscriptions.add(activeDomElement.onKeyUp.listen((html.KeyboardEvent event) { + handleChange(event); })); // In Firefox the context menu item "Select All" does not work without // listening to onSelect. On the other browsers onSelectionChange is // enough for covering "Select All" functionality. - _subscriptions.add(domElement.onSelect.listen(_handleChange)); + subscriptions.add(activeDomElement.onSelect.listen(handleChange)); - // Refocus on the domElement after blur, so that user can keep editing the + // Refocus on the activeDomElement after blur, so that user can keep editing the // text field. - _subscriptions.add(domElement.onBlur.listen((_) { - domElement.focus(); + subscriptions.add(activeDomElement.onBlur.listen((_) { + _postponeFocus(); })); preventDefaultForMouseEvents(); } + void _postponeFocus() { + // Firefox does not focus on the editing element if we call the focus + // inside the blur event, therefore we postpone the focus. + // Calling focus inside a Timer for `0` milliseconds guarantee that it is + // called after blur event propagation is completed. + Timer(const Duration(milliseconds: 0), () { + activeDomElement.focus(); + }); + } + @override void placeElement() { - domElement.focus(); - _geometry?.applyToDomElement(domElement); + activeDomElement.focus(); + geometry?.applyToDomElement(activeDomElement); // Set the last editing state if it exists, this is critical for a // users ongoing work to continue uninterrupted when there is an update to // the transform. - if (_lastEditingState != null) { - _lastEditingState!.applyToDomElement(domElement); + if (lastEditingState != null) { + lastEditingState!.applyToDomElement(activeDomElement); } } } +/// Base class for all `TextInput` commands sent through the `flutter/textinput` +/// channel. +@immutable +abstract class TextInputCommand { + const TextInputCommand(); + + /// Executes the logic for this command. + void run(HybridTextEditing textEditing); +} + +/// Responds to the 'TextInput.setClient' message. +class TextInputSetClient extends TextInputCommand { + const TextInputSetClient({ + required this.clientId, + required this.configuration, + }); + + final int clientId; + final InputConfiguration configuration; + + @override + void run(HybridTextEditing textEditing) { + final bool clientIdChanged = textEditing._clientId != null && textEditing._clientId != clientId; + if (clientIdChanged && textEditing.isEditing) { + // We're connecting a new client. Any pending command for the previous client + // are irrelevant at this point. + textEditing.stopEditing(); + } + textEditing._clientId = clientId; + textEditing.configuration = configuration; + } +} + +/// Creates the text editing strategy used in non-a11y mode. +DefaultTextEditingStrategy createDefaultTextEditingStrategy(HybridTextEditing textEditing) { + DefaultTextEditingStrategy strategy; + if (browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs) { + strategy = IOSTextEditingStrategy(textEditing); + } else if (browserEngine == BrowserEngine.webkit) { + strategy = SafariDesktopTextEditingStrategy(textEditing); + } else if (browserEngine == BrowserEngine.blink && + operatingSystem == OperatingSystem.android) { + strategy = AndroidTextEditingStrategy(textEditing); + } else if (browserEngine == BrowserEngine.firefox) { + strategy = FirefoxTextEditingStrategy(textEditing); + } else { + strategy = GloballyPositionedTextEditingStrategy(textEditing); + } + return strategy; +} + +/// Responds to the 'TextInput.updateConfig' message. +class TextInputUpdateConfig extends TextInputCommand { + const TextInputUpdateConfig(); + + @override + void run(HybridTextEditing textEditing) { + textEditing.strategy.applyConfiguration(textEditing.configuration!); + } +} + +/// Responds to the 'TextInput.setEditingState' message. +class TextInputSetEditingState extends TextInputCommand { + const TextInputSetEditingState({ + required this.state, + }); + + final EditingState state; + + @override + void run(HybridTextEditing textEditing) { + textEditing.strategy.setEditingState(state); + } +} + +/// Responds to the 'TextInput.show' message. +class TextInputShow extends TextInputCommand { + const TextInputShow(); + + @override + void run(HybridTextEditing textEditing) { + if (!textEditing.isEditing) { + textEditing._startEditing(); + } + } +} + +/// Responds to the 'TextInput.setEditableSizeAndTransform' message. +class TextInputSetEditableSizeAndTransform extends TextInputCommand { + const TextInputSetEditableSizeAndTransform({ + required this.geometry, + }); + + final EditableTextGeometry geometry; + + @override + void run(HybridTextEditing textEditing) { + textEditing.strategy.updateElementPlacement(geometry); + } +} + +/// Responds to the 'TextInput.setStyle' message. +class TextInputSetStyle extends TextInputCommand { + const TextInputSetStyle({ + required this.style, + }); + + final EditableTextStyle style; + + @override + void run(HybridTextEditing textEditing) { + textEditing.strategy.updateElementStyle(style); + } +} + +/// Responds to the 'TextInput.clearClient' message. +class TextInputClearClient extends TextInputCommand { + const TextInputClearClient(); + + @override + void run(HybridTextEditing textEditing) { + if (textEditing.isEditing) { + textEditing.stopEditing(); + } + } +} + +/// Responds to the 'TextInput.hide' message. +class TextInputHide extends TextInputCommand { + const TextInputHide(); + + @override + void run(HybridTextEditing textEditing) { + if (textEditing.isEditing) { + textEditing.stopEditing(); + } + } +} + +class TextInputSetMarkedTextRect extends TextInputCommand { + const TextInputSetMarkedTextRect(); + + @override + void run(HybridTextEditing textEditing) { + // No-op: this message is currently only used on iOS to implement + // UITextInput.firstRecForRange. + } +} + +class TextInputSetCaretRect extends TextInputCommand { + const TextInputSetCaretRect(); + + @override + void run(HybridTextEditing textEditing) { + // No-op: not supported on this platform. + } +} + +class TextInputRequestAutofill extends TextInputCommand { + const TextInputRequestAutofill(); + + @override + void run(HybridTextEditing textEditing) { + // No-op: not supported on this platform. + } +} + +class TextInputFinishAutofillContext extends TextInputCommand { + const TextInputFinishAutofillContext({ + required this.saveForm, + }); + + final bool saveForm; + + @override + void run(HybridTextEditing textEditing) { + // Close the text editing connection. Form is finalizing. + textEditing.sendTextConnectionClosedToFrameworkIfAny(); + if (saveForm) { + saveForms(); + } + // Clean the forms from DOM after submitting them. + cleanForms(); + } +} + +/// Submits the forms currently attached to the DOM. +/// +/// Browser will save the information entered to the form. +/// +/// Called when the form is finalized with save option `true`. +/// See: https://github.com/flutter/flutter/blob/bf9f3a3dcfea3022f9cf2dfc3ab10b120b48b19d/packages/flutter/lib/src/services/text_input.dart#L1277 +void saveForms() { + formsOnTheDom.forEach((String identifier, html.FormElement form) { + final html.InputElement submitBtn = + form.getElementsByClassName('submitBtn').first as html.InputElement; + submitBtn.click(); + }); +} + +/// Removes the forms from the DOM. +/// +/// Called when the form is finalized. +void cleanForms() { + for (final html.FormElement form in formsOnTheDom.values) { + form.remove(); + } + formsOnTheDom.clear(); +} + /// Translates the message-based communication between the framework and the /// engine [implementation]. /// @@ -1280,144 +1598,133 @@ class TextEditingChannel { ByteData? data, ui.PlatformMessageResponseCallback? callback) { const JSONMethodCodec codec = JSONMethodCodec(); final MethodCall call = codec.decodeMethodCall(data); + final TextInputCommand command; switch (call.method) { case 'TextInput.setClient': - implementation.setClient( - call.arguments[0], - InputConfiguration.fromFrameworkMessage(call.arguments[1]), + command = TextInputSetClient( + clientId: call.arguments[0] as int, + configuration: InputConfiguration.fromFrameworkMessage(call.arguments[1] as Map), ); break; case 'TextInput.updateConfig': - final config = InputConfiguration.fromFrameworkMessage(call.arguments); - implementation.updateConfig(config); + // Set configuration eagerly because it contains data about the text + // field used to flush the command queue. However, delaye applying the + // configuration because the strategy may not be available yet. + implementation.configuration = InputConfiguration.fromFrameworkMessage( + call.arguments as Map + ); + command = const TextInputUpdateConfig(); break; case 'TextInput.setEditingState': - implementation - .setEditingState(EditingState.fromFrameworkMessage(call.arguments)); + command = TextInputSetEditingState( + state: EditingState.fromFrameworkMessage( + call.arguments as Map + ), + ); break; case 'TextInput.show': - implementation.show(); + command = const TextInputShow(); break; case 'TextInput.setEditableSizeAndTransform': - implementation.setEditableSizeAndTransform( - EditableTextGeometry.fromFrameworkMessage(call.arguments)); + command = TextInputSetEditableSizeAndTransform( + geometry: EditableTextGeometry.fromFrameworkMessage( + call.arguments as Map + ), + ); break; case 'TextInput.setStyle': - implementation - .setStyle(EditableTextStyle.fromFrameworkMessage(call.arguments)); + command = TextInputSetStyle( + style: EditableTextStyle.fromFrameworkMessage( + call.arguments as Map, + ), + ); break; case 'TextInput.clearClient': - implementation.clearClient(); + command = const TextInputClearClient(); break; case 'TextInput.hide': - implementation.hide(); + command = const TextInputHide(); break; case 'TextInput.requestAutofill': - // No-op: This message is sent by the framework to requests the platform autofill UI to appear. - // Since autofill UI is a part of the browser, web engine does not need to utilize this method. + // There's no API to request autofill on the web. Instead we let the + // browser show autofill options automatically, if available. We + // therefore simply ignore this message. + command = const TextInputRequestAutofill(); break; case 'TextInput.finishAutofillContext': - final bool saveForm = call.arguments as bool; - // Close the text editing connection. Form is finalizing. - implementation.sendTextConnectionClosedToFrameworkIfAny(); - if (saveForm) { - saveForms(); - } - // Clean the forms from DOM after submitting them. - cleanForms(); + command = TextInputFinishAutofillContext( + saveForm: call.arguments as bool, + ); break; case 'TextInput.setMarkedTextRect': - // No-op: this message is currently only used on iOS to implement - // UITextInput.firstRecForRange. + command = const TextInputSetMarkedTextRect(); + break; + + case 'TextInput.setCaretRect': + command = const TextInputSetCaretRect(); break; default: - throw StateError( - 'Unsupported method call on the flutter/textinput channel: ${call.method}'); + EnginePlatformDispatcher.instance.replyToPlatformMessage(callback, null); + return; } - window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); - } - /// Used for submitting the forms attached on the DOM. - /// - /// Browser will save the information entered to the form. - /// - /// Called when the form is finalized with save option `true`. - /// See: https://github.com/flutter/flutter/blob/bf9f3a3dcfea3022f9cf2dfc3ab10b120b48b19d/packages/flutter/lib/src/services/text_input.dart#L1277 - void saveForms() { - formsOnTheDom.forEach((String identifier, html.FormElement form) { - final html.InputElement submitBtn = - form.getElementsByClassName('submitBtn').first as html.InputElement; - submitBtn.click(); + implementation.acceptCommand(command, () { + EnginePlatformDispatcher.instance + .replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); }); } - /// Used for removing the forms on the DOM. - /// - /// Called when the form is finalized. - void cleanForms() { - for (html.FormElement form in formsOnTheDom.values) { - form.remove(); - } - formsOnTheDom.clear(); - } - /// Sends the 'TextInputClient.updateEditingState' message to the framework. void updateEditingState(int? clientId, EditingState? editingState) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/textinput', - const JSONMethodCodec().encodeMethodCall( - MethodCall('TextInputClient.updateEditingState', [ - clientId, - editingState!.toFlutter(), - ]), - ), - _emptyCallback, - ); - } + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/textinput', + const JSONMethodCodec().encodeMethodCall( + MethodCall('TextInputClient.updateEditingState', [ + clientId, + editingState!.toFlutter(), + ]), + ), + _emptyCallback, + ); } /// Sends the 'TextInputClient.performAction' message to the framework. void performAction(int? clientId, String? inputAction) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/textinput', - const JSONMethodCodec().encodeMethodCall( - MethodCall( - 'TextInputClient.performAction', - [clientId, inputAction], - ), + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/textinput', + const JSONMethodCodec().encodeMethodCall( + MethodCall( + 'TextInputClient.performAction', + [clientId, inputAction], ), - _emptyCallback, - ); - } + ), + _emptyCallback, + ); } /// Sends the 'TextInputClient.onConnectionClosed' message to the framework. void onConnectionClosed(int? clientId) { - if (window._onPlatformMessage != null) { - window.invokeOnPlatformMessage( - 'flutter/textinput', - const JSONMethodCodec().encodeMethodCall( - MethodCall( - 'TextInputClient.onConnectionClosed', - [clientId], - ), + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/textinput', + const JSONMethodCodec().encodeMethodCall( + MethodCall( + 'TextInputClient.onConnectionClosed', + [clientId], ), - _emptyCallback, - ); - } + ), + _emptyCallback, + ); } } @@ -1431,7 +1738,7 @@ final HybridTextEditing textEditing = HybridTextEditing(); /// /// See: https://github.com/flutter/flutter/blob/bf9f3a3dcfea3022f9cf2dfc3ab10b120b48b19d/packages/flutter/lib/src/services/text_input.dart#L1277 final Map formsOnTheDom = - Map(); + {}; /// Should be used as a singleton to provide support for text editing in /// Flutter Web. @@ -1447,132 +1754,46 @@ class HybridTextEditing { /// The constructor also decides which text editing strategy to use depending /// on the operating system and browser engine. HybridTextEditing() { - if (browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs) { - this._defaultEditingElement = IOSTextEditingStrategy(this); - } else if (browserEngine == BrowserEngine.webkit) { - this._defaultEditingElement = SafariDesktopTextEditingStrategy(this); - } else if (browserEngine == BrowserEngine.blink && - operatingSystem == OperatingSystem.android) { - this._defaultEditingElement = AndroidTextEditingStrategy(this); - } else if (browserEngine == BrowserEngine.firefox) { - this._defaultEditingElement = FirefoxTextEditingStrategy(this); - } else { - this._defaultEditingElement = GloballyPositionedTextEditingStrategy(this); - } channel = TextEditingChannel(this); } late TextEditingChannel channel; - /// The text editing stategy used. It can change depending on the - /// formfactor/browser. - /// - /// It uses an HTML element to manage editing state when a custom element is - /// not provided via [useCustomEditableElement] - late final DefaultTextEditingStrategy _defaultEditingElement; - - /// The HTML element used to manage editing state. - /// - /// This field is populated using [useCustomEditableElement]. If `null` the - /// [_defaultEditingElement] is used instead. - DefaultTextEditingStrategy? _customEditingElement; - - DefaultTextEditingStrategy get editingElement { - return _customEditingElement ?? _defaultEditingElement; - } - - /// Responds to the 'TextInput.setClient' message. - void setClient(int? clientId, InputConfiguration configuration) { - final bool clientIdChanged = _clientId != null && _clientId != clientId; - if (clientIdChanged && isEditing) { - stopEditing(); - } - _clientId = clientId; - _configuration = configuration; - } - - void updateConfig(InputConfiguration configuration) { - _configuration = configuration; - editingElement._applyConfiguration(_configuration); - } - - /// Responds to the 'TextInput.setEditingState' message. - void setEditingState(EditingState state) { - editingElement.setEditingState(state); - } - - /// Responds to the 'TextInput.show' message. - void show() { - if (!isEditing) { - _startEditing(); - } - } - - /// Responds to the 'TextInput.setEditableSizeAndTransform' message. - void setEditableSizeAndTransform(EditableTextGeometry geometry) { - editingElement.updateElementPlacement(geometry); - } - - /// Responds to the 'TextInput.setStyle' message. - void setStyle(EditableTextStyle style) { - editingElement.updateElementStyle(style); - } - - /// Responds to the 'TextInput.clearClient' message. - void clearClient() { - // We do not distinguish between "clearClient" and "hide" on the Web. - hide(); - } - - /// Responds to the 'TextInput.hide' message. - void hide() { - if (isEditing) { - stopEditing(); - } - } - /// A CSS class name used to identify all elements used for text editing. @visibleForTesting static const String textEditingClass = 'flt-text-editing'; - static bool isEditingElement(html.Element element) { - return element.classes.contains(textEditingClass); - } - - /// Requests that [customEditingElement] is used for managing text editing state - /// instead of the hidden default element. - /// - /// Use [stopUsingCustomEditableElement] to switch back to default element. - void useCustomEditableElement( - DefaultTextEditingStrategy? customEditingElement) { - if (isEditing && customEditingElement != _customEditingElement) { - stopEditing(); - } - _customEditingElement = customEditingElement; - } - - /// Switches back to using the built-in default element for managing text - /// editing state. - void stopUsingCustomEditableElement() { - useCustomEditableElement(null); - } - int? _clientId; /// Flag which shows if there is an ongoing editing. /// /// Also used to define if a keyboard is needed. - @visibleForTesting bool isEditing = false; - late InputConfiguration _configuration; + InputConfiguration? configuration; + + DefaultTextEditingStrategy? debugTextEditingStrategyOverride; + + /// Supplies the DOM element used for editing. + late final DefaultTextEditingStrategy strategy = + debugTextEditingStrategyOverride ?? + (EngineSemanticsOwner.instance.semanticsEnabled + ? SemanticsTextEditingStrategy.ensureInitialized(this) + : createDefaultTextEditingStrategy(this)); + + void acceptCommand(TextInputCommand command, ui.VoidCallback callback) { + if (_debugPrintTextInputCommands) { + print('flutter/textinput channel command: ${command.runtimeType}'); + } + command.run(this); + callback(); + } void _startEditing() { assert(!isEditing); isEditing = true; - editingElement.enable( - _configuration, + strategy.enable( + configuration!, onChange: (EditingState? editingState) { channel.updateEditingState(_clientId, editingState); }, @@ -1585,7 +1806,7 @@ class HybridTextEditing { void stopEditing() { assert(isEditing); isEditing = false; - editingElement.disable(); + strategy.disable(); } void sendTextConnectionClosedToFrameworkIfAny() { @@ -1615,9 +1836,9 @@ class EditableTextStyle { assert(flutterStyle.containsKey('textAlignIndex')); assert(flutterStyle.containsKey('textDirectionIndex')); - final int textAlignIndex = flutterStyle['textAlignIndex']; - final int textDirectionIndex = flutterStyle['textDirectionIndex']; - final int? fontWeightIndex = flutterStyle['fontWeightIndex']; + final int textAlignIndex = flutterStyle['textAlignIndex'] as int; + final int textDirectionIndex = flutterStyle['textDirectionIndex'] as int; + final int? fontWeightIndex = flutterStyle['fontWeightIndex'] as int?; // Convert [fontWeightIndex] to its CSS equivalent value. final String fontWeight = fontWeightIndex != null @@ -1628,8 +1849,8 @@ class EditableTextStyle { // corresponding enum values in [ui.TextAlign] and [ui.TextDirection] // respectively. return EditableTextStyle( - fontSize: flutterStyle['fontSize'], - fontFamily: flutterStyle['fontFamily'], + fontSize: flutterStyle.tryDouble('fontSize'), + fontFamily: flutterStyle.tryString('fontFamily'), textAlign: ui.TextAlign.values[textAlignIndex], textDirection: ui.TextDirection.values[textDirectionIndex], fontWeight: fontWeight, @@ -1646,7 +1867,8 @@ class EditableTextStyle { String? get align => textAlignToCssValue(textAlign, textDirection); - String get cssFont => '${fontWeight} ${fontSize}px ${fontFamily}'; + String get cssFont => + '$fontWeight ${fontSize}px ${canonicalizeFontFamily(fontFamily)}'; void applyToDomElement(html.HtmlElement domElement) { domElement.style @@ -1661,7 +1883,7 @@ class EditableTextStyle { /// message from the framework. @immutable class EditableTextGeometry { - EditableTextGeometry({ + const EditableTextGeometry({ required this.width, required this.height, required this.globalTransform, @@ -1676,10 +1898,10 @@ class EditableTextGeometry { assert(encodedGeometry.containsKey('transform')); final List transformList = - List.from(encodedGeometry['transform']); + List.from(encodedGeometry.readList('transform')); return EditableTextGeometry( - width: encodedGeometry['width'], - height: encodedGeometry['height'], + width: encodedGeometry.readDouble('width'), + height: encodedGeometry.readDouble('height'), globalTransform: Float32List.fromList(transformList), ); } diff --git a/lib/web_ui/lib/src/engine/ulps.dart b/lib/web_ui/lib/src/engine/ulps.dart index a4b279bf700c6..58e5b2c00bad0 100644 --- a/lib/web_ui/lib/src/engine/ulps.dart +++ b/lib/web_ui/lib/src/engine/ulps.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; // This is a small library to handle stability for floating point operations. // @@ -50,14 +50,15 @@ int twosComplimentToSignBit(int x) { class _FloatBitConverter { final Float32List float32List; final Int32List int32List; - _FloatBitConverter._(this.float32List, this.int32List); factory _FloatBitConverter() { - Float32List float32List = Float32List(1); + final Float32List float32List = Float32List(1); return _FloatBitConverter._( float32List, float32List.buffer.asInt32List(0, 1)); } + _FloatBitConverter._(this.float32List, this.int32List); + int toInt(Float32List source, int index) { float32List[0] = source[index]; return int32List[0]; @@ -101,7 +102,7 @@ int floatAs2sCompliment(double x) => double twosComplimentAsFloat(int x) => bitsToFloat(twosComplimentToSignBit(x)); bool _argumentsDenormalized(double a, double b, int epsilon) { - double denormalizedCheck = kFltEpsilon * epsilon / 2; + final double denormalizedCheck = kFltEpsilon * epsilon / 2; return a.abs() <= denormalizedCheck && b.abs() <= denormalizedCheck; } @@ -109,8 +110,8 @@ bool equalUlps(double a, double b, int epsilon, int depsilon) { if (_argumentsDenormalized(a, b, depsilon)) { return true; } - int aBits = floatAs2sCompliment(a); - int bBits = floatAs2sCompliment(b); + final int aBits = floatAs2sCompliment(a); + final int bBits = floatAs2sCompliment(b); // Find the difference in ULPs. return aBits < bBits + epsilon && bBits < aBits + epsilon; } @@ -148,10 +149,10 @@ bool approximatelyEqual(double ax, double ay, double bx, double by) { if (!roughlyEqualUlps(ax, bx) || !roughlyEqualUlps(ay, by)) { return false; } - final double dx = (ax - bx); - final double dy = (ay - by); - double dist = math.sqrt(dx * dx + dy * dy); - double tiniest = math.min(math.min(math.min(ax, bx), ay), by); + final double dx = ax - bx; + final double dy = ay - by; + final double dist = math.sqrt(dx * dx + dy * dy); + final double tiniest = math.min(math.min(math.min(ax, bx), ay), by); double largest = math.max(math.max(math.max(ax, bx), ay), by); largest = math.max(largest, -tiniest); return almostDequalUlps(largest, largest + dist); @@ -174,8 +175,8 @@ bool roughlyEqualUlps(double a, double b) { } bool dEqualUlpsEpsilon(double a, double b, int epsilon) { - int aBits = floatAs2sCompliment(a); - int bBits = floatAs2sCompliment(b); + final int aBits = floatAs2sCompliment(a); + final int bBits = floatAs2sCompliment(b); // Find the difference in ULPs. return aBits < bBits + epsilon && bBits < aBits + epsilon; } diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 277f5484e2f07..026a459a7fca2 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -2,8 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'vector_math.dart'; /// Generic callback signature, used by [_futurize]. typedef Callback = void Function(T result); @@ -73,7 +81,7 @@ void setElementTransform(html.Element element, Float32List matrix4) { /// See also: /// * https://github.com/flutter/flutter/issues/32274 /// * https://bugs.chromium.org/p/chromium/issues/detail?id=1040222 -String float64ListToCssTransform(Float32List matrix) { +String float64ListToCssTransform(List matrix) { assert(matrix.length == 16); final TransformKind transformKind = transformKindOf(matrix); if (transformKind == TransformKind.transform2d) { @@ -105,14 +113,14 @@ enum TransformKind { } /// Detects the kind of transform the [matrix] performs. -TransformKind transformKindOf(Float32List matrix) { +TransformKind transformKindOf(List matrix) { assert(matrix.length == 16); - final Float32List m = matrix; + final List m = matrix; // If matrix contains scaling, rotation, z translation or // perspective transform, it is not considered simple. - final bool isSimple2dTransform = - m[15] == 1.0 && // start reading from the last element to eliminate range checks in subsequent reads. + final bool isSimple2dTransform = m[15] == + 1.0 && // start reading from the last element to eliminate range checks in subsequent reads. m[14] == 0.0 && // z translation is NOT simple // m[13] - y translation is simple // m[12] - x translation is simple @@ -126,8 +134,8 @@ TransformKind transformKindOf(Float32List matrix) { // m[4] - 2D rotation is simple m[3] == 0.0 && m[2] == 0.0; - // m[1] - 2D rotation is simple - // m[0] - scale x is simple + // m[1] - 2D rotation is simple + // m[0] - scale x is simple if (!isSimple2dTransform) { return TransformKind.complex; @@ -136,8 +144,7 @@ TransformKind transformKindOf(Float32List matrix) { // From this point on we're sure the transform is 2D, but we don't know if // it's identity or not. To check, we need to look at the remaining elements // that were not checked above. - final bool isIdentityTransform = - m[0] == 1.0 && + final bool isIdentityTransform = m[0] == 1.0 && m[1] == 0.0 && m[4] == 0.0 && m[5] == 1.0 && @@ -164,15 +171,15 @@ bool isIdentityFloat32ListTransform(Float32List matrix) { /// permitted. However, it is inefficient to construct a matrix for an identity /// transform. Consider removing the CSS `transform` property from elements /// that apply identity transform. -String float64ListToCssTransform2d(Float32List matrix) { - assert (transformKindOf(matrix) != TransformKind.complex); +String float64ListToCssTransform2d(List matrix) { + assert(transformKindOf(matrix) != TransformKind.complex); return 'matrix(${matrix[0]},${matrix[1]},${matrix[4]},${matrix[5]},${matrix[12]},${matrix[13]})'; } /// Converts [matrix] to a 3D CSS transform value. -String float64ListToCssTransform3d(Float32List matrix) { +String float64ListToCssTransform3d(List matrix) { assert(matrix.length == 16); - final Float32List m = matrix; + final List m = matrix; if (m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && @@ -279,10 +286,32 @@ void transformLTRB(Matrix4 transform, Float32List ltrb) { _tempPointMatrix.multiplyTranspose(transform); - ltrb[0] = math.min(math.min(math.min(_tempPointData[0], _tempPointData[1]), _tempPointData[2]), _tempPointData[3]); - ltrb[1] = math.min(math.min(math.min(_tempPointData[4], _tempPointData[5]), _tempPointData[6]), _tempPointData[7]); - ltrb[2] = math.max(math.max(math.max(_tempPointData[0], _tempPointData[1]), _tempPointData[2]), _tempPointData[3]); - ltrb[3] = math.max(math.max(math.max(_tempPointData[4], _tempPointData[5]), _tempPointData[6]), _tempPointData[7]); + // Handle non-homogenous matrices. + double w = transform[15]; + if (w == 0.0) { + w = 1.0; + } + + ltrb[0] = math.min( + math.min(math.min(_tempPointData[0], _tempPointData[1]), + _tempPointData[2]), + _tempPointData[3]) / + w; + ltrb[1] = math.min( + math.min(math.min(_tempPointData[4], _tempPointData[5]), + _tempPointData[6]), + _tempPointData[7]) / + w; + ltrb[2] = math.max( + math.max(math.max(_tempPointData[0], _tempPointData[1]), + _tempPointData[2]), + _tempPointData[3]) / + w; + ltrb[3] = math.max( + math.max(math.max(_tempPointData[4], _tempPointData[5]), + _tempPointData[6]), + _tempPointData[7]) / + w; } /// Returns true if [rect] contains every point that is also contained by the @@ -297,33 +326,6 @@ bool rectContainsOther(ui.Rect rect, ui.Rect other) { rect.bottom >= other.bottom; } -/// Counter used for generating clip path id inside an svg tag. -int _clipIdCounter = 0; - -/// Converts Path to svg element that contains a clip-path definition. -/// -/// Calling this method updates [_clipIdCounter]. The HTML id of the generated -/// clip is set to "svgClip${_clipIdCounter}", e.g. "svgClip123". -String _pathToSvgClipPath(ui.Path path, - {double offsetX = 0, - double offsetY = 0, - double scaleX = 1.0, - double scaleY = 1.0}) { - _clipIdCounter += 1; - final StringBuffer sb = StringBuffer(); - sb.write(''); - sb.write(''); - - final String clipId = 'svgClip$_clipIdCounter'; - sb.write(''); - - sb.write(' _genericFontFamilies = { /// /// For iOS, default to -apple-system, where it should be available, otherwise /// default to Arial. BlinkMacSystemFont is used for Chrome on iOS. -final String _fallbackFontFamily = _isMacOrIOS ? - '-apple-system, BlinkMacSystemFont' : 'Arial'; - -bool get _isMacOrIOS => operatingSystem == OperatingSystem.iOs || - operatingSystem == OperatingSystem.macOs; +final String _fallbackFontFamily = + isMacOrIOS ? '-apple-system, BlinkMacSystemFont' : 'Arial'; /// Create a font-family string appropriate for CSS. /// @@ -434,13 +433,15 @@ String? canonicalizeFontFamily(String? fontFamily) { if (_genericFontFamilies.contains(fontFamily)) { return fontFamily; } - if (_isMacOrIOS) { + if (isMacOrIOS) { // Unlike Safari, Chrome on iOS does not correctly fallback to cupertino // on sans-serif. // Map to San Francisco Text/Display fonts, use -apple-system, // BlinkMacSystemFont. - if (fontFamily == '.SF Pro Text' || fontFamily == '.SF Pro Display' || - fontFamily == '.SF UI Text' || fontFamily == '.SF UI Display') { + if (fontFamily == '.SF Pro Text' || + fontFamily == '.SF Pro Display' || + fontFamily == '.SF UI Text' || + fontFamily == '.SF UI Display') { return _fallbackFontFamily; } } @@ -450,7 +451,7 @@ String? canonicalizeFontFamily(String? fontFamily) { /// Converts a list of [Offset] to a typed array of floats. Float32List offsetListToFloat32List(List offsetList) { final int length = offsetList.length; - final floatList = Float32List(length * 2); + final Float32List floatList = Float32List(length * 2); for (int i = 0, destIndex = 0; i < length; i++, destIndex += 2) { floatList[destIndex] = offsetList[i].dx; floatList[destIndex + 1] = offsetList[i].dy; @@ -474,39 +475,22 @@ void applyWebkitClipFix(html.Element? containerElement) { } } -final ByteData? _fontChangeMessage = JSONMessageCodec().encodeMessage({'type': 'fontsChange'}); - -// Font load callbacks will typically arrive in sequence, we want to prevent -// sendFontChangeMessage of causing multiple synchronous rebuilds. -// This flag ensures we properly schedule a single call to framework. -bool _fontChangeScheduled = false; - -FutureOr sendFontChangeMessage() async { - if (window._onPlatformMessage != null) - if (!_fontChangeScheduled) { - _fontChangeScheduled = true; - // Batch updates into next animationframe. - html.window.requestAnimationFrame((num _) { - _fontChangeScheduled = false; - window.invokeOnPlatformMessage( - 'flutter/system', - _fontChangeMessage, - (_) {}, - ); - }); - } -} - // Stores matrix in a form that allows zero allocation transforms. -class _FastMatrix64 { - final Float64List matrix; +class FastMatrix32 { + final Float32List matrix; double transformedX = 0, transformedY = 0; - _FastMatrix64(this.matrix); + FastMatrix32(this.matrix); void transform(double x, double y) { transformedX = matrix[12] + (matrix[0] * x) + (matrix[4] * y); transformedY = matrix[13] + (matrix[1] * x) + (matrix[5] * y); } + + String debugToString() => + '${matrix[0].toStringAsFixed(3)}, ${matrix[4].toStringAsFixed(3)}, ${matrix[8].toStringAsFixed(3)}, ${matrix[12].toStringAsFixed(3)}\n' + '${matrix[1].toStringAsFixed(3)}, ${matrix[5].toStringAsFixed(3)}, ${matrix[9].toStringAsFixed(3)}, ${matrix[13].toStringAsFixed(3)}\n' + '${matrix[2].toStringAsFixed(3)}, ${matrix[6].toStringAsFixed(3)}, ${matrix[10].toStringAsFixed(3)}, ${matrix[14].toStringAsFixed(3)}\n' + '${matrix[3].toStringAsFixed(3)}, ${matrix[7].toStringAsFixed(3)}, ${matrix[11].toStringAsFixed(3)}, ${matrix[15].toStringAsFixed(3)}\n'; } /// Roughly the inverse of [ui.Shadow.convertRadiusToSigma]. @@ -528,31 +512,6 @@ bool isUnsoundNull(dynamic object) { return object == null; } -bool _offsetIsValid(ui.Offset offset) { - assert(offset != null, 'Offset argument was null.'); // ignore: unnecessary_null_comparison - assert(!offset.dx.isNaN && !offset.dy.isNaN, - 'Offset argument contained a NaN value.'); - return true; -} - -bool _matrix4IsValid(Float32List matrix4) { - assert(matrix4 != null, 'Matrix4 argument was null.'); // ignore: unnecessary_null_comparison - assert(matrix4.length == 16, 'Matrix4 must have 16 entries.'); - return true; -} - -void _validateColorStops(List colors, List? colorStops) { - if (colorStops == null) { - if (colors.length != 2) - throw ArgumentError( - '"colors" must have length 2 if "colorStops" is omitted.'); - } else { - if (colors.length != colorStops.length) - throw ArgumentError( - '"colors" and "colorStops" arguments must have equal length.'); - } -} - int clampInt(int value, int min, int max) { assert(min <= max); if (value < min) { @@ -563,3 +522,121 @@ int clampInt(int value, int min, int max) { return value; } } + +/// Prints a warning message to the console. +/// +/// This function can be overridden in tests. This could be useful, for example, +/// to verify that warnings are printed under certain circumstances. +void Function(String) printWarning = html.window.console.warn; + +/// Determines if lists [a] and [b] are deep equivalent. +/// +/// Returns true if the lists are both null, or if they are both non-null, have +/// the same length, and contain the same elements in the same order. Returns +/// false otherwise. +bool listEquals(List? a, List? b) { + if (a == null) { + return b == null; + } + if (b == null || a.length != b.length) { + return false; + } + for (int index = 0; index < a.length; index += 1) { + if (a[index] != b[index]) { + return false; + } + } + return true; +} + +// HTML only supports a single radius, but Flutter ImageFilter supports separate +// horizontal and vertical radii. The best approximation we can provide is to +// average the two radii together for a single compromise value. +String blurSigmasToCssString(double sigmaX, double sigmaY) { + return 'blur(${(sigmaX + sigmaY) * 0.5}px)'; +} + +/// Checks if the dynamic [object] is equal to null. +bool unsafeIsNull(dynamic object) { + return object == null; +} + +/// A typed variant of [html.Window.fetch]. +Future httpFetch(String url) async { + final dynamic result = await html.window.fetch(url); + return result as html.Body; +} + +/// Extensions to [Map] that make it easier to treat it as a JSON object. The +/// keys are `dynamic` because when JSON is deserialized from method channels +/// it arrives as `Map`. +// TODO(yjbanov): use Json typedef when type aliases are shipped +extension JsonExtensions on Map { + Map readJson(String propertyName) { + return this[propertyName] as Map; + } + + Map? tryJson(String propertyName) { + return this[propertyName] as Map?; + } + + Map readDynamicJson(String propertyName) { + return this[propertyName] as Map; + } + + Map? tryDynamicJson(String propertyName) { + return this[propertyName] as Map?; + } + + List readList(String propertyName) { + return this[propertyName] as List; + } + + List? tryList(String propertyName) { + return this[propertyName] as List?; + } + + List castList(String propertyName) { + return (this[propertyName] as List).cast(); + } + + List? tryCastList(String propertyName) { + final List? rawList = tryList(propertyName); + if (rawList == null) { + return null; + } + return rawList.cast(); + } + + String readString(String propertyName) { + return this[propertyName] as String; + } + + String? tryString(String propertyName) { + return this[propertyName] as String?; + } + + bool readBool(String propertyName) { + return this[propertyName] as bool; + } + + bool? tryBool(String propertyName) { + return this[propertyName] as bool?; + } + + int readInt(String propertyName) { + return this[propertyName] as int; + } + + int? tryInt(String propertyName) { + return this[propertyName] as int?; + } + + double readDouble(String propertyName) { + return this[propertyName] as double; + } + + double? tryDouble(String propertyName) { + return this[propertyName] as double?; + } +} diff --git a/lib/web_ui/lib/src/engine/validators.dart b/lib/web_ui/lib/src/engine/validators.dart index c01fa8eff2b23..252920e48093d 100644 --- a/lib/web_ui/lib/src/engine/validators.dart +++ b/lib/web_ui/lib/src/engine/validators.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:typed_data'; + +import 'package:ui/ui.dart' as ui; bool rectIsValid(ui.Rect rect) { assert(rect != null, 'Rect argument was null.'); // ignore: unnecessary_null_comparison @@ -46,3 +47,16 @@ bool radiusIsValid(ui.Radius radius) { 'Radius argument contained a NaN value.'); return true; } + +/// Validates color and color stops used for a gradient. +void validateColorStops(List colors, List? colorStops) { + if (colorStops == null) { + if (colors.length != 2) + throw ArgumentError( + '"colors" must have length 2 if "colorStops" is omitted.'); + } else { + if (colors.length != colorStops.length) + throw ArgumentError( + '"colors" and "colorStops" arguments must have equal length.'); + } +} diff --git a/lib/web_ui/lib/src/engine/vector_math.dart b/lib/web_ui/lib/src/engine/vector_math.dart index 1b7dbdfa0dead..2c30a4e4e38b9 100644 --- a/lib/web_ui/lib/src/engine/vector_math.dart +++ b/lib/web_ui/lib/src/engine/vector_math.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'util.dart'; class Matrix4 { final Float32List _m4storage; @@ -67,7 +69,12 @@ class Matrix4 { Matrix4.zero() : _m4storage = Float32List(16); /// Identity matrix. - factory Matrix4.identity() => Matrix4.zero()..setIdentity(); + Matrix4.identity() : _m4storage = Float32List(16) { + _m4storage[15] = 1.0; + _m4storage[0] = 1.0; + _m4storage[5] = 1.0; + _m4storage[10] = 1.0; + } /// Copies values from [other]. factory Matrix4.copy(Matrix4 other) => Matrix4.zero()..setFrom(other); @@ -98,14 +105,12 @@ class Matrix4 { ..setRotationZ(radians); /// Translation matrix. - factory Matrix4.translation(Vector3 translation) => Matrix4.zero() - ..setIdentity() + factory Matrix4.translation(Vector3 translation) => Matrix4.identity() ..setTranslation(translation); /// Translation matrix. factory Matrix4.translationValues(double x, double y, double z) => - Matrix4.zero() - ..setIdentity() + Matrix4.identity() ..setTranslationRaw(x, y, z); /// Scale matrix. @@ -198,6 +203,9 @@ class Matrix4 { /// Copy into [arg]. Matrix4 copyInto(Matrix4 arg) { final Float32List argStorage = arg._m4storage; + // Start reading from the last element to eliminate range checks + // in subsequent reads. + argStorage[15] = _m4storage[15]; argStorage[0] = _m4storage[0]; argStorage[1] = _m4storage[1]; argStorage[2] = _m4storage[2]; @@ -213,7 +221,6 @@ class Matrix4 { argStorage[12] = _m4storage[12]; argStorage[13] = _m4storage[13]; argStorage[14] = _m4storage[14]; - argStorage[15] = _m4storage[15]; return arg; } @@ -248,6 +255,7 @@ class Matrix4 { final double sy = y ?? x; final double sz = z ?? x; const double sw = 1.0; + _m4storage[15] *= sw; _m4storage[0] *= sx; _m4storage[1] *= sx; _m4storage[2] *= sx; @@ -263,7 +271,6 @@ class Matrix4 { _m4storage[12] *= sw; _m4storage[13] *= sw; _m4storage[14] *= sw; - _m4storage[15] *= sw; } /// Create a copy of [this] scaled by a [Vector3], [Vector4] or [x],[y], and @@ -272,6 +279,7 @@ class Matrix4 { /// Zeros [this]. void setZero() { + _m4storage[15] = 0.0; _m4storage[0] = 0.0; _m4storage[1] = 0.0; _m4storage[2] = 0.0; @@ -287,11 +295,11 @@ class Matrix4 { _m4storage[12] = 0.0; _m4storage[13] = 0.0; _m4storage[14] = 0.0; - _m4storage[15] = 0.0; } /// Makes [this] into the identity matrix. void setIdentity() { + _m4storage[15] = 1.0; _m4storage[0] = 1.0; _m4storage[1] = 0.0; _m4storage[2] = 0.0; @@ -307,7 +315,6 @@ class Matrix4 { _m4storage[12] = 0.0; _m4storage[13] = 0.0; _m4storage[14] = 0.0; - _m4storage[15] = 1.0; } /// Returns the tranpose of this. @@ -337,56 +344,42 @@ class Matrix4 { /// Returns the determinant of this matrix. double determinant() { + final Float32List m = _m4storage; final double det2_01_01 = - _m4storage[0] * _m4storage[5] - _m4storage[1] * _m4storage[4]; + m[0] * m[5] - m[1] * m[4]; final double det2_01_02 = - _m4storage[0] * _m4storage[6] - _m4storage[2] * _m4storage[4]; + m[0] * m[6] - m[2] * m[4]; final double det2_01_03 = - _m4storage[0] * _m4storage[7] - _m4storage[3] * _m4storage[4]; + m[0] * m[7] - m[3] * m[4]; final double det2_01_12 = - _m4storage[1] * _m4storage[6] - _m4storage[2] * _m4storage[5]; + m[1] * m[6] - m[2] * m[5]; final double det2_01_13 = - _m4storage[1] * _m4storage[7] - _m4storage[3] * _m4storage[5]; + m[1] * m[7] - m[3] * m[5]; final double det2_01_23 = - _m4storage[2] * _m4storage[7] - _m4storage[3] * _m4storage[6]; - final double det3_201_012 = _m4storage[8] * det2_01_12 - - _m4storage[9] * det2_01_02 + - _m4storage[10] * det2_01_01; - final double det3_201_013 = _m4storage[8] * det2_01_13 - - _m4storage[9] * det2_01_03 + - _m4storage[11] * det2_01_01; - final double det3_201_023 = _m4storage[8] * det2_01_23 - - _m4storage[10] * det2_01_03 + - _m4storage[11] * det2_01_02; - final double det3_201_123 = _m4storage[9] * det2_01_23 - - _m4storage[10] * det2_01_13 + - _m4storage[11] * det2_01_12; - return -det3_201_123 * _m4storage[12] + - det3_201_023 * _m4storage[13] - - det3_201_013 * _m4storage[14] + - det3_201_012 * _m4storage[15]; - } - - /// Returns a new vector or matrix by multiplying [this] with [arg]. - dynamic operator *(dynamic arg) { - if (arg is double) { - return scaled(arg); - } - if (arg is Vector3) { - final Vector3 copy = arg.clone(); - transform3(copy.storage); - return copy; - } - if (arg is Matrix4) { - return multiplied(arg); - } - throw ArgumentError(arg); + m[2] * m[7] - m[3] * m[6]; + final double det3_201_012 = m[8] * det2_01_12 - + m[9] * det2_01_02 + + m[10] * det2_01_01; + final double det3_201_013 = m[8] * det2_01_13 - + m[9] * det2_01_03 + + m[11] * det2_01_01; + final double det3_201_023 = m[8] * det2_01_23 - + m[10] * det2_01_03 + + m[11] * det2_01_02; + final double det3_201_123 = m[9] * det2_01_23 - + m[10] * det2_01_13 + + m[11] * det2_01_12; + return -det3_201_123 * m[12] + + det3_201_023 * m[13] - + det3_201_013 * m[14] + + det3_201_012 * m[15]; } /// Transform [arg] of type [Vector3] using the perspective transformation /// defined by [this]. Vector3 perspectiveTransform(Vector3 arg) { final Float32List argStorage = arg._v3storage; + final double x = (_m4storage[0] * argStorage[0]) + (_m4storage[4] * argStorage[1]) + (_m4storage[8] * argStorage[2]) + @@ -411,27 +404,41 @@ class Matrix4 { } bool isIdentity() => - _m4storage[0] == 1.0 // col 1 - && + _m4storage[0] == 1.0 && // col 1 _m4storage[1] == 0.0 && _m4storage[2] == 0.0 && _m4storage[3] == 0.0 && - _m4storage[4] == 0.0 // col 2 - && + _m4storage[4] == 0.0 && // col 2 _m4storage[5] == 1.0 && _m4storage[6] == 0.0 && _m4storage[7] == 0.0 && - _m4storage[8] == 0.0 // col 3 - && + _m4storage[8] == 0.0 && // col 3 _m4storage[9] == 0.0 && _m4storage[10] == 1.0 && _m4storage[11] == 0.0 && - _m4storage[12] == 0.0 // col 4 - && + _m4storage[12] == 0.0 && // col 4 _m4storage[13] == 0.0 && _m4storage[14] == 0.0 && _m4storage[15] == 1.0; + /// Whether transform is identity or simple translation using m[12,13,14]. + /// + /// We check for [15] first since that will eliminate bounds checks for rest. + bool isIdentityOrTranslation() => + _m4storage[15] == 1.0 && + _m4storage[0] == 1.0 && // col 1 + _m4storage[1] == 0.0 && + _m4storage[2] == 0.0 && + _m4storage[3] == 0.0 && + _m4storage[4] == 0.0 && // col 2 + _m4storage[5] == 1.0 && + _m4storage[6] == 0.0 && + _m4storage[7] == 0.0 && + _m4storage[8] == 0.0 && // col 3 + _m4storage[9] == 0.0 && + _m4storage[10] == 1.0 && + _m4storage[11] == 0.0; + /// Returns the translation vector from this homogeneous transformation matrix. Vector3 getTranslation() { final double z = _m4storage[14]; @@ -719,6 +726,7 @@ class Matrix4 { /// Multiply [this] by [arg]. void multiply(Matrix4 arg) { + final double m33 = _m4storage[15]; final double m00 = _m4storage[0]; final double m01 = _m4storage[4]; final double m02 = _m4storage[8]; @@ -734,8 +742,8 @@ class Matrix4 { final double m30 = _m4storage[3]; final double m31 = _m4storage[7]; final double m32 = _m4storage[11]; - final double m33 = _m4storage[15]; final Float32List argStorage = arg._m4storage; + final double n33 = argStorage[15]; final double n00 = argStorage[0]; final double n01 = argStorage[4]; final double n02 = argStorage[8]; @@ -751,7 +759,6 @@ class Matrix4 { final double n30 = argStorage[3]; final double n31 = argStorage[7]; final double n32 = argStorage[11]; - final double n33 = argStorage[15]; _m4storage[0] = (m00 * n00) + (m01 * n10) + (m02 * n20) + (m03 * n30); _m4storage[4] = (m00 * n01) + (m01 * n11) + (m02 * n21) + (m03 * n31); _m4storage[8] = (m00 * n02) + (m01 * n12) + (m02 * n22) + (m03 * n32); @@ -775,6 +782,7 @@ class Matrix4 { /// Multiply a transposed [this] with [arg]. void transposeMultiply(Matrix4 arg) { + final double m33 = _m4storage[15]; final double m00 = _m4storage[0]; final double m01 = _m4storage[1]; final double m02 = _m4storage[2]; @@ -790,7 +798,7 @@ class Matrix4 { final double m30 = _m4storage[12]; final double m31 = _m4storage[13]; final double m32 = _m4storage[14]; - final double m33 = _m4storage[15]; + final Float32List argStorage = arg._m4storage; _m4storage[0] = (m00 * argStorage[0]) + (m01 * argStorage[1]) + @@ -996,8 +1004,8 @@ class Matrix4 { /// This transformation forgets the final Z component. If you need the /// Z component, see [transform3]. void transform2(Float32List vector) { - double x = vector[0]; - double y = vector[1]; + final double x = vector[0]; + final double y = vector[1]; vector[0] = (_m4storage[0] * x) + (_m4storage[4] * y) + _m4storage[12]; diff --git a/lib/web_ui/lib/src/engine/web_experiments.dart b/lib/web_ui/lib/src/engine/web_experiments.dart index 1a425ab0bb2f2..7dea7f8e98622 100644 --- a/lib/web_ui/lib/src/engine/web_experiments.dart +++ b/lib/web_ui/lib/src/engine/web_experiments.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +import 'dart:js' as js; + +import '../engine.dart' show registerHotRestartListener; /// A bag of all experiment flags in the web engine. /// @@ -18,35 +19,15 @@ class WebExperiments { } static WebExperiments ensureInitialized() { - return WebExperiments.instance ?? (WebExperiments.instance = WebExperiments._()); + return WebExperiments.instance ?? + (WebExperiments.instance = WebExperiments._()); } static WebExperiments? instance; - /// Experiment flag for using canvas-based text measurement. - bool get useCanvasText => _useCanvasText; - set useCanvasText(bool? enabled) { - _useCanvasText = enabled ?? _defaultUseCanvasText; - } - - static const bool _defaultUseCanvasText = const bool.fromEnvironment( - 'FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT', - defaultValue: true, - ); - - bool _useCanvasText = _defaultUseCanvasText; - /// Reset all experimental flags to their default values. - void reset() { - _useCanvasText = _defaultUseCanvasText; - } + void reset() {} /// Used to enable/disable experimental flags in the web engine. - void updateExperiment(String name, bool enabled) { - switch (name) { - case 'useCanvasText': - useCanvasText = enabled; - break; - } - } + void updateExperiment(String name, bool? enabled) {} } diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 1c04be3c3f3b0..17c1271513529 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -2,52 +2,210 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -part of engine; +@JS() +library window; + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:js/js.dart'; +import 'package:meta/meta.dart'; +import 'package:ui/ui.dart' as ui; + +import 'browser_detection.dart'; +import 'navigation/history.dart'; +import 'navigation/js_url_strategy.dart'; +import 'navigation/url_strategy.dart'; +import 'platform_dispatcher.dart'; +import 'services.dart'; +import 'test_embedding.dart'; +import 'util.dart'; + +typedef _HandleMessageCallBack = Future Function(); /// When set to true, all platform messages will be printed to the console. -const bool _debugPrintPlatformMessages = false; +const bool debugPrintPlatformMessages = false; -/// Requests that the browser schedule a frame. +/// Whether [_customUrlStrategy] has been set or not. /// -/// This may be overridden in tests, for example, to pump fake frames. -ui.VoidCallback? scheduleFrameCallback; - -/// The Web implementation of [ui.Window]. -class EngineWindow extends ui.Window { - EngineWindow() { - _addBrightnessMediaQueryListener(); - js.context['_flutter_web_set_location_strategy'] = (LocationStrategy strategy) { - locationStrategy = strategy; - }; - registerHotRestartListener(() { - js.context['_flutter_web_set_location_strategy'] = null; - }); +/// It is valid to set [_customUrlStrategy] to null, so we can't use a null +/// check to determine whether it was set or not. We need an extra boolean. +bool _isUrlStrategySet = false; + +/// A custom URL strategy set by the app before running. +UrlStrategy? _customUrlStrategy; +set customUrlStrategy(UrlStrategy? strategy) { + assert(!_isUrlStrategySet, 'Cannot set URL strategy more than once.'); + _isUrlStrategySet = true; + _customUrlStrategy = strategy; +} + +/// The Web implementation of [ui.SingletonFlutterWindow]. +class EngineFlutterWindow extends ui.SingletonFlutterWindow { + EngineFlutterWindow(this._windowId, this.platformDispatcher) { + final EnginePlatformDispatcher engineDispatcher = + platformDispatcher as EnginePlatformDispatcher; + engineDispatcher.windows[_windowId] = this; + engineDispatcher.windowConfigurations[_windowId] = const ui.ViewConfiguration(); + if (_isUrlStrategySet) { + _browserHistory = createHistoryForExistingState(_customUrlStrategy); + } } + final Object _windowId; + @override - double get devicePixelRatio => _debugDevicePixelRatio ?? browserDevicePixelRatio; + final ui.PlatformDispatcher platformDispatcher; - /// Returns device pixel ratio returned by browser. - static double get browserDevicePixelRatio { - double? ratio = html.window.devicePixelRatio as double?; - // Guard against WebOS returning 0 and other browsers returning null. - return (ratio == null || ratio == 0.0) ? 1.0 : ratio; + /// Handles the browser history integration to allow users to use the back + /// button, etc. + BrowserHistory get browserHistory { + return _browserHistory ??= + createHistoryForExistingState(_urlStrategyForInitialization); } - /// Overrides the default device pixel ratio. - /// - /// This is useful in tests to emulate screens of different dimensions. - void debugOverrideDevicePixelRatio(double value) { - _debugDevicePixelRatio = value; + UrlStrategy? get _urlStrategyForInitialization { + final UrlStrategy? urlStrategy = + _isUrlStrategySet ? _customUrlStrategy : _createDefaultUrlStrategy(); + // Prevent any further customization of URL strategy. + _isUrlStrategySet = true; + return urlStrategy; } - double? _debugDevicePixelRatio; + BrowserHistory? + _browserHistory; // Must be either SingleEntryBrowserHistory or MultiEntriesBrowserHistory. + + Future _useSingleEntryBrowserHistory() async { + // Recreate the browser history mode that's appropriate for the existing + // history state. + // + // If it happens to be a single-entry one, then there's nothing further to do. + // + // But if it's a multi-entry one, it will be torn down below and replaced + // with a single-entry history. + // + // See: https://github.com/flutter/flutter/issues/79241 + _browserHistory ??= + createHistoryForExistingState(_urlStrategyForInitialization); + + if (_browserHistory is SingleEntryBrowserHistory) { + return; + } + + // At this point, we know that `_browserHistory` is a non-null + // `MultiEntriesBrowserHistory` instance. + final UrlStrategy? strategy = _browserHistory?.urlStrategy; + await _browserHistory?.tearDown(); + _browserHistory = SingleEntryBrowserHistory(urlStrategy: strategy); + } + + Future _useMultiEntryBrowserHistory() async { + // Recreate the browser history mode that's appropriate for the existing + // history state. + // + // If it happens to be a multi-entry one, then there's nothing further to do. + // + // But if it's a single-entry one, it will be torn down below and replaced + // with a multi-entry history. + // + // See: https://github.com/flutter/flutter/issues/79241 + _browserHistory ??= + createHistoryForExistingState(_urlStrategyForInitialization); + + if (_browserHistory is MultiEntriesBrowserHistory) { + return; + } + + // At this point, we know that `_browserHistory` is a non-null + // `SingleEntryBrowserHistory` instance. + final UrlStrategy? strategy = _browserHistory?.urlStrategy; + await _browserHistory?.tearDown(); + _browserHistory = MultiEntriesBrowserHistory(urlStrategy: strategy); + } + + @visibleForTesting + Future debugInitializeHistory( + UrlStrategy? strategy, { + required bool useSingle, + }) async { + // Prevent any further customization of URL strategy. + _isUrlStrategySet = true; + await _browserHistory?.tearDown(); + if (useSingle) { + _browserHistory = SingleEntryBrowserHistory(urlStrategy: strategy); + } else { + _browserHistory = MultiEntriesBrowserHistory(urlStrategy: strategy); + } + } + + Future resetHistory() async { + await _browserHistory?.tearDown(); + _browserHistory = null; + // Reset the globals too. + _isUrlStrategySet = false; + _customUrlStrategy = null; + } + + Future _endOfTheLine = Future.value(null); + + Future _waitInTheLine(_HandleMessageCallBack callback) async { + final Future currentPosition = _endOfTheLine; + final Completer completer = Completer(); + _endOfTheLine = completer.future; + await currentPosition; + bool result = false; + try { + result = await callback(); + } finally { + completer.complete(); + } + return result; + } + + Future handleNavigationMessage(ByteData? data) async { + return _waitInTheLine(() async { + final MethodCall decoded = const JSONMethodCodec().decodeMethodCall(data); + final Map? arguments = decoded.arguments as Map?; + switch (decoded.method) { + case 'selectMultiEntryHistory': + await _useMultiEntryBrowserHistory(); + return true; + case 'selectSingleEntryHistory': + await _useSingleEntryBrowserHistory(); + return true; + // the following cases assert that arguments are not null + case 'routeUpdated': // deprecated + assert(arguments != null); + await _useSingleEntryBrowserHistory(); + browserHistory.setRouteName(arguments!.tryString('routeName')); + return true; + case 'routeInformationUpdated': + assert(arguments != null); + browserHistory.setRouteName( + arguments!.tryString('location'), + state: arguments['state'], + replace: arguments.tryBool('replace') ?? false, + ); + return true; + } + return false; + }); + } + + @override + ui.ViewConfiguration get viewConfiguration { + final EnginePlatformDispatcher engineDispatcher = + platformDispatcher as EnginePlatformDispatcher; + assert(engineDispatcher.windowConfigurations.containsKey(_windowId)); + return engineDispatcher.windowConfigurations[_windowId] ?? + const ui.ViewConfiguration(); + } @override ui.Size get physicalSize { if (_physicalSize == null) { - _computePhysicalSize(); + computePhysicalSize(); } assert(_physicalSize != null); return _physicalSize!; @@ -57,7 +215,7 @@ class EngineWindow extends ui.Window { /// /// This function is expensive. It triggers browser layout if there are /// pending DOM writes. - void _computePhysicalSize() { + void computePhysicalSize() { bool override = false; assert(() { @@ -72,9 +230,27 @@ class EngineWindow extends ui.Window { double windowInnerWidth; double windowInnerHeight; final html.VisualViewport? viewport = html.window.visualViewport; + if (viewport != null) { - windowInnerWidth = viewport.width!.toDouble() * devicePixelRatio; - windowInnerHeight = viewport.height!.toDouble() * devicePixelRatio; + if (operatingSystem == OperatingSystem.iOs) { + /// Chrome on iOS reports incorrect viewport.height when app + /// starts in portrait orientation and the phone is rotated to + /// landscape. + /// + /// We instead use documentElement clientWidth/Height to read + /// accurate physical size. VisualViewport api is only used during + /// text editing to make sure inset is correctly reported to + /// framework. + final double docWidth = + html.document.documentElement!.clientWidth.toDouble(); + final double docHeight = + html.document.documentElement!.clientHeight.toDouble(); + windowInnerWidth = docWidth * devicePixelRatio; + windowInnerHeight = docHeight * devicePixelRatio; + } else { + windowInnerWidth = viewport.width!.toDouble() * devicePixelRatio; + windowInnerHeight = viewport.height!.toDouble() * devicePixelRatio; + } } else { windowInnerWidth = html.window.innerWidth! * devicePixelRatio; windowInnerHeight = html.window.innerHeight! * devicePixelRatio; @@ -86,11 +262,21 @@ class EngineWindow extends ui.Window { } } - void computeOnScreenKeyboardInsets() { + /// Forces the window to recompute its physical size. Useful for tests. + void debugForceResize() { + computePhysicalSize(); + } + + void computeOnScreenKeyboardInsets(bool isEditingOnMobile) { double windowInnerHeight; final html.VisualViewport? viewport = html.window.visualViewport; if (viewport != null) { - windowInnerHeight = viewport.height!.toDouble() * devicePixelRatio; + if (operatingSystem == OperatingSystem.iOs && !isEditingOnMobile) { + windowInnerHeight = + html.document.documentElement!.clientHeight * devicePixelRatio; + } else { + windowInnerHeight = viewport.height!.toDouble() * devicePixelRatio; + } } else { windowInnerHeight = html.window.innerHeight! * devicePixelRatio; } @@ -117,7 +303,8 @@ class EngineWindow extends ui.Window { double height = 0; double width = 0; if (html.window.visualViewport != null) { - height = html.window.visualViewport!.height!.toDouble() * devicePixelRatio; + height = + html.window.visualViewport!.height!.toDouble() * devicePixelRatio; width = html.window.visualViewport!.width!.toDouble() * devicePixelRatio; } else { height = html.window.innerHeight! * devicePixelRatio; @@ -126,7 +313,7 @@ class EngineWindow extends ui.Window { // This method compares the new dimensions with the previous ones. // Return false if the previous dimensions are not set. - if(_physicalSize != null) { + if (_physicalSize != null) { // First confirm both height and width are effected. if (_physicalSize!.height != height && _physicalSize!.width != width) { // If prior to rotation height is bigger than width it should be the @@ -150,690 +337,62 @@ class EngineWindow extends ui.Window { /// Overrides the value of [physicalSize] in tests. ui.Size? webOnlyDebugPhysicalSizeOverride; +} - /// Handles the browser history integration to allow users to use the back - /// button, etc. - @visibleForTesting - BrowserHistory get browserHistory => _browserHistory; - BrowserHistory _browserHistory = MultiEntriesBrowserHistory(); - - @visibleForTesting - Future debugSwitchBrowserHistory({required bool useSingle}) async { - if (useSingle) - await _useSingleEntryBrowserHistory(); - else - await _useMultiEntryBrowserHistory(); - } - - /// This function should only be used for test setup. In real application, we - /// only allow one time switch from the MultiEntriesBrowserHistory to - /// the SingleEntryBrowserHistory to prevent the application to switch back - /// forth between router and non-router. - Future _useMultiEntryBrowserHistory() async { - if (_browserHistory is MultiEntriesBrowserHistory) { - return; - } - final LocationStrategy? strategy = _browserHistory.locationStrategy; - if (strategy != null) - await _browserHistory.setLocationStrategy(null); - _browserHistory = MultiEntriesBrowserHistory(); - if (strategy != null) - await _browserHistory.setLocationStrategy(strategy); - } - - Future _useSingleEntryBrowserHistory() async { - if (_browserHistory is SingleEntryBrowserHistory) { - return; - } - final LocationStrategy? strategy = _browserHistory.locationStrategy; - if (strategy != null) - await _browserHistory.setLocationStrategy(null); - _browserHistory = SingleEntryBrowserHistory(); - if (strategy != null) - await _browserHistory.setLocationStrategy(strategy); - } - - /// Simulates clicking the browser's back button. - Future webOnlyBack() => _browserHistory.back(); - - /// Lazily initialized when the `defaultRouteName` getter is invoked. - /// - /// The reason for the lazy initialization is to give enough time for the app to set [locationStrategy] - /// in `lib/src/ui/initialization.dart`. - String? _defaultRouteName; - - @override - String get defaultRouteName => _defaultRouteName ??= _browserHistory.currentPath; - - @override - void scheduleFrame() { - if (scheduleFrameCallback == null) { - throw new Exception( - 'scheduleFrameCallback must be initialized first.'); - } - scheduleFrameCallback!(); - } - - /// Change the strategy to use for handling browser history location. - /// Setting this member will automatically update [_browserHistory]. - /// - /// By setting this to null, the browser history will be disabled. - set locationStrategy(LocationStrategy? strategy) { - _browserHistory.setLocationStrategy(strategy); - } - - /// Returns the currently active location strategy. - @visibleForTesting - LocationStrategy? get locationStrategy => _browserHistory.locationStrategy; - - @override - ui.VoidCallback? get onTextScaleFactorChanged => _onTextScaleFactorChanged; - ui.VoidCallback? _onTextScaleFactorChanged; - Zone? _onTextScaleFactorChangedZone; - @override - set onTextScaleFactorChanged(ui.VoidCallback? callback) { - _onTextScaleFactorChanged = callback; - _onTextScaleFactorChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnTextScaleFactorChanged() { - _invoke(_onTextScaleFactorChanged, _onTextScaleFactorChangedZone); - } - - @override - ui.VoidCallback? get onPlatformBrightnessChanged => - _onPlatformBrightnessChanged; - ui.VoidCallback? _onPlatformBrightnessChanged; - Zone? _onPlatformBrightnessChangedZone; - @override - set onPlatformBrightnessChanged(ui.VoidCallback? callback) { - _onPlatformBrightnessChanged = callback; - _onPlatformBrightnessChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnPlatformBrightnessChanged() { - _invoke(_onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); - } - - @override - ui.VoidCallback? get onMetricsChanged => _onMetricsChanged; - ui.VoidCallback? _onMetricsChanged; - Zone _onMetricsChangedZone = Zone.root; - @override - set onMetricsChanged(ui.VoidCallback? callback) { - _onMetricsChanged = callback; - _onMetricsChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnMetricsChanged() { - if (window._onMetricsChanged != null) { - _invoke(_onMetricsChanged, _onMetricsChangedZone); - } - } - - @override - ui.VoidCallback? get onLocaleChanged => _onLocaleChanged; - ui.VoidCallback? _onLocaleChanged; - Zone? _onLocaleChangedZone; - @override - set onLocaleChanged(ui.VoidCallback? callback) { - _onLocaleChanged = callback; - _onLocaleChangedZone = Zone.current; - } - - /// The locale used when we fail to get the list from the browser. - static const _defaultLocale = const ui.Locale('en', 'US'); +typedef _JsSetUrlStrategy = void Function(JsUrlStrategy?); + +/// A JavaScript hook to customize the URL strategy of a Flutter app. +// +// Keep this js name in sync with flutter_web_plugins. Find it at: +// https://github.com/flutter/flutter/blob/custom_location_strategy/packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart +// +// TODO(mdebbar): Add integration test https://github.com/flutter/flutter/issues/66852 +@JS('_flutter_web_set_location_strategy') +external set jsSetUrlStrategy(_JsSetUrlStrategy? newJsSetUrlStrategy); + +UrlStrategy? _createDefaultUrlStrategy() { + return ui.debugEmulateFlutterTesterEnvironment + ? TestUrlStrategy.fromEntry(const TestHistoryEntry('default', null, '/')) + : const HashUrlStrategy(); +} - /// We use the first locale in the [locales] list instead of the browser's - /// built-in `navigator.language` because browsers do not agree on the - /// implementation. - /// - /// See also: - /// - /// * https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages, - /// which explains browser quirks in the implementation notes. - @override - ui.Locale get locale => _locales!.first; +/// The Web implementation of [ui.SingletonFlutterWindow]. +class EngineSingletonFlutterWindow extends EngineFlutterWindow { + EngineSingletonFlutterWindow( + Object windowId, ui.PlatformDispatcher platformDispatcher) + : super(windowId, platformDispatcher); @override - List? get locales => _locales; - List? _locales = parseBrowserLanguages(); + double get devicePixelRatio => + _debugDevicePixelRatio ?? + EnginePlatformDispatcher.browserDevicePixelRatio; - /// Sets locales to `null`. + /// Overrides the default device pixel ratio. /// - /// `null` is not a valid value for locales. This is only used for testing - /// locale update logic. - void debugResetLocales() { - _locales = null; - } - - // Called by DomRenderer when browser languages change. - void _updateLocales() { - _locales = parseBrowserLanguages(); - } - - static List parseBrowserLanguages() { - // TODO(yjbanov): find a solution for IE - var languages = html.window.navigator.languages; - if (languages == null || languages.isEmpty) { - // To make it easier for the app code, let's not leave the locales list - // empty. This way there's fewer corner cases for apps to handle. - return const [_defaultLocale]; - } - - final List locales = []; - for (final String language in languages) { - final List parts = language.split('-'); - if (parts.length > 1) { - locales.add(ui.Locale(parts.first, parts.last)); - } else { - locales.add(ui.Locale(language)); - } - } - - assert(locales.isNotEmpty); - return locales; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnLocaleChanged() { - _invoke(_onLocaleChanged, _onLocaleChangedZone); - } - - @override - ui.FrameCallback? get onBeginFrame => _onBeginFrame; - ui.FrameCallback? _onBeginFrame; - Zone? _onBeginFrameZone; - @override - set onBeginFrame(ui.FrameCallback? callback) { - _onBeginFrame = callback; - _onBeginFrameZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnBeginFrame(Duration duration) { - _invoke1(_onBeginFrame, _onBeginFrameZone, duration); - } - - @override - ui.TimingsCallback? get onReportTimings => _onReportTimings; - ui.TimingsCallback? _onReportTimings; - Zone? _onReportTimingsZone; - @override - set onReportTimings(ui.TimingsCallback? callback) { - _onReportTimings = callback; - _onReportTimingsZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnReportTimings(List timings) { - _invoke1>( - _onReportTimings, _onReportTimingsZone, timings); - } - - @override - ui.VoidCallback? get onDrawFrame => _onDrawFrame; - ui.VoidCallback? _onDrawFrame; - Zone? _onDrawFrameZone; - @override - set onDrawFrame(ui.VoidCallback? callback) { - _onDrawFrame = callback; - _onDrawFrameZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnDrawFrame() { - _invoke(_onDrawFrame, _onDrawFrameZone); - } - - @override - ui.PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; - ui.PointerDataPacketCallback? _onPointerDataPacket; - Zone? _onPointerDataPacketZone; - @override - set onPointerDataPacket(ui.PointerDataPacketCallback? callback) { - _onPointerDataPacket = callback; - _onPointerDataPacketZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnPointerDataPacket(ui.PointerDataPacket packet) { - _invoke1( - _onPointerDataPacket, _onPointerDataPacketZone, packet); - } - - @override - ui.VoidCallback? get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - ui.VoidCallback? _onSemanticsEnabledChanged; - Zone? _onSemanticsEnabledChangedZone; - @override - set onSemanticsEnabledChanged(ui.VoidCallback? callback) { - _onSemanticsEnabledChanged = callback; - _onSemanticsEnabledChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnSemanticsEnabledChanged() { - _invoke(_onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); - } - - @override - ui.SemanticsActionCallback? get onSemanticsAction => _onSemanticsAction; - ui.SemanticsActionCallback? _onSemanticsAction; - Zone? _onSemanticsActionZone; - @override - set onSemanticsAction(ui.SemanticsActionCallback? callback) { - _onSemanticsAction = callback; - _onSemanticsActionZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnSemanticsAction( - int id, ui.SemanticsAction action, ByteData? args) { - _invoke3( - _onSemanticsAction, _onSemanticsActionZone, id, action, args); - } - - @override - ui.VoidCallback? get onAccessibilityFeaturesChanged => - _onAccessibilityFeaturesChanged; - ui.VoidCallback? _onAccessibilityFeaturesChanged; - Zone? _onAccessibilityFeaturesChangedZone; - @override - set onAccessibilityFeaturesChanged(ui.VoidCallback? callback) { - _onAccessibilityFeaturesChanged = callback; - _onAccessibilityFeaturesChangedZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnAccessibilityFeaturesChanged() { - _invoke( - _onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); - } - - @override - ui.PlatformMessageCallback? get onPlatformMessage => _onPlatformMessage; - ui.PlatformMessageCallback? _onPlatformMessage; - Zone? _onPlatformMessageZone; - @override - set onPlatformMessage(ui.PlatformMessageCallback? callback) { - _onPlatformMessage = callback; - _onPlatformMessageZone = Zone.current; - } - - /// Engine code should use this method instead of the callback directly. - /// Otherwise zones won't work properly. - void invokeOnPlatformMessage( - String name, ByteData? data, ui.PlatformMessageResponseCallback callback) { - _invoke3( - _onPlatformMessage, - _onPlatformMessageZone, - name, - data, - callback, - ); - } - - @override - void sendPlatformMessage( - String name, - ByteData? data, - ui.PlatformMessageResponseCallback? callback, - ) { - _sendPlatformMessage( - name, data, _zonedPlatformMessageResponseCallback(callback)); - } - - /// Wraps the given [callback] in another callback that ensures that the - /// original callback is called in the zone it was registered in. - static ui.PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback? callback) { - if (callback == null) { - return null; - } - - // Store the zone in which the callback is being registered. - final Zone registrationZone = Zone.current; - - return (ByteData? data) { - registrationZone.runUnaryGuarded(callback, data); - }; + /// This is useful in tests to emulate screens of different dimensions. + void debugOverrideDevicePixelRatio(double value) { + _debugDevicePixelRatio = value; } - void _sendPlatformMessage( - String name, - ByteData? data, - ui.PlatformMessageResponseCallback? callback, - ) { - // In widget tests we want to bypass processing of platform messages. - if (assertionsEnabled && ui.debugEmulateFlutterTesterEnvironment) { - return; - } - - if (_debugPrintPlatformMessages) { - print('Sent platform message on channel: "$name"'); - } - - if (assertionsEnabled && name == 'flutter/debug-echo') { - // Echoes back the data unchanged. Used for testing purpopses. - _replyToPlatformMessage(callback, data); - return; - } - - switch (name) { - /// This should be in sync with shell/common/shell.cc - case 'flutter/skia': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - switch (decoded.method) { - case 'Skia.setResourceCacheMaxBytes': - if (decoded.arguments is int) { - rasterizer?.setSkiaResourceCacheMaxBytes(decoded.arguments); - } - break; - } - - return; - case 'flutter/assets': - assert(ui.webOnlyAssetManager != null); // ignore: unnecessary_null_comparison - final String url = utf8.decode(data!.buffer.asUint8List()); - ui.webOnlyAssetManager.load(url).then((ByteData assetData) { - _replyToPlatformMessage(callback, assetData); - }, onError: (dynamic error) { - html.window.console - .warn('Error while trying to load an asset: $error'); - _replyToPlatformMessage(callback, null); - }); - return; - - case 'flutter/platform': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - switch (decoded.method) { - case 'SystemNavigator.pop': - _browserHistory.exit().then((_) { - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - }); - return; - case 'HapticFeedback.vibrate': - final String? type = decoded.arguments; - domRenderer.vibrate(_getHapticFeedbackDuration(type)); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - return; - case 'SystemChrome.setApplicationSwitcherDescription': - final Map arguments = decoded.arguments; - domRenderer.setTitle(arguments['label']); - domRenderer.setThemeColor(ui.Color(arguments['primaryColor'])); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - return; - case 'SystemChrome.setPreferredOrientations': - final List? arguments = decoded.arguments; - domRenderer.setPreferredOrientation(arguments).then((bool success) { - _replyToPlatformMessage(callback, - codec.encodeSuccessEnvelope(success)); - }); - return; - case 'SystemSound.play': - // There are no default system sounds on web. - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - return; - case 'Clipboard.setData': - ClipboardMessageHandler().setDataMethodCall(decoded, callback); - return; - case 'Clipboard.getData': - ClipboardMessageHandler().getDataMethodCall(callback); - return; - } - break; - - // Dispatched by the bindings to delay service worker initialization. - case 'flutter/service_worker': - html.window.dispatchEvent(html.Event('flutter-first-frame')); - return; - - case 'flutter/textinput': - textEditing.channel.handleTextInput(data, callback); - return; - - case 'flutter/mousecursor': - const MethodCodec codec = StandardMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - final Map? arguments = decoded.arguments; - switch (decoded.method) { - case 'activateSystemCursor': - MouseCursor.instance!.activateSystemCursor(arguments!['kind']); - } - return; - - case 'flutter/web_test_e2e': - const MethodCodec codec = JSONMethodCodec(); - _replyToPlatformMessage( - callback, - codec.encodeSuccessEnvelope( - _handleWebTestEnd2EndMessage(codec, data))); - return; - - case 'flutter/platform_views': - if (experimentalUseSkia) { - rasterizer!.surface.viewEmbedder.handlePlatformViewCall(data, callback); - } else { - ui.handlePlatformViewCall(data!, callback!); - } - return; - - case 'flutter/accessibility': - // In widget tests we want to bypass processing of platform messages. - final StandardMessageCodec codec = StandardMessageCodec(); - accessibilityAnnouncements.handleMessage(codec, data); - _replyToPlatformMessage(callback, codec.encodeMessage(true)); - return; - - case 'flutter/navigation': - const MethodCodec codec = JSONMethodCodec(); - final MethodCall decoded = codec.decodeMethodCall(data); - final Map message = decoded.arguments as Map; - switch (decoded.method) { - case 'routeUpdated': - _useSingleEntryBrowserHistory().then((void data) { - _browserHistory.setRouteName(message['routeName']); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - }); - break; - case 'routeInformationUpdated': - assert(_browserHistory is MultiEntriesBrowserHistory); - _browserHistory.setRouteName( - message['location'], - state: message['state'], - ); - _replyToPlatformMessage( - callback, codec.encodeSuccessEnvelope(true)); - break; - } - // As soon as Flutter starts taking control of the app navigation, we - // should reset [_defaultRouteName] to "/" so it doesn't have any - // further effect after this point. - _defaultRouteName = '/'; - return; - } - - if (pluginMessageCallHandler != null) { - pluginMessageCallHandler!(name, data, callback); - return; - } - - // Passing [null] to [callback] indicates that the platform message isn't - // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is - // handled. - _replyToPlatformMessage(callback, null); - } + double? _debugDevicePixelRatio; +} - int _getHapticFeedbackDuration(String? type) { - switch (type) { - case 'HapticFeedbackType.lightImpact': - return DomRenderer.vibrateLightImpact; - case 'HapticFeedbackType.mediumImpact': - return DomRenderer.vibrateMediumImpact; - case 'HapticFeedbackType.heavyImpact': - return DomRenderer.vibrateHeavyImpact; - case 'HapticFeedbackType.selectionClick': - return DomRenderer.vibrateSelectionClick; - default: - return DomRenderer.vibrateLongPress; - } - } +/// A type of [FlutterView] that can be hosted inside of a [FlutterWindow]. +class EngineFlutterWindowView extends ui.FlutterWindow { + EngineFlutterWindowView._(this._viewId, this.platformDispatcher); - /// In Flutter, platform messages are exchanged between threads so the - /// messages and responses have to be exchanged asynchronously. We simulate - /// that by adding a zero-length delay to the reply. - void _replyToPlatformMessage( - ui.PlatformMessageResponseCallback? callback, - ByteData? data, - ) { - Future.delayed(Duration.zero).then((_) { - if (callback != null) { - callback(data); - } - }); - } + final Object _viewId; @override - ui.Brightness get platformBrightness => _platformBrightness; - ui.Brightness _platformBrightness = ui.Brightness.light; - - /// Updates [_platformBrightness] and invokes [onPlatformBrightnessChanged] - /// callback if [_platformBrightness] changed. - void _updatePlatformBrightness(ui.Brightness newPlatformBrightness) { - ui.Brightness previousPlatformBrightness = _platformBrightness; - _platformBrightness = newPlatformBrightness; - - if (previousPlatformBrightness != _platformBrightness && - onPlatformBrightnessChanged != null) { - invokeOnPlatformBrightnessChanged(); - } - } - - /// Reference to css media query that indicates the user theme preference on the web. - final html.MediaQueryList _brightnessMediaQuery = - html.window.matchMedia('(prefers-color-scheme: dark)'); - - /// A callback that is invoked whenever [_brightnessMediaQuery] changes value. - /// - /// Updates the [_platformBrightness] with the new user preference. - html.EventListener? _brightnessMediaQueryListener; - - /// Set the callback function for listening changes in [_brightnessMediaQuery] value. - void _addBrightnessMediaQueryListener() { - _updatePlatformBrightness(_brightnessMediaQuery.matches - ? ui.Brightness.dark - : ui.Brightness.light); - - _brightnessMediaQueryListener = (html.Event event) { - final html.MediaQueryListEvent mqEvent = event as html.MediaQueryListEvent; - _updatePlatformBrightness( - mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light); - }; - _brightnessMediaQuery.addListener(_brightnessMediaQueryListener); - registerHotRestartListener(() { - _removeBrightnessMediaQueryListener(); - }); - } - - /// Remove the callback function for listening changes in [_brightnessMediaQuery] value. - void _removeBrightnessMediaQueryListener() { - _brightnessMediaQuery.removeListener(_brightnessMediaQueryListener); - _brightnessMediaQueryListener = null; - } + final ui.PlatformDispatcher platformDispatcher; @override - void render(ui.Scene scene) { - if (experimentalUseSkia) { - final LayerScene layerScene = scene as LayerScene; - rasterizer!.draw(layerScene.layerTree); - } else { - final SurfaceScene surfaceScene = scene as SurfaceScene; - domRenderer.renderScene(surfaceScene.webOnlyRootElement); - } - } - - @visibleForTesting - late Rasterizer? rasterizer = experimentalUseSkia ? Rasterizer(Surface(HtmlViewEmbedder())) : null; -} - -bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData? data) { - final MethodCall decoded = codec.decodeMethodCall(data); - double ratio = double.parse(decoded.arguments); - switch (decoded.method) { - case 'setDevicePixelRatio': - window.debugOverrideDevicePixelRatio(ratio); - window.onMetricsChanged!(); - return true; - } - return false; -} - -/// Invokes [callback] inside the given [zone]. -void _invoke(void callback()?, Zone? zone) { - if (callback == null) { - return; - } - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(); - } else { - zone!.runGuarded(callback); - } -} - -/// Invokes [callback] inside the given [zone] passing it [arg]. -void _invoke1(void callback(A a)?, Zone? zone, A arg) { - if (callback == null) { - return; - } - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(arg); - } else { - zone!.runUnaryGuarded(callback, arg); - } -} - -/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void _invoke3( - void callback(A1 a1, A2 a2, A3 a3)?, Zone? zone, A1 arg1, A2 arg2, A3 arg3) { - if (callback == null) { - return; - } - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(arg1, arg2, arg3); - } else { - zone!.runGuarded(() { - callback(arg1, arg2, arg3); - }); + ui.ViewConfiguration get viewConfiguration { + final EnginePlatformDispatcher engineDispatcher = + platformDispatcher as EnginePlatformDispatcher; + assert(engineDispatcher.windowConfigurations.containsKey(_viewId)); + return engineDispatcher.windowConfigurations[_viewId] ?? + const ui.ViewConfiguration(); } } @@ -842,7 +401,8 @@ void _invoke3( /// `dart:ui` window delegates to this value. However, this value has a wider /// API surface, providing Web-specific functionality that the standard /// `dart:ui` version does not. -final EngineWindow window = EngineWindow(); +final EngineSingletonFlutterWindow window = + EngineSingletonFlutterWindow(0, EnginePlatformDispatcher.instance); /// The Web implementation of [ui.WindowPadding]. class WindowPadding implements ui.WindowPadding { @@ -853,8 +413,12 @@ class WindowPadding implements ui.WindowPadding { required this.bottom, }); + @override final double left; + @override final double top; + @override final double right; + @override final double bottom; } diff --git a/lib/web_ui/lib/src/ui/annotations.dart b/lib/web_ui/lib/src/ui/annotations.dart index c2670d5282cc7..3dbd5823e060d 100644 --- a/lib/web_ui/lib/src/ui/annotations.dart +++ b/lib/web_ui/lib/src/ui/annotations.dart @@ -4,7 +4,6 @@ // TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. -// @dart = 2.10 part of ui; // TODO(dnfield): Update this if/when we default this to on in the tool, diff --git a/lib/web_ui/lib/src/ui/canvas.dart b/lib/web_ui/lib/src/ui/canvas.dart index 1699a1a287410..bfdbb949627ae 100644 --- a/lib/web_ui/lib/src/ui/canvas.dart +++ b/lib/web_ui/lib/src/ui/canvas.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; enum PointMode { @@ -30,7 +29,7 @@ class Vertices { List? colors, List? indices, }) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkVertices( mode, positions, @@ -53,7 +52,7 @@ class Vertices { Int32List? colors, Uint16List? indices, }) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkVertices.raw( mode, positions, @@ -73,7 +72,7 @@ class Vertices { abstract class PictureRecorder { factory PictureRecorder() { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkPictureRecorder(); } else { return engine.EnginePictureRecorder(); @@ -85,10 +84,11 @@ abstract class PictureRecorder { abstract class Canvas { factory Canvas(PictureRecorder recorder, [Rect? cullRect]) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CanvasKitCanvas(recorder, cullRect); } else { - return engine.SurfaceCanvas(recorder as engine.EnginePictureRecorder, cullRect); + return engine.SurfaceCanvas( + recorder as engine.EnginePictureRecorder, cullRect); } } void save(); @@ -100,7 +100,8 @@ abstract class Canvas { void rotate(double radians); void skew(double sx, double sy); void transform(Float64List matrix4); - void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}); + void clipRect(Rect rect, + {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}); void clipRRect(RRect rrect, {bool doAntiAlias = true}); void clipPath(Path path, {bool doAntiAlias = true}); void drawColor(Color color, BlendMode blendMode); @@ -111,7 +112,8 @@ abstract class Canvas { void drawDRRect(RRect outer, RRect inner, Paint paint); void drawOval(Rect rect, Paint paint); void drawCircle(Offset c, double radius, Paint paint); - void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint); + void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, + Paint paint); void drawPath(Path path, Paint paint); void drawImage(Image image, Offset offset, Paint paint); void drawImageRect(Image image, Rect src, Rect dst, Paint paint); diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index 9ecb59a4b72fa..22b5b6169558f 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -2,142 +2,249 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 + +// This is identical to ../../../../ui/channel_buffers.dart with the +// following exceptions: +// +// * All comments except this one are removed. +// * _invokeX is replaced with engine.invokeX (X=1,2) +// * _printDebug is replaced with print in an assert. + part of ui; +typedef DrainChannelCallback = Future Function(ByteData? data, PlatformMessageResponseCallback callback); + +typedef ChannelCallback = void Function(ByteData? data, PlatformMessageResponseCallback callback); + +class _ChannelCallbackRecord { + _ChannelCallbackRecord(this._callback) : _zone = Zone.current; + final ChannelCallback _callback; + final Zone _zone; + + void invoke(ByteData? dataArg, PlatformMessageResponseCallback callbackArg) { + engine.invoke2(_callback, _zone, dataArg, callbackArg); + } +} + class _StoredMessage { - _StoredMessage(this._data, this._callback); - final ByteData? _data; - ByteData? get data => _data; + _StoredMessage(this.data, this._callback) : _zone = Zone.current; + + final ByteData? data; + final PlatformMessageResponseCallback _callback; - PlatformMessageResponseCallback get callback => _callback; + + final Zone _zone; + + void invoke(ByteData? dataArg) { + engine.invoke1(_callback, _zone, dataArg); + } } -class _RingBuffer { - final collection.ListQueue _queue; +class _Channel { + _Channel([ this._capacity = ChannelBuffers.kDefaultBufferSize ]) + : _queue = collection.ListQueue<_StoredMessage>(_capacity); + + final collection.ListQueue<_StoredMessage> _queue; - _RingBuffer(this._capacity) : _queue = collection.ListQueue(_capacity); int get length => _queue.length; - int _capacity; + + bool debugEnableDiscardWarnings = true; + int get capacity => _capacity; - bool get isEmpty => _queue.isEmpty; - Function(T)? _dropItemCallback; - set dropItemCallback(Function(T) callback) { - _dropItemCallback = callback; + int _capacity; + set capacity(int newSize) { + _capacity = newSize; + _dropOverflowMessages(newSize); } - bool push(T val) { + bool _draining = false; + + bool push(_StoredMessage message) { + if (!_draining && _channelCallbackRecord != null) { + assert(_queue.isEmpty); + _channelCallbackRecord!.invoke(message.data, message.invoke); + return false; + } if (_capacity <= 0) { - return true; - } else { - final int overflowCount = _dropOverflowItems(_capacity - 1); - _queue.addLast(val); - return overflowCount > 0; + return debugEnableDiscardWarnings; } + final bool result = _dropOverflowMessages(_capacity - 1); + _queue.addLast(message); + return result; } - T? pop() { - return _queue.isEmpty ? null : _queue.removeFirst(); - } + _StoredMessage pop() => _queue.removeFirst(); - int _dropOverflowItems(int lengthLimit) { - int result = 0; + bool _dropOverflowMessages(int lengthLimit) { + bool result = false; while (_queue.length > lengthLimit) { - final T item = _queue.removeFirst(); - _dropItemCallback?.call(item); - result += 1; + final _StoredMessage message = _queue.removeFirst(); + message.invoke(null); // send empty reply to the plugin side + result = true; } return result; } - int resize(int newSize) { - _capacity = newSize; - return _dropOverflowItems(newSize); + _ChannelCallbackRecord? _channelCallbackRecord; + + void setListener(ChannelCallback callback) { + final bool needDrain = _channelCallbackRecord == null; + _channelCallbackRecord = _ChannelCallbackRecord(callback); + if (needDrain && !_draining) + _drain(); } -} -typedef DrainChannelCallback = Future Function(ByteData?, PlatformMessageResponseCallback); + void clearListener() { + _channelCallbackRecord = null; + } + + void _drain() { + assert(!_draining); + _draining = true; + scheduleMicrotask(_drainStep); + } + + void _drainStep() { + assert(_draining); + if (_queue.isNotEmpty && _channelCallbackRecord != null) { + final _StoredMessage message = pop(); + _channelCallbackRecord!.invoke(message.data, message.invoke); + scheduleMicrotask(_drainStep); + } else { + _draining = false; + } + } +} class ChannelBuffers { + ChannelBuffers(); + static const int kDefaultBufferSize = 1; static const String kControlChannelName = 'dev.flutter/channel-buffers'; - final Map?> _messages = - ?>{}; - _RingBuffer<_StoredMessage> _makeRingBuffer(int size) { - final _RingBuffer<_StoredMessage> result = _RingBuffer<_StoredMessage>(size); - result.dropItemCallback = _onDropItem; - return result; + final Map _channels = {}; + + void push(String name, ByteData? data, PlatformMessageResponseCallback callback) { + final _Channel channel = _channels.putIfAbsent(name, () => _Channel()); + if (channel.push(_StoredMessage(data, callback))) { + assert(() { + print( + 'A message on the $name channel was discarded before it could be handled.\n' + 'This happens when a plugin sends messages to the framework side before the ' + 'framework has had an opportunity to register a listener. See the ChannelBuffers ' + 'API documentation for details on how to configure the channel to expect more ' + 'messages, or to expect messages to get discarded:\n' + ' https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html' + ); + return true; + }()); + } } - void _onDropItem(_StoredMessage message) { - message.callback(null); + void setListener(String name, ChannelCallback callback) { + final _Channel channel = _channels.putIfAbsent(name, () => _Channel()); + channel.setListener(callback); } - bool push(String channel, ByteData? data, PlatformMessageResponseCallback callback) { - _RingBuffer<_StoredMessage>? queue = _messages[channel]; - if (queue == null) { - queue = _makeRingBuffer(kDefaultBufferSize); - _messages[channel] = queue; - } - final bool didOverflow = queue.push(_StoredMessage(data, callback)); - if (didOverflow) { - // TODO(aaclarke): Update this message to include instructions on how to resize - // the buffer once that is available to users and print in all engine builds - // after we verify that dropping messages isn't part of normal execution. - _debugPrintWarning('Overflow on channel: $channel. ' - 'Messages on this channel are being discarded in FIFO fashion. ' - 'The engine may not be running or you need to adjust ' - 'the buffer size if of the channel.'); - } - return didOverflow; + void clearListener(String name) { + final _Channel? channel = _channels[name]; + if (channel != null) + channel.clearListener(); } - _StoredMessage? _pop(String channel) { - final _RingBuffer<_StoredMessage>? queue = _messages[channel]; - final _StoredMessage? result = queue?.pop(); - return result; - } - - bool _isEmpty(String channel) { - final _RingBuffer<_StoredMessage>? queue = _messages[channel]; - return (queue == null) ? true : queue.isEmpty; + Future drain(String name, DrainChannelCallback callback) async { + final _Channel? channel = _channels[name]; + while (channel != null && !channel._queue.isEmpty) { + final _StoredMessage message = channel.pop(); + await callback(message.data, message.invoke); + } } - void _resize(String channel, int newSize) { - _RingBuffer<_StoredMessage>? queue = _messages[channel]; - if (queue == null) { - queue = _makeRingBuffer(newSize); - _messages[channel] = queue; + void handleMessage(ByteData data) { + final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + if (bytes[0] == 0x07) { // 7 = value code for string + final int methodNameLength = bytes[1]; + if (methodNameLength >= 254) // lengths greater than 253 have more elaborate encoding + throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); + int index = 2; // where we are in reading the bytes + final String methodName = utf8.decode(bytes.sublist(index, index + methodNameLength)); + index += methodNameLength; + switch (methodName) { + case 'resize': + if (bytes[index] != 0x0C) // 12 = value code for list + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)'); + index += 1; + if (bytes[index] < 0x02) // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)'); + index += 1; + if (bytes[index] != 0x07) // 7 = value code for string + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (first argument must be a string)'); + index += 1; + final int channelNameLength = bytes[index]; + if (channelNameLength >= 254) // lengths greater than 253 have more elaborate encoding + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (channel name must be less than 254 characters long)'); + index += 1; + final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); + index += channelNameLength; + if (bytes[index] != 0x03) // 3 = value code for uint32 + throw Exception('Invalid arguments for \'resize\' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)'); + index += 1; + resize(channelName, data.getUint32(index, Endian.host)); + break; + case 'overflow': + if (bytes[index] != 0x0C) // 12 = value code for list + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)'); + index += 1; + if (bytes[index] < 0x02) // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)'); + index += 1; + if (bytes[index] != 0x07) // 7 = value code for string + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (first argument must be a string)'); + index += 1; + final int channelNameLength = bytes[index]; + if (channelNameLength >= 254) // lengths greater than 253 have more elaborate encoding + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (channel name must be less than 254 characters long)'); + index += 1; + final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); + index += channelNameLength; + if (bytes[index] != 0x01 && bytes[index] != 0x02) // 1 = value code for true, 2 = value code for false + throw Exception('Invalid arguments for \'overflow\' method sent to $kControlChannelName (second argument must be a boolean)'); + allowOverflow(channelName, bytes[index] == 0x01); + break; + default: + throw Exception('Unrecognized method \'$methodName\' sent to $kControlChannelName'); + } } else { - final int numberOfDroppedMessages = queue.resize(newSize); - if (numberOfDroppedMessages > 0) { - _debugPrintWarning('Dropping messages on channel "$channel" as a result of shrinking the buffer size.'); + final List parts = utf8.decode(bytes).split('\r'); + if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') { + resize(parts[1], int.parse(parts[2])); + } else { + throw Exception('Unrecognized message $parts sent to $kControlChannelName.'); } } } - Future drain(String channel, DrainChannelCallback callback) async { - while (!_isEmpty(channel)) { - final _StoredMessage message = _pop(channel)!; - await callback(message.data, message.callback); + void resize(String name, int newSize) { + _Channel? channel = _channels[name]; + if (channel == null) { + channel = _Channel(newSize); + _channels[name] = channel; + } else { + channel.capacity = newSize; } } - String _getString(ByteData data) { - final ByteBuffer buffer = data.buffer; - final Uint8List list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - return utf8.decode(list); - } - - void handleMessage(ByteData data) { - final List command = _getString(data).split('\r'); - if (command.length == /*arity=*/2 + 1 && command[0] == 'resize') { - _resize(command[1], int.parse(command[2])); - } else { - throw Exception('Unrecognized command $command sent to $kControlChannelName.'); - } + void allowOverflow(String name, bool allowed) { + assert(() { + _Channel? channel = _channels[name]; + if (channel == null && allowed) { + channel = _Channel(); + _channels[name] = channel; + } + channel?.debugEnableDiscardWarnings = !allowed; + return true; + }()); } } diff --git a/lib/web_ui/lib/src/ui/compositing.dart b/lib/web_ui/lib/src/ui/compositing.dart index 665ebbe14c68d..de1e6cdbee255 100644 --- a/lib/web_ui/lib/src/ui/compositing.dart +++ b/lib/web_ui/lib/src/ui/compositing.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; abstract class Scene { @@ -34,60 +33,62 @@ abstract class PhysicalShapeEngineLayer implements EngineLayer {} abstract class SceneBuilder { factory SceneBuilder() { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.LayerSceneBuilder(); } else { return engine.SurfaceSceneBuilder(); } } - OffsetEngineLayer? pushOffset( + OffsetEngineLayer pushOffset( double dx, double dy, { OffsetEngineLayer? oldLayer, }); - TransformEngineLayer? pushTransform( + TransformEngineLayer pushTransform( Float64List matrix4, { TransformEngineLayer? oldLayer, }); - ClipRectEngineLayer? pushClipRect( + ClipRectEngineLayer pushClipRect( Rect rect, { Clip clipBehavior = Clip.antiAlias, ClipRectEngineLayer? oldLayer, }); - ClipRRectEngineLayer? pushClipRRect( + ClipRRectEngineLayer pushClipRRect( RRect rrect, { required Clip clipBehavior, ClipRRectEngineLayer? oldLayer, }); - ClipPathEngineLayer? pushClipPath( + ClipPathEngineLayer pushClipPath( Path path, { Clip clipBehavior = Clip.antiAlias, ClipPathEngineLayer? oldLayer, }); - OpacityEngineLayer? pushOpacity( + OpacityEngineLayer pushOpacity( int alpha, { Offset offset = Offset.zero, OpacityEngineLayer? oldLayer, }); - ColorFilterEngineLayer? pushColorFilter( + ColorFilterEngineLayer pushColorFilter( ColorFilter filter, { ColorFilterEngineLayer? oldLayer, }); - ImageFilterEngineLayer? pushImageFilter( + ImageFilterEngineLayer pushImageFilter( ImageFilter filter, { ImageFilterEngineLayer? oldLayer, }); - BackdropFilterEngineLayer? pushBackdropFilter( + BackdropFilterEngineLayer pushBackdropFilter( ImageFilter filter, { + BlendMode blendMode = BlendMode.srcOver, BackdropFilterEngineLayer? oldLayer, }); - ShaderMaskEngineLayer? pushShaderMask( + ShaderMaskEngineLayer pushShaderMask( Shader shader, Rect maskRect, BlendMode blendMode, { ShaderMaskEngineLayer? oldLayer, + FilterQuality filterQuality = FilterQuality.low, }); - PhysicalShapeEngineLayer? pushPhysicalShape({ + PhysicalShapeEngineLayer pushPhysicalShape({ required Path path, required double elevation, required Color color, @@ -118,13 +119,6 @@ abstract class SceneBuilder { double width = 0.0, double height = 0.0, }); - void addChildScene({ - Offset offset = Offset.zero, - double width = 0.0, - double height = 0.0, - required SceneHost sceneHost, - bool hitTestable = true, - }); void setRasterizerTracingThreshold(int frameInterval); void setCheckerboardRasterCacheImages(bool checkerboard); void setCheckerboardOffscreenLayers(bool checkerboard); @@ -140,25 +134,6 @@ abstract class SceneBuilder { ); } -class EngineLayer {} - -class SceneHost { - SceneHost( - dynamic viewHolderToken, - void Function() viewConnectedCallback, - void Function() viewDisconnectedCallback, - void Function(bool) viewStateChangedCallback, - ); +class EngineLayer { void dispose() {} - void setProperties( - double width, - double height, - double insetTop, - double insetRight, - double insetBottom, - double insetLeft, - bool focusable, - ) { - throw UnimplementedError(); - } } diff --git a/lib/web_ui/lib/src/ui/geometry.dart b/lib/web_ui/lib/src/ui/geometry.dart index 0ec3c06550a20..06e4655b339a3 100644 --- a/lib/web_ui/lib/src/ui/geometry.dart +++ b/lib/web_ui/lib/src/ui/geometry.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// See https://github.com/flutter/engine/blob/master/lib/ui/geometry.dart for +// documentation of APIs. +// ignore_for_file: public_member_api_docs part of ui; abstract class OffsetBase { @@ -547,7 +549,7 @@ class RRect { assert(brRadiusY != null), // ignore: unnecessary_null_comparison assert(blRadiusX != null), // ignore: unnecessary_null_comparison assert(blRadiusY != null), // ignore: unnecessary_null_comparison - this.webOnlyUniformRadii = uniformRadii; + webOnlyUniformRadii = uniformRadii; final double left; final double top; @@ -701,10 +703,12 @@ class RRect { RRect scaleRadii() { double scale = 1.0; - scale = _getMin(scale, blRadiusY, tlRadiusY, height); - scale = _getMin(scale, tlRadiusX, trRadiusX, width); - scale = _getMin(scale, trRadiusY, brRadiusY, height); - scale = _getMin(scale, brRadiusX, blRadiusX, width); + final double absWidth = width.abs(); + final double absHeight = height.abs(); + scale = _getMin(scale, blRadiusY, tlRadiusY, absHeight); + scale = _getMin(scale, tlRadiusX, trRadiusX, absWidth); + scale = _getMin(scale, trRadiusY, brRadiusY, absHeight); + scale = _getMin(scale, brRadiusX, blRadiusX, absWidth); if (scale < 1.0) { return RRect._raw( diff --git a/lib/web_ui/lib/src/ui/hash_codes.dart b/lib/web_ui/lib/src/ui/hash_codes.dart index 58efa17c69588..b94b22a33ab87 100644 --- a/lib/web_ui/lib/src/ui/hash_codes.dart +++ b/lib/web_ui/lib/src/ui/hash_codes.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; class _HashEnd { @@ -14,7 +13,11 @@ const _HashEnd _hashEnd = _HashEnd(); // Jenkins hash function, optimized for small integers. // // Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart. -class _Jenkins { +abstract class _Jenkins { + // This class is not meant to be instantiated or extended; this constructor + // prevents instantiation and extension. + _Jenkins._(); + static int combine(int hash, Object? o) { assert(o is! Iterable); hash = 0x1fffffff & (hash + o.hashCode); @@ -117,7 +120,7 @@ int hashValues( int hashList(Iterable? arguments) { int result = 0; if (arguments != null) { - for (Object? argument in arguments) + for (final Object? argument in arguments) result = _Jenkins.combine(result, argument); } return _Jenkins.finish(result); diff --git a/lib/web_ui/lib/src/ui/initialization.dart b/lib/web_ui/lib/src/ui/initialization.dart index a7b06b3586def..13662b627b2a5 100644 --- a/lib/web_ui/lib/src/ui/initialization.dart +++ b/lib/web_ui/lib/src/ui/initialization.dart @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; Future webOnlyInitializePlatform({ engine.AssetManager? assetManager, }) { - final Future initializationFuture = _initializePlatform(assetManager: assetManager); + final Future initializationFuture = + _initializePlatform(assetManager: assetManager); scheduleMicrotask(() { // Access [engine.lineLookup] to force the lazy unpacking of line break data // now. Removing this line won't break anything. It's just an optimization @@ -21,21 +21,17 @@ Future webOnlyInitializePlatform({ Future _initializePlatform({ engine.AssetManager? assetManager, }) async { - if (!debugEmulateFlutterTesterEnvironment) { - engine.window.locationStrategy = const engine.HashLocationStrategy(); - } - engine.initializeEngine(); // This needs to be after `webOnlyInitializeEngine` because that is where the // canvaskit script is added to the page. - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { await engine.initializeCanvasKit(); } assetManager ??= const engine.AssetManager(); await webOnlySetAssetManager(assetManager); - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { await engine.skiaFontCollection.ensureFontsLoaded(); } else { await _fontCollection!.ensureFontsLoaded(); @@ -51,14 +47,16 @@ engine.FontCollection? _fontCollection; bool _webOnlyIsInitialized = false; bool get webOnlyIsInitialized => _webOnlyIsInitialized; Future webOnlySetAssetManager(engine.AssetManager assetManager) async { - assert(assetManager != null, 'Cannot set assetManager to null'); // ignore: unnecessary_null_comparison + // ignore: unnecessary_null_comparison + assert(assetManager != null, + 'Cannot set assetManager to null'); if (assetManager == _assetManager) { return; } _assetManager = assetManager; - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { engine.ensureSkiaFontCollectionInitialized(); } else { _fontCollection ??= engine.FontCollection(); @@ -66,25 +64,31 @@ Future webOnlySetAssetManager(engine.AssetManager assetManager) async { } if (_assetManager != null) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { await engine.skiaFontCollection.registerFonts(_assetManager!); } else { await _fontCollection!.registerFonts(_assetManager!); } } - if (debugEmulateFlutterTesterEnvironment && !engine.experimentalUseSkia) { - _fontCollection!.debugRegisterTestFonts(); + if (debugEmulateFlutterTesterEnvironment) { + if (engine.useCanvasKit) { + engine.skiaFontCollection.debugRegisterTestFonts(); + } else { + _fontCollection!.debugRegisterTestFonts(); + } } } -bool get debugEmulateFlutterTesterEnvironment => _debugEmulateFlutterTesterEnvironment; +bool get debugEmulateFlutterTesterEnvironment => + _debugEmulateFlutterTesterEnvironment; set debugEmulateFlutterTesterEnvironment(bool value) { _debugEmulateFlutterTesterEnvironment = value; if (_debugEmulateFlutterTesterEnvironment) { const Size logicalSize = Size(800.0, 600.0); - engine.window.webOnlyDebugPhysicalSizeOverride = logicalSize * window.devicePixelRatio; + engine.window.webOnlyDebugPhysicalSizeOverride = + logicalSize * window.devicePixelRatio; } } diff --git a/lib/web_ui/lib/src/ui/key.dart b/lib/web_ui/lib/src/ui/key.dart new file mode 100644 index 0000000000000..b1665526298bd --- /dev/null +++ b/lib/web_ui/lib/src/ui/key.dart @@ -0,0 +1,160 @@ +// 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. + + +part of ui; + +/// The type of a key event. +// Must match the KeyEventType enum in ui/window/key_data.h. +enum KeyEventType { + /// The key is pressed. + down, + + /// The key is released. + up, + + /// The key is held, causing a repeated key input. + repeat, +} + +/// Information about a key event. +class KeyData { + /// Creates an object that represents a key event. + const KeyData({ + required this.timeStamp, + required this.type, + required this.physical, + required this.logical, + required this.character, + required this.synthesized, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + /// + /// For [KeyEventType.synchronize] and [KeyEventType.cancel] events, the [timeStamp] + /// might not be the actual time that the key press or release happens. + final Duration timeStamp; + + /// The type of the event. + final KeyEventType type; + + /// The key code for the physical key that has changed. + final int physical; + + /// The key code for the logical key that has changed. + final int logical; + + /// Character input from the event. + /// + /// Ignored for up events. + final String? character; + + /// If [synthesized] is true, this event does not correspond to a native event. + /// + /// Although most of Flutter's keyboard events are transformed from native + /// events, some events are not based on native events, and are synthesized + /// only to conform Flutter's key event model (as documented in + /// the `HardwareKeyboard` class in the framework). + /// + /// For example, some key downs or ups might be lost when the window loses + /// focus. Some platforms provides ways to query whether a key is being held. + /// If Flutter detects an inconsistency between the state Flutter records and + /// the state returned by the system, Flutter will synthesize a corresponding + /// event to synchronize the state without breaking the event model. + /// + /// As another example, macOS treats CapsLock in a special way by sending + /// down and up events at the down of alterate presses to indicate the + /// direction in which the lock is toggled instead of that the physical key is + /// going. Flutter normalizes the behavior by converting a native down event + /// into a down event followed immediately by a synthesized up event, and + /// the native up event also into a down event followed immediately by a + /// synthesized up event. + /// + /// Synthesized events do not have a trustworthy [timeStamp], and should not be + /// processed as if the key actually went down or up at the time of the + /// callback. + /// + /// [KeyRepeatEvent] is never synthesized. + final bool synthesized; + + String _logicalToString() { + final String result = '0x${logical.toRadixString(16)}'; + // Find the bits that are not included in `valueMask`, shifted to the right. + // For example, if [logical] is 0x12abcdabcd, then the result is 0x12. + // + // This is mostly equivalent to a right shift, resolving the problem that + // JavaScript only support 32-bit bitwise operations and needs to use + // division instead. + final int planeNum = (logical / 0x100000000).floor(); + final String planeDescription = (() { + switch (planeNum) { + case 0x000: + return ' (Unicode)'; + case 0x001: + return ' (Unprintable)'; + case 0x002: + return ' (Flutter)'; + case 0x017: + return ' (Web)'; + } + return ''; + })(); + return '$result$planeDescription'; + } + + String? _escapeCharacter() { + if (character == null) { + return character ?? ''; + } + switch (character!) { + case '\n': + return r'"\n"'; + case '\t': + return r'"\t"'; + case '\r': + return r'"\r"'; + case '\b': + return r'"\b"'; + case '\f': + return r'"\f"'; + default: + return '"$character"'; + } + } + + String? _quotedCharCode() { + if (character == null) + return ''; + final Iterable hexChars = character!.codeUnits + .map((int code) => code.toRadixString(16).padLeft(2, '0')); + return ' (0x${hexChars.join(' ')})'; + } + + @override + String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, ' + 'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${_quotedCharCode()}${synthesized ? ', synthesized' : ''})'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + 'type: ${_typeToString(type)}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: $character, ' + 'synthesized: $synthesized' + ')'; + } + + static String _typeToString(KeyEventType type) { + switch (type) { + case KeyEventType.up: + return 'up'; + case KeyEventType.down: + return 'down'; + case KeyEventType.repeat: + return 'repeat'; + } + } +} diff --git a/lib/web_ui/lib/src/ui/lerp.dart b/lib/web_ui/lib/src/ui/lerp.dart index 364209411fa6e..ca7ee00f7f595 100644 --- a/lib/web_ui/lib/src/ui/lerp.dart +++ b/lib/web_ui/lib/src/ui/lerp.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; /// Linearly interpolate between two numbers, `a` and `b`, by an extrapolation diff --git a/lib/web_ui/lib/src/ui/natives.dart b/lib/web_ui/lib/src/ui/natives.dart index dbdb7a9144214..8c94e6a69ec74 100644 --- a/lib/web_ui/lib/src/ui/natives.dart +++ b/lib/web_ui/lib/src/ui/natives.dart @@ -2,19 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; -/// Prints a warning to the browser's debug console. -void _debugPrintWarning(String warning) { - if (engine.assertionsEnabled) { - // Use a lower log level message to reduce noise in release mode. - html.window.console.debug(warning); - return; - } - html.window.console.warn(warning); -} - List saveCompilationTrace() { if (engine.assertionsEnabled) { throw UnimplementedError('saveCompilationTrace is not implemented on the web.'); diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index a76cddb602714..cf6c5928b97ad 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 +// For documentation see https://github.com/flutter/engine/blob/master/lib/ui/painting.dart +// ignore_for_file: public_member_api_docs part of ui; // ignore: unused_element, Used in Shader assert. @@ -34,7 +35,7 @@ Color _scaleAlpha(Color a, double factor) { } class Color { - const Color(int value) : this.value = value & 0xFFFFFFFF; + const Color(int value) : this.value = value & 0xFFFFFFFF;// ignore: unnecessary_this const Color.fromARGB(int a, int r, int g, int b) : value = (((a & 0xff) << 24) | ((r & 0xff) << 16) | @@ -230,7 +231,7 @@ enum Clip { } abstract class Paint { - factory Paint() => engine.experimentalUseSkia ? engine.CkPaint() : engine.SurfacePaint(); + factory Paint() => engine.useCanvasKit ? engine.CkPaint() : engine.SurfacePaint(); static bool enableDithering = false; BlendMode get blendMode; set blendMode(BlendMode value); @@ -278,9 +279,14 @@ abstract class Gradient extends Shader { List? colorStops, TileMode tileMode = TileMode.clamp, Float64List? matrix4, - ]) => engine.experimentalUseSkia - ? engine.CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4) - : engine.GradientLinear(from, to, colors, colorStops, tileMode, matrix4); + ]) { + final Float32List? matrix = matrix4 == null ? null : engine.toMatrix32(matrix4); + return engine.useCanvasKit + ? engine.CkGradientLinear( + from, to, colors, colorStops, tileMode, matrix) + : engine.GradientLinear(from, to, colors, colorStops, tileMode, matrix); + } + factory Gradient.radial( Offset center, double radius, @@ -296,13 +302,13 @@ abstract class Gradient extends Shader { // If focal == center and the focal radius is 0.0, it's still a regular radial gradient final Float32List? matrix32 = matrix4 != null ? engine.toMatrix32(matrix4) : null; if (focal == null || (focal == center && focalRadius == 0.0)) { - return engine.experimentalUseSkia + return engine.useCanvasKit ? engine.CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix32) : engine.GradientRadial(center, radius, colors, colorStops, tileMode, matrix32); } else { assert(center != Offset.zero || focal != Offset.zero); // will result in exception(s) in Skia side - return engine.experimentalUseSkia + return engine.useCanvasKit ? engine.CkGradientConical( focal, focalRadius, center, radius, colors, colorStops, tileMode, matrix32) : engine.GradientConical( @@ -317,7 +323,7 @@ abstract class Gradient extends Shader { double startAngle = 0.0, double endAngle = math.pi * 2, Float64List? matrix4, - ]) => engine.experimentalUseSkia + ]) => engine.useCanvasKit ? engine.CkGradientSweep(center, colors, colorStops, tileMode, startAngle, endAngle, matrix4 != null ? engine.toMatrix32(matrix4) : null) : engine.GradientSweep(center, colors, colorStops, tileMode, startAngle, @@ -329,6 +335,7 @@ abstract class Image { int get height; Future toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}); void dispose(); + bool get debugDisposed; Image clone() => this; @@ -383,8 +390,6 @@ class MaskFilter { } enum FilterQuality { - // This list comes from Skia's SkFilterQuality.h and the values (order) should - // be kept in sync. none, low, medium, @@ -392,18 +397,29 @@ enum FilterQuality { } class ImageFilter { - factory ImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0}) { - if (engine.experimentalUseSkia) { - return engine.CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY); + factory ImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0, TileMode tileMode = TileMode.clamp}) { + if (engine.useCanvasKit) { + return engine.CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + } + // TODO(ferhat): implement TileMode. + return engine.EngineImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + } + + factory ImageFilter.matrix(Float64List matrix4, {FilterQuality filterQuality = FilterQuality.low}) { + if (matrix4.length != 16) + throw ArgumentError('"matrix4" must have 16 entries.'); + if (engine.useCanvasKit) { + return engine.CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); } - return engine.EngineImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY); + // TODO(yjbanov): implement FilterQuality. + return engine.EngineImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); } - ImageFilter.matrix(Float64List matrix4, {FilterQuality filterQuality = FilterQuality.low}) { - // TODO(flutter_web): add implementation. - throw UnimplementedError('ImageFilter.matrix not implemented for web platform.'); - // if (matrix4.length != 16) - // throw ArgumentError('"matrix4" must have 16 entries.'); + // TODO(ferhat): add implementation and remove the "ignore". + // ignore: avoid_unused_constructor_parameters + ImageFilter.compose({required ImageFilter outer, required ImageFilter inner}) { + throw UnimplementedError( + 'ImageFilter.compose not implemented for web platform.'); } } @@ -444,26 +460,25 @@ Future instantiateImageCodec( int? targetWidth, int? targetHeight, bool allowUpscaling = true, -}) { - return _futurize((engine.Callback callback) => - // TODO: Implement targetWidth and targetHeight support. - _instantiateImageCodec(list, callback)); -} - -String? _instantiateImageCodec(Uint8List list, engine.Callback callback) { - if (engine.experimentalUseSkia) { - engine.skiaInstantiateImageCodec(list, callback); - return null; +}) async { + if (engine.useCanvasKit) { + // TODO(hterkelsen): Implement targetWidth and targetHeight support. + return engine.skiaInstantiateImageCodec(list); + } else { + final html.Blob blob = html.Blob([list.buffer]); + return engine.HtmlBlobCodec(blob); } - final html.Blob blob = html.Blob([list.buffer]); - callback(engine.HtmlBlobCodec(blob)); - return null; } -Future webOnlyInstantiateImageCodecFromUrl(Uri uri, - {engine.WebOnlyImageCodecChunkCallback? chunkCallback}) { - return _futurize((engine.Callback callback) => +Future webOnlyInstantiateImageCodecFromUrl(Uri uri, + {engine.WebOnlyImageCodecChunkCallback? chunkCallback}) { + if (engine.useCanvasKit) { + return engine.skiaInstantiateWebImageCodec( + uri.toString(), chunkCallback); + } else { + return _futurize((engine.Callback callback) => _instantiateImageCodecFromUrl(uri, chunkCallback, callback)); + } } String? _instantiateImageCodecFromUrl( @@ -471,13 +486,8 @@ String? _instantiateImageCodecFromUrl( engine.WebOnlyImageCodecChunkCallback? chunkCallback, engine.Callback callback, ) { - if (engine.experimentalUseSkia) { - engine.skiaInstantiateWebImageCodec(uri.toString(), callback, chunkCallback); - return null; - } else { - callback(engine.HtmlCodec(uri.toString(), chunkCallback: chunkCallback)); - return null; - } + callback(engine.HtmlCodec(uri.toString(), chunkCallback: chunkCallback)); + return null; } void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) { @@ -557,12 +567,9 @@ Future _createBmp( } } - final Completer codecCompleter = Completer(); - _instantiateImageCodec( + return instantiateImageCodec( bmpData.buffer.asUint8List(), - (Codec codec) => codecCompleter.complete(codec), ); - return codecCompleter.future; } void decodeImageFromPixels( @@ -576,28 +583,26 @@ void decodeImageFromPixels( int? targetHeight, bool allowUpscaling = true, }) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { engine.skiaInstantiateImageCodec( pixels, - (Codec codec) { - codec.getNextFrame().then((FrameInfo info) { - callback(info.image); - }); - }, width, height, format.index, rowBytes, - ); + ).getNextFrame().then((FrameInfo info) { + callback(info.image); + }); return; } - void Function(Codec) callbacker = (Codec codec) { + void executeCallback(Codec codec) { codec.getNextFrame().then((FrameInfo frameInfo) { callback(frameInfo.image); }); - }; - _createBmp(pixels, width, height, rowBytes ?? width, format).then(callbacker); + } + _createBmp(pixels, width, height, rowBytes ?? width, format).then( + executeCallback); } @@ -617,7 +622,7 @@ class Shadow { // See SkBlurMask::ConvertRadiusToSigma(). // static double convertRadiusToSigma(double radius) { - return radius * 0.57735 + 0.5; + return radius > 0 ? radius * 0.57735 + 0.5 : 0; } double get blurSigma => convertRadiusToSigma(blurRadius); @@ -694,12 +699,11 @@ class Shadow { } class ImageShader extends Shader { - factory ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) { - if (engine.experimentalUseSkia) { - return engine.CkImageShader(image, tmx, tmy, matrix4); - } - throw UnsupportedError('ImageShader not implemented for web platform.'); - } + factory ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4, { + FilterQuality? filterQuality, + }) => engine.useCanvasKit + ? engine.CkImageShader(image, tmx, tmy, matrix4, filterQuality) + : engine.EngineImageShader(image, tmx, tmy, matrix4, filterQuality); } class ImmutableBuffer { @@ -712,6 +716,15 @@ class ImmutableBuffer { Uint8List? _list; final int length; + + bool get debugDisposed { + late bool disposed; + assert(() { + disposed = _list == null; + return true; + }()); + return disposed; + } void dispose() => _list = null; } @@ -761,7 +774,7 @@ class ImageDescriptor { throw StateError('Object is disposed'); } if (_width == null) { - return await instantiateImageCodec( + return instantiateImageCodec( _data!, targetWidth: targetWidth, targetHeight: targetHeight, @@ -769,6 +782,6 @@ class ImageDescriptor { ); } - return await _createBmp(_data!, width, height, _rowBytes ?? width, _format!); + return _createBmp(_data!, width, height, _rowBytes ?? width, _format!); } } diff --git a/lib/web_ui/lib/src/ui/path.dart b/lib/web_ui/lib/src/ui/path.dart index 02d4e40d73484..0d9b7cc6a9fb9 100644 --- a/lib/web_ui/lib/src/ui/path.dart +++ b/lib/web_ui/lib/src/ui/path.dart @@ -2,19 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; +// For documentation see https://github.com/flutter/engine/blob/master/lib/ui/painting.dart +// ignore_for_file: public_member_api_docs + abstract class Path { factory Path() { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkPath(); } else { return engine.SurfacePath(); } } factory Path.from(Path source) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkPath.from(source as engine.CkPath); } else { return engine.SurfacePath.from(source as engine.SurfacePath); @@ -64,7 +66,7 @@ abstract class Path { static Path combine(PathOperation operation, Path path1, Path path2) { assert(path1 != null); // ignore: unnecessary_null_comparison assert(path2 != null); // ignore: unnecessary_null_comparison - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkPath.combine(operation, path1, path2); } throw UnimplementedError(); diff --git a/lib/web_ui/lib/src/ui/path_metrics.dart b/lib/web_ui/lib/src/ui/path_metrics.dart index 65f07f881f1a2..d4324f1d4a476 100644 --- a/lib/web_ui/lib/src/ui/path_metrics.dart +++ b/lib/web_ui/lib/src/ui/path_metrics.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; abstract class PathMetrics extends collection.IterableBase { @@ -22,7 +21,7 @@ abstract class PathMetric { double get length; int get contourIndex; Tangent? getTangentForOffset(double distance); - Path? extractPath(double start, double end, {bool startWithMoveTo = true}); + Path extractPath(double start, double end, {bool startWithMoveTo = true}); bool get isClosed; } diff --git a/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/lib/web_ui/lib/src/ui/platform_dispatcher.dart new file mode 100644 index 0000000000000..8408c11865d96 --- /dev/null +++ b/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -0,0 +1,439 @@ +// 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. + +part of ui; + +typedef VoidCallback = void Function(); +typedef FrameCallback = void Function(Duration duration); +typedef TimingsCallback = void Function(List timings); +typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); +typedef KeyDataCallback = bool Function(KeyData packet); +typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); +typedef PlatformMessageResponseCallback = void Function(ByteData? data); +typedef PlatformMessageCallback = void Function( + String name, ByteData? data, PlatformMessageResponseCallback? callback); +typedef PlatformConfigurationChangedCallback = void Function(PlatformConfiguration configuration); + +abstract class PlatformDispatcher { + static PlatformDispatcher get instance => engine.EnginePlatformDispatcher.instance; + + PlatformConfiguration get configuration; + VoidCallback? get onPlatformConfigurationChanged; + set onPlatformConfigurationChanged(VoidCallback? callback); + + Iterable get views; + + VoidCallback? get onMetricsChanged; + set onMetricsChanged(VoidCallback? callback); + + FrameCallback? get onBeginFrame; + set onBeginFrame(FrameCallback? callback); + + VoidCallback? get onDrawFrame; + set onDrawFrame(VoidCallback? callback); + + PointerDataPacketCallback? get onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback? callback); + + KeyDataCallback? get onKeyData; + set onKeyData(KeyDataCallback? callback); + + TimingsCallback? get onReportTimings; + set onReportTimings(TimingsCallback? callback); + + void sendPlatformMessage( + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, + ); + + PlatformMessageCallback? get onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback? callback); + + void setIsolateDebugName(String name) {} + + ByteData? getPersistentIsolateData() => null; + + void scheduleFrame(); + + void render(Scene scene, [FlutterView view]); + + AccessibilityFeatures get accessibilityFeatures; + + VoidCallback? get onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback? callback); + + void updateSemantics(SemanticsUpdate update); + + Locale get locale; + + List get locales => configuration.locales; + + Locale? computePlatformResolvedLocale(List supportedLocales); + + VoidCallback? get onLocaleChanged; + set onLocaleChanged(VoidCallback? callback); + + String get initialLifecycleState => 'AppLifecycleState.resumed'; + + bool get alwaysUse24HourFormat => configuration.alwaysUse24HourFormat; + + double get textScaleFactor => configuration.textScaleFactor; + + VoidCallback? get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback? callback); + + Brightness get platformBrightness => configuration.platformBrightness; + + VoidCallback? get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback? callback); + + bool get semanticsEnabled => configuration.semanticsEnabled; + + VoidCallback? get onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback? callback); + + SemanticsActionCallback? get onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback? callback); + + String get defaultRouteName; + + FrameData get frameData; + + VoidCallback? get onFrameDataChanged => null; + set onFrameDataChanged(VoidCallback? callback) {} +} + +class PlatformConfiguration { + const PlatformConfiguration({ + this.accessibilityFeatures = const AccessibilityFeatures._(0), + this.alwaysUse24HourFormat = false, + this.semanticsEnabled = false, + this.platformBrightness = Brightness.light, + this.textScaleFactor = 1.0, + this.locales = const [], + this.defaultRouteName = '/', + }); + + PlatformConfiguration copyWith({ + AccessibilityFeatures? accessibilityFeatures, + bool? alwaysUse24HourFormat, + bool? semanticsEnabled, + Brightness? platformBrightness, + double? textScaleFactor, + List? locales, + String? defaultRouteName, + }) { + return PlatformConfiguration( + accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, + alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, + semanticsEnabled: semanticsEnabled ?? this.semanticsEnabled, + platformBrightness: platformBrightness ?? this.platformBrightness, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + locales: locales ?? this.locales, + defaultRouteName: defaultRouteName ?? this.defaultRouteName, + ); + } + + final AccessibilityFeatures accessibilityFeatures; + final bool alwaysUse24HourFormat; + final bool semanticsEnabled; + final Brightness platformBrightness; + final double textScaleFactor; + final List locales; + final String defaultRouteName; +} + +class ViewConfiguration { + const ViewConfiguration({ + this.window, + this.devicePixelRatio = 1.0, + this.geometry = Rect.zero, + this.visible = false, + this.viewInsets = WindowPadding.zero, + this.viewPadding = WindowPadding.zero, + this.systemGestureInsets = WindowPadding.zero, + this.padding = WindowPadding.zero, + this.gestureSettings = const GestureSettings(), + }); + + ViewConfiguration copyWith({ + FlutterWindow? window, + double? devicePixelRatio, + Rect? geometry, + bool? visible, + WindowPadding? viewInsets, + WindowPadding? viewPadding, + WindowPadding? systemGestureInsets, + WindowPadding? padding, + GestureSettings? gestureSettings, + }) { + return ViewConfiguration( + window: window ?? this.window, + devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio, + geometry: geometry ?? this.geometry, + visible: visible ?? this.visible, + viewInsets: viewInsets ?? this.viewInsets, + viewPadding: viewPadding ?? this.viewPadding, + systemGestureInsets: systemGestureInsets ?? this.systemGestureInsets, + padding: padding ?? this.padding, + gestureSettings: gestureSettings ?? this.gestureSettings, + ); + } + + final FlutterWindow? window; + final double devicePixelRatio; + final Rect geometry; + final bool visible; + final WindowPadding viewInsets; + final WindowPadding viewPadding; + final WindowPadding systemGestureInsets; + final WindowPadding padding; + final GestureSettings gestureSettings; + + @override + String toString() { + return '$runtimeType[window: $window, geometry: $geometry]'; + } +} + +enum FramePhase { + vsyncStart, + buildStart, + buildFinish, + rasterStart, + rasterFinish, + rasterFinishWallTime, +} + +class FrameTiming { + factory FrameTiming({ + required int vsyncStart, + required int buildStart, + required int buildFinish, + required int rasterStart, + required int rasterFinish, + required int rasterFinishWallTime, + int frameNumber = 1, + }) { + return FrameTiming._([ + vsyncStart, + buildStart, + buildFinish, + rasterStart, + rasterFinish, + rasterFinishWallTime, + frameNumber, + ]); + } + + FrameTiming._(this._timestamps) + : assert(_timestamps.length == FramePhase.values.length + 1); + + int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; + + Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); + + Duration get buildDuration => + _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); + + Duration get rasterDuration => + _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); + + Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); + + Duration get totalSpan => + _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); + + int get frameNumber => _timestamps.last; + + final List _timestamps; // in microseconds + + String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms'; + + @override + String toString() { + return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, vsyncOverhead: ${_formatMS(vsyncOverhead)}, totalSpan: ${_formatMS(totalSpan)}, frameNumber: $frameNumber)'; + } +} + +enum AppLifecycleState { + resumed, + inactive, + paused, + detached, +} + +abstract class WindowPadding { + const factory WindowPadding._( + {required double left, + required double top, + required double right, + required double bottom}) = engine.WindowPadding; + + double get left; + double get top; + double get right; + double get bottom; + + static const WindowPadding zero = WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); + + @override + String toString() { + return 'WindowPadding(left: $left, top: $top, right: $right, bottom: $bottom)'; + } +} + +class Locale { + const Locale( + this._languageCode, [ + this._countryCode, + ]) : assert(_languageCode != null), // ignore: unnecessary_null_comparison + assert(_languageCode != ''), + scriptCode = null; + + const Locale.fromSubtags({ + String languageCode = 'und', + this.scriptCode, + String? countryCode, + }) : assert(languageCode != null), // ignore: unnecessary_null_comparison + assert(languageCode != ''), + _languageCode = languageCode, + assert(scriptCode != ''), + assert(countryCode != ''), + _countryCode = countryCode; + + String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; + final String _languageCode; + + // This map is generated by //flutter/tools/gen_locale.dart + // Mappings generated for language subtag registry as of 2019-02-27. + static const Map _deprecatedLanguageSubtagMap = { + 'in': 'id', // Indonesian; deprecated 1989-01-01 + 'iw': 'he', // Hebrew; deprecated 1989-01-01 + 'ji': 'yi', // Yiddish; deprecated 1989-01-01 + 'jw': 'jv', // Javanese; deprecated 2001-08-13 + 'mo': 'ro', // Moldavian, Moldovan; deprecated 2008-11-22 + 'aam': 'aas', // Aramanik; deprecated 2015-02-12 + 'adp': 'dz', // Adap; deprecated 2015-02-12 + 'aue': 'ktz', // ǂKxʼauǁʼein; deprecated 2015-02-12 + 'ayx': 'nun', // Ayi (China); deprecated 2011-08-16 + 'bgm': 'bcg', // Baga Mboteni; deprecated 2016-05-30 + 'bjd': 'drl', // Bandjigali; deprecated 2012-08-12 + 'ccq': 'rki', // Chaungtha; deprecated 2012-08-12 + 'cjr': 'mom', // Chorotega; deprecated 2010-03-11 + 'cka': 'cmr', // Khumi Awa Chin; deprecated 2012-08-12 + 'cmk': 'xch', // Chimakum; deprecated 2010-03-11 + 'coy': 'pij', // Coyaima; deprecated 2016-05-30 + 'cqu': 'quh', // Chilean Quechua; deprecated 2016-05-30 + 'drh': 'khk', // Darkhat; deprecated 2010-03-11 + 'drw': 'prs', // Darwazi; deprecated 2010-03-11 + 'gav': 'dev', // Gabutamon; deprecated 2010-03-11 + 'gfx': 'vaj', // Mangetti Dune ǃXung; deprecated 2015-02-12 + 'ggn': 'gvr', // Eastern Gurung; deprecated 2016-05-30 + 'gti': 'nyc', // Gbati-ri; deprecated 2015-02-12 + 'guv': 'duz', // Gey; deprecated 2016-05-30 + 'hrr': 'jal', // Horuru; deprecated 2012-08-12 + 'ibi': 'opa', // Ibilo; deprecated 2012-08-12 + 'ilw': 'gal', // Talur; deprecated 2013-09-10 + 'jeg': 'oyb', // Jeng; deprecated 2017-02-23 + 'kgc': 'tdf', // Kasseng; deprecated 2016-05-30 + 'kgh': 'kml', // Upper Tanudan Kalinga; deprecated 2012-08-12 + 'koj': 'kwv', // Sara Dunjo; deprecated 2015-02-12 + 'krm': 'bmf', // Krim; deprecated 2017-02-23 + 'ktr': 'dtp', // Kota Marudu Tinagas; deprecated 2016-05-30 + 'kvs': 'gdj', // Kunggara; deprecated 2016-05-30 + 'kwq': 'yam', // Kwak; deprecated 2015-02-12 + 'kxe': 'tvd', // Kakihum; deprecated 2015-02-12 + 'kzj': 'dtp', // Coastal Kadazan; deprecated 2016-05-30 + 'kzt': 'dtp', // Tambunan Dusun; deprecated 2016-05-30 + 'lii': 'raq', // Lingkhim; deprecated 2015-02-12 + 'lmm': 'rmx', // Lamam; deprecated 2014-02-28 + 'meg': 'cir', // Mea; deprecated 2013-09-10 + 'mst': 'mry', // Cataelano Mandaya; deprecated 2010-03-11 + 'mwj': 'vaj', // Maligo; deprecated 2015-02-12 + 'myt': 'mry', // Sangab Mandaya; deprecated 2010-03-11 + 'nad': 'xny', // Nijadali; deprecated 2016-05-30 + 'ncp': 'kdz', // Ndaktup; deprecated 2018-03-08 + 'nnx': 'ngv', // Ngong; deprecated 2015-02-12 + 'nts': 'pij', // Natagaimas; deprecated 2016-05-30 + 'oun': 'vaj', // ǃOǃung; deprecated 2015-02-12 + 'pcr': 'adx', // Panang; deprecated 2013-09-10 + 'pmc': 'huw', // Palumata; deprecated 2016-05-30 + 'pmu': 'phr', // Mirpur Panjabi; deprecated 2015-02-12 + 'ppa': 'bfy', // Pao; deprecated 2016-05-30 + 'ppr': 'lcq', // Piru; deprecated 2013-09-10 + 'pry': 'prt', // Pray 3; deprecated 2016-05-30 + 'puz': 'pub', // Purum Naga; deprecated 2014-02-28 + 'sca': 'hle', // Sansu; deprecated 2012-08-12 + 'skk': 'oyb', // Sok; deprecated 2017-02-23 + 'tdu': 'dtp', // Tempasuk Dusun; deprecated 2016-05-30 + 'thc': 'tpo', // Tai Hang Tong; deprecated 2016-05-30 + 'thx': 'oyb', // The; deprecated 2015-02-12 + 'tie': 'ras', // Tingal; deprecated 2011-08-16 + 'tkk': 'twm', // Takpa; deprecated 2011-08-16 + 'tlw': 'weo', // South Wemale; deprecated 2012-08-12 + 'tmp': 'tyj', // Tai Mène; deprecated 2016-05-30 + 'tne': 'kak', // Tinoc Kallahan; deprecated 2016-05-30 + 'tnf': 'prs', // Tangshewi; deprecated 2010-03-11 + 'tsf': 'taj', // Southwestern Tamang; deprecated 2015-02-12 + 'uok': 'ema', // Uokha; deprecated 2015-02-12 + 'xba': 'cax', // Kamba (Brazil); deprecated 2016-05-30 + 'xia': 'acn', // Xiandao; deprecated 2013-09-10 + 'xkh': 'waw', // Karahawyana; deprecated 2016-05-30 + 'xsj': 'suj', // Subi; deprecated 2015-02-12 + 'ybd': 'rki', // Yangbye; deprecated 2012-08-12 + 'yma': 'lrr', // Yamphe; deprecated 2012-08-12 + 'ymt': 'mtm', // Mator-Taygi-Karagas; deprecated 2015-02-12 + 'yos': 'zom', // Yos; deprecated 2013-09-10 + 'yuu': 'yug', // Yugh; deprecated 2014-02-28 + }; + + final String? scriptCode; + + String? get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; + final String? _countryCode; + + // This map is generated by //flutter/tools/gen_locale.dart + // Mappings generated for language subtag registry as of 2019-02-27. + static const Map _deprecatedRegionSubtagMap = { + 'BU': 'MM', // Burma; deprecated 1989-12-05 + 'DD': 'DE', // German Democratic Republic; deprecated 1990-10-30 + 'FX': 'FR', // Metropolitan France; deprecated 1997-07-14 + 'TP': 'TL', // East Timor; deprecated 2002-05-20 + 'YD': 'YE', // Democratic Yemen; deprecated 1990-08-14 + 'ZR': 'CD', // Zaire; deprecated 1997-07-14 + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + return other is Locale + && other.languageCode == languageCode + && other.scriptCode == scriptCode + && other.countryCode == countryCode; + } + + @override + int get hashCode => hashValues(languageCode, scriptCode, countryCode); + + @override + String toString() => _rawToString('_'); + + // TODO(yjbanov): implement to match flutter native. + String toLanguageTag() => _rawToString('-'); + + String _rawToString(String separator) { + final StringBuffer out = StringBuffer(languageCode); + if (scriptCode != null) { + out.write('$separator$scriptCode'); + } + if (_countryCode != null) { + out.write('$separator$countryCode'); + } + return out.toString(); + } +} diff --git a/lib/web_ui/lib/src/ui/pointer.dart b/lib/web_ui/lib/src/ui/pointer.dart index 0ad384b4d1209..230b0402ee44d 100644 --- a/lib/web_ui/lib/src/ui/pointer.dart +++ b/lib/web_ui/lib/src/ui/pointer.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; enum PointerChange { diff --git a/lib/web_ui/lib/src/ui/semantics.dart b/lib/web_ui/lib/src/ui/semantics.dart index 00d9d03a68879..f3a6ca21a7198 100644 --- a/lib/web_ui/lib/src/ui/semantics.dart +++ b/lib/web_ui/lib/src/ui/semantics.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; class SemanticsAction { @@ -29,6 +28,7 @@ class SemanticsAction { static const int _kDismissIndex = 1 << 18; static const int _kMoveCursorForwardByWordIndex = 1 << 19; static const int _kMoveCursorBackwardByWordIndex = 1 << 20; + static const int _kSetText = 1 << 21; final int index; static const SemanticsAction tap = SemanticsAction._(_kTapIndex); static const SemanticsAction longPress = SemanticsAction._(_kLongPressIndex); @@ -57,6 +57,7 @@ class SemanticsAction { SemanticsAction._(_kMoveCursorForwardByWordIndex); static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._(_kMoveCursorBackwardByWordIndex); + static const SemanticsAction setText = SemanticsAction._(_kSetText); static const Map values = { _kTapIndex: tap, _kLongPressIndex: longPress, @@ -79,6 +80,7 @@ class SemanticsAction { _kDismissIndex: dismiss, _kMoveCursorForwardByWordIndex: moveCursorForwardByWord, _kMoveCursorBackwardByWordIndex: moveCursorBackwardByWord, + _kSetText: setText, }; @override @@ -126,6 +128,8 @@ class SemanticsAction { return 'SemanticsAction.moveCursorForwardByWord'; case _kMoveCursorBackwardByWordIndex: return 'SemanticsAction.moveCursorBackwardByWord'; + case _kSetText: + return 'SemanticsAction.setText'; } assert(false, 'Unhandled index: $index'); return ''; @@ -157,6 +161,7 @@ class SemanticsFlag { static const int _kIsFocusableIndex = 1 << 21; static const int _kIsLinkIndex = 1 << 22; static const int _kIsSliderIndex = 1 << 23; + static const int _kIsKeyboardKeyIndex = 1 << 24; const SemanticsFlag._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison final int index; @@ -184,6 +189,7 @@ class SemanticsFlag { static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._(_kHasImplicitScrollingIndex); static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex); static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex); + static const SemanticsFlag isKeyboardKey = SemanticsFlag._(_kIsKeyboardKeyIndex); static const Map values = { _kHasCheckedStateIndex: hasCheckedState, _kIsCheckedIndex: isChecked, @@ -209,6 +215,7 @@ class SemanticsFlag { _kHasImplicitScrollingIndex: hasImplicitScrolling, _kIsMultilineIndex: isMultiline, _kIsReadOnlyIndex: isReadOnly, + _kIsKeyboardKeyIndex: isKeyboardKey, }; @override @@ -260,12 +267,66 @@ class SemanticsFlag { return 'SemanticsFlag.isMultiline'; case _kIsReadOnlyIndex: return 'SemanticsFlag.isReadOnly'; + case _kIsKeyboardKeyIndex: + return 'SemanticsFlag.isKeyboardKey'; } assert(false, 'Unhandled index: $index'); return ''; } } + +// When adding a new StringAttributeType, the classes in these file must be +// updated as well. +// * engine/src/flutter/lib/ui/semantics.dart +// * engine/src/flutter/lib/ui/semantics/string_attribute.h +// * engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java + +abstract class StringAttribute { + StringAttribute._({ + required this.range, + }); + + final TextRange range; + + StringAttribute copy({required TextRange range}); +} + +class SpellOutStringAttribute extends StringAttribute { + SpellOutStringAttribute({ + required TextRange range, + }) : super._(range: range); + + @override + StringAttribute copy({required TextRange range}) { + return SpellOutStringAttribute(range: range); + } + + @override + String toString() { + return 'SpellOutStringAttribute(range: $range)'; + } +} + +class LocaleStringAttribute extends StringAttribute { + LocaleStringAttribute({ + required TextRange range, + required this.locale, + }) : super._(range: range); + + final Locale locale; + + @override + StringAttribute copy({required TextRange range}) { + return LocaleStringAttribute(range: range, locale: locale); + } + + @override + String toString() { + return 'LocaleStringAttribute(range: $range, locale: ${locale.toLanguageTag()})'; + } +} + class SemanticsUpdateBuilder { SemanticsUpdateBuilder(); @@ -288,10 +349,15 @@ class SemanticsUpdateBuilder { required double thickness, required Rect rect, required String label, - required String hint, + List? labelAttributes, required String value, + List? valueAttributes, required String increasedValue, + List? increasedValueAttributes, required String decreasedValue, + List? decreasedValueAttributes, + required String hint, + List? hintAttributes, TextDirection? textDirection, required Float64List transform, required Int32List childrenInTraversalOrder, @@ -315,10 +381,15 @@ class SemanticsUpdateBuilder { scrollExtentMin: scrollExtentMin, rect: rect, label: label, - hint: hint, + labelAttributes: labelAttributes, value: value, + valueAttributes: valueAttributes, increasedValue: increasedValue, + increasedValueAttributes: increasedValueAttributes, decreasedValue: decreasedValue, + decreasedValueAttributes: decreasedValueAttributes, + hint: hint, + hintAttributes: hintAttributes, textDirection: textDirection, transform: engine.toMatrix32(transform), elevation: elevation, diff --git a/lib/web_ui/lib/src/ui/test_embedding.dart b/lib/web_ui/lib/src/ui/test_embedding.dart index 7007ebf4420c2..d8a6c2f8cf2b3 100644 --- a/lib/web_ui/lib/src/ui/test_embedding.dart +++ b/lib/web_ui/lib/src/ui/test_embedding.dart @@ -2,9 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(flutter_web): the Web-only API below need to be cleaned up. +// TODO(yjbanov): the Web-only API below need to be cleaned up. -// @dart = 2.10 part of ui; Future? _testPlatformInitializedFuture; @@ -25,7 +24,7 @@ Future? _platformInitializedFuture; Future webOnlyInitializeTestDomRenderer({double devicePixelRatio = 3.0}) { // Force-initialize DomRenderer so it doesn't overwrite test pixel ratio. - engine.domRenderer; + engine.ensureDomRendererInitialized(); // The following parameters are hard-coded in Flutter's test embedder. Since // we don't have an embedder yet this is the lowest-most layer we can put diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index abf00f154674b..a076c6bcd8fe4 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1,9 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Synced 2019-05-30T14:20:57.833907. -// @dart = 2.10 part of ui; enum FontStyle { @@ -47,9 +45,15 @@ class FontWeight { ]; static FontWeight? lerp(FontWeight? a, FontWeight? b, double t) { assert(t != null); // ignore: unnecessary_null_comparison - if (a == null && b == null) + if (a == null && b == null) { return null; - return values[engine.clampInt(lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)!.round(), 0, 8)]; + } + return values[engine.clampInt( + lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)! + .round(), + 0, + 8, + )]; } @override @@ -71,52 +75,108 @@ class FontWeight { class FontFeature { const FontFeature(this.feature, [this.value = 1]) : assert(feature != null), // ignore: unnecessary_null_comparison - assert(feature.length == 4), + assert(feature.length == 4, + 'Feature tag must be exactly four characters long.'), assert(value != null), // ignore: unnecessary_null_comparison - assert(value >= 0); + assert(value >= 0, 'Feature value must be zero or a positive integer.'); const FontFeature.enable(String feature) : this(feature, 1); const FontFeature.disable(String feature) : this(feature, 0); - const FontFeature.randomize() - : feature = 'rand', + const FontFeature.alternative(this.value) : feature = 'aalt'; + const FontFeature.alternativeFractions() + : feature = 'afrc', value = 1; - factory FontFeature.stylisticSet(int value) { + const FontFeature.contextualAlternates() + : feature = 'calt', + value = 1; + const FontFeature.caseSensitiveForms() + : feature = 'case', + value = 1; + factory FontFeature.characterVariant(int value) { assert(value >= 1); assert(value <= 20); - return FontFeature('ss${value.toString().padLeft(2, "0")}'); + return FontFeature('cv${value.toString().padLeft(2, "0")}'); } - const FontFeature.slashedZero() - : feature = 'zero', + const FontFeature.denominator() + : feature = 'dnom', + value = 1; + const FontFeature.fractions() + : feature = 'frac', + value = 1; + const FontFeature.historicalForms() + : feature = 'hist', + value = 1; + const FontFeature.historicalLigatures() + : feature = 'hlig', + value = 1; + const FontFeature.liningFigures() + : feature = 'lnum', + value = 1; + const FontFeature.localeAware({bool enable = true}) + : feature = 'locl', + value = enable ? 1 : 0; + const FontFeature.notationalForms([this.value = 1]) + : feature = 'nalt', + assert(value >= 0); + const FontFeature.numerators() + : feature = 'numr', value = 1; const FontFeature.oldstyleFigures() : feature = 'onum', value = 1; + const FontFeature.ordinalForms() + : feature = 'ordn', + value = 1; const FontFeature.proportionalFigures() : feature = 'pnum', value = 1; + const FontFeature.randomize() + : feature = 'rand', + value = 1; + const FontFeature.stylisticAlternates() + : feature = 'salt', + value = 1; + const FontFeature.scientificInferiors() + : feature = 'sinf', + value = 1; + factory FontFeature.stylisticSet(int value) { + assert(value >= 1); + assert(value <= 20); + return FontFeature('ss${value.toString().padLeft(2, "0")}'); + } + const FontFeature.subscripts() + : feature = 'subs', + value = 1; + const FontFeature.superscripts() + : feature = 'sups', + value = 1; + const FontFeature.swash([this.value = 1]) + : feature = 'swsh', + assert(value >= 0); const FontFeature.tabularFigures() : feature = 'tnum', value = 1; + const FontFeature.slashedZero() + : feature = 'zero', + value = 1; + final String feature; final int value; @override bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } if (other.runtimeType != runtimeType) { return false; } - return other is FontFeature - && other.feature == feature - && other.value == value; + return other is FontFeature && + other.feature == feature && + other.value == value; } @override int get hashCode => hashValues(feature, value); @override - String toString() => 'FontFeature($feature, $value)'; + String toString() => "FontFeature('$feature', $value)"; } // The order of this enum must match the order of the values in RenderStyleConstants.h's ETextAlign. @@ -138,7 +198,7 @@ class TextDecoration { const TextDecoration._(this._mask); factory TextDecoration.combine(List decorations) { int mask = 0; - for (TextDecoration decoration in decorations) { + for (final TextDecoration decoration in decorations) { mask |= decoration._mask; } return TextDecoration._(mask); @@ -156,8 +216,7 @@ class TextDecoration { @override bool operator ==(Object other) { - return other is TextDecoration - && other._mask == _mask; + return other is TextDecoration && other._mask == _mask; } @override @@ -185,35 +244,32 @@ class TextDecoration { } } -enum TextDecorationStyle { - solid, - double, - dotted, - dashed, - wavy +enum TextDecorationStyle { solid, double, dotted, dashed, wavy } + +enum TextLeadingDistribution { + proportional, + even, } class TextHeightBehavior { const TextHeightBehavior({ this.applyHeightToFirstAscent = true, this.applyHeightToLastDescent = true, + this.leadingDistribution = TextLeadingDistribution.proportional, }); - const TextHeightBehavior.fromEncoded(int encoded) - : applyHeightToFirstAscent = (encoded & 0x1) == 0, - applyHeightToLastDescent = (encoded & 0x2) == 0; final bool applyHeightToFirstAscent; final bool applyHeightToLastDescent; - int encode() { - return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); - } + final TextLeadingDistribution leadingDistribution; @override bool operator ==(Object other) { - if (other.runtimeType != runtimeType) - return false; - return other is TextHeightBehavior - && other.applyHeightToFirstAscent == applyHeightToFirstAscent - && other.applyHeightToLastDescent == applyHeightToLastDescent; + if (other.runtimeType != runtimeType) { + return false; + } + return other is TextHeightBehavior && + other.applyHeightToFirstAscent == applyHeightToFirstAscent && + other.applyHeightToLastDescent == applyHeightToLastDescent && + other.leadingDistribution == leadingDistribution; } @override @@ -227,9 +283,10 @@ class TextHeightBehavior { @override String toString() { return 'TextHeightBehavior(' - 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent' - ')'; + 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' + 'applyHeightToLastDescent: $applyHeightToLastDescent, ' + 'leadingDistribution: $leadingDistribution' + ')'; } } @@ -249,13 +306,14 @@ abstract class TextStyle { double? letterSpacing, double? wordSpacing, double? height, + TextLeadingDistribution? leadingDistribution, Locale? locale, Paint? background, Paint? foreground, List? shadows, List? fontFeatures, }) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkTextStyle( color: color, decoration: decoration, @@ -271,6 +329,7 @@ abstract class TextStyle { letterSpacing: letterSpacing, wordSpacing: wordSpacing, height: height, + leadingDistribution: leadingDistribution, locale: locale, background: background as engine.CkPaint?, foreground: foreground as engine.CkPaint?, @@ -319,7 +378,7 @@ abstract class ParagraphStyle { String? ellipsis, Locale? locale, }) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -354,16 +413,78 @@ abstract class ParagraphStyle { } abstract class StrutStyle { + /// Creates a new StrutStyle object. + /// + /// * `fontFamily`: The name of the font to use when painting the text (e.g., + /// Roboto). + /// + /// * `fontFamilyFallback`: An ordered list of font family names that will be searched for when + /// the font in `fontFamily` cannot be found. + /// + /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting + /// the text. + /// + /// * `lineHeight`: The minimum height of the line boxes, as a multiple of the + /// font size. The lines of the paragraph will be at least + /// `(lineHeight + leading) * fontSize` tall when fontSize + /// is not null. When fontSize is null, there is no minimum line height. Tall + /// glyphs due to baseline alignment or large [TextStyle.fontSize] may cause + /// the actual line height after layout to be taller than specified here. + /// [fontSize] must be provided for this property to take effect. + /// + /// * `leading`: The minimum amount of leading between lines as a multiple of + /// the font size. [fontSize] must be provided for this property to take effect. + /// + /// * `fontWeight`: The typeface thickness to use when painting the text + /// (e.g., bold). + /// + /// * `fontStyle`: The typeface variant to use when drawing the letters (e.g., + /// italics). + /// + /// * `forceStrutHeight`: When true, the paragraph will force all lines to be exactly + /// `(lineHeight + leading) * fontSize` tall from baseline to baseline. + /// [TextStyle] is no longer able to influence the line height, and any tall + /// glyphs may overlap with lines above. If a [fontFamily] is specified, the + /// total ascent of the first line will be the min of the `Ascent + half-leading` + /// of the [fontFamily] and `(lineHeight + leading) * fontSize`. Otherwise, it + /// will be determined by the Ascent + half-leading of the first text. factory StrutStyle({ String? fontFamily, List? fontFamilyFallback, double? fontSize, double? height, + TextLeadingDistribution? leadingDistribution, double? leading, FontWeight? fontWeight, FontStyle? fontStyle, bool? forceStrutHeight, - }) = engine.EngineStrutStyle; + }) { + if (engine.useCanvasKit) { + return engine.CkStrutStyle( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + height: height, + leadingDistribution: leadingDistribution, + leading: leading, + fontWeight: fontWeight, + fontStyle: fontStyle, + forceStrutHeight: forceStrutHeight, + ); + } else { + return engine.EngineStrutStyle( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + height: height, + leadingDistribution: leadingDistribution, + leading: leading, + fontWeight: fontWeight, + fontStyle: fontStyle, + forceStrutHeight: forceStrutHeight, + ); + } + } } // The order of this enum must match the order of the values in TextDirection.h's TextDirection. @@ -402,12 +523,12 @@ class TextBox { if (other.runtimeType != runtimeType) { return false; } - return other is TextBox - && other.left == left - && other.top == top - && other.right == right - && other.bottom == bottom - && other.direction == direction; + return other is TextBox && + other.left == left && + other.top == top && + other.right == right && + other.bottom == bottom && + other.direction == direction; } @override @@ -438,9 +559,9 @@ class TextPosition { if (other.runtimeType != runtimeType) { return false; } - return other is TextPosition - && other.offset == offset - && other.affinity == affinity; + return other is TextPosition && + other.offset == offset && + other.affinity == affinity; } @override @@ -456,10 +577,10 @@ class TextRange { const TextRange({ required this.start, required this.end, - }) : assert(start != null && start >= -1), // ignore: unnecessary_null_comparison - assert(end != null && end >= -1); // ignore: unnecessary_null_comparison + }) : assert(start >= -1), + assert(end >= -1); const TextRange.collapsed(int offset) - : assert(offset != null && offset >= -1), // ignore: unnecessary_null_comparison + : assert(offset >= -1), start = offset, end = offset; static const TextRange empty = TextRange(start: -1, end: -1); @@ -488,9 +609,7 @@ class TextRange { if (identical(this, other)) { return true; } - return other is TextRange - && other.start == start - && other.end == end; + return other is TextRange && other.start == start && other.end == end; } @override @@ -514,8 +633,7 @@ class ParagraphConstraints { if (other.runtimeType != runtimeType) { return false; } - return other is ParagraphConstraints - && other.width == width; + return other is ParagraphConstraints && other.width == width; } @override @@ -586,11 +704,10 @@ abstract class Paragraph { abstract class ParagraphBuilder { factory ParagraphBuilder(ParagraphStyle style) { - if (engine.experimentalUseSkia) { + if (engine.useCanvasKit) { return engine.CkParagraphBuilder(style); - } else { - return engine.EngineParagraphBuilder(style as engine.EngineParagraphStyle); } + return engine.CanvasParagraphBuilder(style as engine.EngineParagraphStyle); } void pushStyle(TextStyle style); void pop(); @@ -609,13 +726,13 @@ abstract class ParagraphBuilder { } Future loadFontFromList(Uint8List list, {String? fontFamily}) { - if (engine.experimentalUseSkia) { - return engine.skiaFontCollection.loadFontFromList(list, fontFamily: fontFamily).then( - (_) => engine.sendFontChangeMessage() - ); + if (engine.useCanvasKit) { + return engine.skiaFontCollection + .loadFontFromList(list, fontFamily: fontFamily) + .then((_) => engine.sendFontChangeMessage()); } else { - return _fontCollection!.loadFontFromList(list, fontFamily: fontFamily!).then( - (_) => engine.sendFontChangeMessage() - ); + return _fontCollection! + .loadFontFromList(list, fontFamily: fontFamily!) + .then((_) => engine.sendFontChangeMessage()); } } diff --git a/lib/web_ui/lib/src/ui/tile_mode.dart b/lib/web_ui/lib/src/ui/tile_mode.dart index 567b9a551573b..2f98e128e5c78 100644 --- a/lib/web_ui/lib/src/ui/tile_mode.dart +++ b/lib/web_ui/lib/src/ui/tile_mode.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 part of ui; // These enum values must be kept in sync with SkShader::TileMode. @@ -10,4 +9,5 @@ enum TileMode { clamp, repeated, mirror, + decal, } diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index fa58ff01e5537..b69d30765e4ef 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -1,262 +1,138 @@ // 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. -// Synced 2019-05-30T14:20:57.841444. -// @dart = 2.10 part of ui; -typedef VoidCallback = void Function(); -typedef FrameCallback = void Function(Duration duration); -typedef TimingsCallback = void Function(List timings); -typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); -typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args); -typedef PlatformMessageResponseCallback = void Function(ByteData? data); -typedef PlatformMessageCallback = void Function( - String name, ByteData? data, PlatformMessageResponseCallback? callback); - -enum AppLifecycleState { - resumed, - inactive, - paused, - detached, +abstract class FlutterView { + PlatformDispatcher get platformDispatcher; + ViewConfiguration get viewConfiguration; + double get devicePixelRatio => viewConfiguration.devicePixelRatio; + Rect get physicalGeometry => viewConfiguration.geometry; + Size get physicalSize => viewConfiguration.geometry.size; + WindowPadding get viewInsets => viewConfiguration.viewInsets; + WindowPadding get viewPadding => viewConfiguration.viewPadding; + WindowPadding get systemGestureInsets => viewConfiguration.systemGestureInsets; + WindowPadding get padding => viewConfiguration.padding; + void render(Scene scene) => platformDispatcher.render(scene, this); } -abstract class WindowPadding { - const factory WindowPadding._({ - required double left, - required double top, - required double right, - required double bottom, - }) = engine.WindowPadding; - - double get left; - double get top; - double get right; - double get bottom; - static const WindowPadding zero = WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); +abstract class FlutterWindow extends FlutterView { + @override + PlatformDispatcher get platformDispatcher; @override - String toString() { - return 'WindowPadding(left: $left, top: $top, right: $right, bottom: $bottom)'; - } + ViewConfiguration get viewConfiguration; } -class Locale { - const Locale( - this._languageCode, [ - this._countryCode, - ]) : assert(_languageCode != null), // ignore: unnecessary_null_comparison - assert(_languageCode != ''), - scriptCode = null; - const Locale.fromSubtags({ - String languageCode = 'und', - this.scriptCode, - String? countryCode, - }) : assert(languageCode != null), // ignore: unnecessary_null_comparison - assert(languageCode != ''), - _languageCode = languageCode, - assert(scriptCode != ''), - assert(countryCode != ''), - _countryCode = countryCode; - String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; - final String _languageCode; - - // This map is generated by //flutter/tools/gen_locale.dart - // Mappings generated for language subtag registry as of 2019-02-27. - static const Map _deprecatedLanguageSubtagMap = { - 'in': 'id', // Indonesian; deprecated 1989-01-01 - 'iw': 'he', // Hebrew; deprecated 1989-01-01 - 'ji': 'yi', // Yiddish; deprecated 1989-01-01 - 'jw': 'jv', // Javanese; deprecated 2001-08-13 - 'mo': 'ro', // Moldavian, Moldovan; deprecated 2008-11-22 - 'aam': 'aas', // Aramanik; deprecated 2015-02-12 - 'adp': 'dz', // Adap; deprecated 2015-02-12 - 'aue': 'ktz', // ǂKxʼauǁʼein; deprecated 2015-02-12 - 'ayx': 'nun', // Ayi (China); deprecated 2011-08-16 - 'bgm': 'bcg', // Baga Mboteni; deprecated 2016-05-30 - 'bjd': 'drl', // Bandjigali; deprecated 2012-08-12 - 'ccq': 'rki', // Chaungtha; deprecated 2012-08-12 - 'cjr': 'mom', // Chorotega; deprecated 2010-03-11 - 'cka': 'cmr', // Khumi Awa Chin; deprecated 2012-08-12 - 'cmk': 'xch', // Chimakum; deprecated 2010-03-11 - 'coy': 'pij', // Coyaima; deprecated 2016-05-30 - 'cqu': 'quh', // Chilean Quechua; deprecated 2016-05-30 - 'drh': 'khk', // Darkhat; deprecated 2010-03-11 - 'drw': 'prs', // Darwazi; deprecated 2010-03-11 - 'gav': 'dev', // Gabutamon; deprecated 2010-03-11 - 'gfx': 'vaj', // Mangetti Dune ǃXung; deprecated 2015-02-12 - 'ggn': 'gvr', // Eastern Gurung; deprecated 2016-05-30 - 'gti': 'nyc', // Gbati-ri; deprecated 2015-02-12 - 'guv': 'duz', // Gey; deprecated 2016-05-30 - 'hrr': 'jal', // Horuru; deprecated 2012-08-12 - 'ibi': 'opa', // Ibilo; deprecated 2012-08-12 - 'ilw': 'gal', // Talur; deprecated 2013-09-10 - 'jeg': 'oyb', // Jeng; deprecated 2017-02-23 - 'kgc': 'tdf', // Kasseng; deprecated 2016-05-30 - 'kgh': 'kml', // Upper Tanudan Kalinga; deprecated 2012-08-12 - 'koj': 'kwv', // Sara Dunjo; deprecated 2015-02-12 - 'krm': 'bmf', // Krim; deprecated 2017-02-23 - 'ktr': 'dtp', // Kota Marudu Tinagas; deprecated 2016-05-30 - 'kvs': 'gdj', // Kunggara; deprecated 2016-05-30 - 'kwq': 'yam', // Kwak; deprecated 2015-02-12 - 'kxe': 'tvd', // Kakihum; deprecated 2015-02-12 - 'kzj': 'dtp', // Coastal Kadazan; deprecated 2016-05-30 - 'kzt': 'dtp', // Tambunan Dusun; deprecated 2016-05-30 - 'lii': 'raq', // Lingkhim; deprecated 2015-02-12 - 'lmm': 'rmx', // Lamam; deprecated 2014-02-28 - 'meg': 'cir', // Mea; deprecated 2013-09-10 - 'mst': 'mry', // Cataelano Mandaya; deprecated 2010-03-11 - 'mwj': 'vaj', // Maligo; deprecated 2015-02-12 - 'myt': 'mry', // Sangab Mandaya; deprecated 2010-03-11 - 'nad': 'xny', // Nijadali; deprecated 2016-05-30 - 'ncp': 'kdz', // Ndaktup; deprecated 2018-03-08 - 'nnx': 'ngv', // Ngong; deprecated 2015-02-12 - 'nts': 'pij', // Natagaimas; deprecated 2016-05-30 - 'oun': 'vaj', // ǃOǃung; deprecated 2015-02-12 - 'pcr': 'adx', // Panang; deprecated 2013-09-10 - 'pmc': 'huw', // Palumata; deprecated 2016-05-30 - 'pmu': 'phr', // Mirpur Panjabi; deprecated 2015-02-12 - 'ppa': 'bfy', // Pao; deprecated 2016-05-30 - 'ppr': 'lcq', // Piru; deprecated 2013-09-10 - 'pry': 'prt', // Pray 3; deprecated 2016-05-30 - 'puz': 'pub', // Purum Naga; deprecated 2014-02-28 - 'sca': 'hle', // Sansu; deprecated 2012-08-12 - 'skk': 'oyb', // Sok; deprecated 2017-02-23 - 'tdu': 'dtp', // Tempasuk Dusun; deprecated 2016-05-30 - 'thc': 'tpo', // Tai Hang Tong; deprecated 2016-05-30 - 'thx': 'oyb', // The; deprecated 2015-02-12 - 'tie': 'ras', // Tingal; deprecated 2011-08-16 - 'tkk': 'twm', // Takpa; deprecated 2011-08-16 - 'tlw': 'weo', // South Wemale; deprecated 2012-08-12 - 'tmp': 'tyj', // Tai Mène; deprecated 2016-05-30 - 'tne': 'kak', // Tinoc Kallahan; deprecated 2016-05-30 - 'tnf': 'prs', // Tangshewi; deprecated 2010-03-11 - 'tsf': 'taj', // Southwestern Tamang; deprecated 2015-02-12 - 'uok': 'ema', // Uokha; deprecated 2015-02-12 - 'xba': 'cax', // Kamba (Brazil); deprecated 2016-05-30 - 'xia': 'acn', // Xiandao; deprecated 2013-09-10 - 'xkh': 'waw', // Karahawyana; deprecated 2016-05-30 - 'xsj': 'suj', // Subi; deprecated 2015-02-12 - 'ybd': 'rki', // Yangbye; deprecated 2012-08-12 - 'yma': 'lrr', // Yamphe; deprecated 2012-08-12 - 'ymt': 'mtm', // Mator-Taygi-Karagas; deprecated 2015-02-12 - 'yos': 'zom', // Yos; deprecated 2013-09-10 - 'yuu': 'yug', // Yugh; deprecated 2014-02-28 - }; - final String? scriptCode; - String? get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; - final String? _countryCode; - - // This map is generated by //flutter/tools/gen_locale.dart - // Mappings generated for language subtag registry as of 2019-02-27. - static const Map _deprecatedRegionSubtagMap = { - 'BU': 'MM', // Burma; deprecated 1989-12-05 - 'DD': 'DE', // German Democratic Republic; deprecated 1990-10-30 - 'FX': 'FR', // Metropolitan France; deprecated 1997-07-14 - 'TP': 'TL', // East Timor; deprecated 2002-05-20 - 'YD': 'YE', // Democratic Yemen; deprecated 1990-08-14 - 'ZR': 'CD', // Zaire; deprecated 1997-07-14 - }; +abstract class SingletonFlutterWindow extends FlutterWindow { + VoidCallback? get onMetricsChanged => platformDispatcher.onMetricsChanged; + set onMetricsChanged(VoidCallback? callback) { + platformDispatcher.onMetricsChanged = callback; + } - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - return other is Locale - && other.languageCode == languageCode - && other.scriptCode == scriptCode - && other.countryCode == countryCode; + Locale get locale => platformDispatcher.locale; + List get locales => platformDispatcher.locales; + + Locale? computePlatformResolvedLocale(List supportedLocales) { + return platformDispatcher.computePlatformResolvedLocale(supportedLocales); } - @override - int get hashCode => hashValues(languageCode, scriptCode, countryCode); + VoidCallback? get onLocaleChanged => platformDispatcher.onLocaleChanged; + set onLocaleChanged(VoidCallback? callback) { + platformDispatcher.onLocaleChanged = callback; + } + + String get initialLifecycleState => platformDispatcher.initialLifecycleState; + + double get textScaleFactor => platformDispatcher.textScaleFactor; + bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat; + + VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback? callback) { + platformDispatcher.onTextScaleFactorChanged = callback; + } + + Brightness get platformBrightness => platformDispatcher.platformBrightness; + + VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback? callback) { + platformDispatcher.onPlatformBrightnessChanged = callback; + } + + FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame; + set onBeginFrame(FrameCallback? callback) { + platformDispatcher.onBeginFrame = callback; + } + + VoidCallback? get onDrawFrame => platformDispatcher.onDrawFrame; + set onDrawFrame(VoidCallback? callback) { + platformDispatcher.onDrawFrame = callback; + } + + TimingsCallback? get onReportTimings => platformDispatcher.onReportTimings; + set onReportTimings(TimingsCallback? callback) { + platformDispatcher.onReportTimings = callback; + } + + PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback? callback) { + platformDispatcher.onPointerDataPacket = callback; + } + + KeyDataCallback? get onKeyData => platformDispatcher.onKeyData; + set onKeyData(KeyDataCallback? callback) { + platformDispatcher.onKeyData = callback; + } + + String get defaultRouteName => platformDispatcher.defaultRouteName; + + void scheduleFrame() => platformDispatcher.scheduleFrame(); @override - String toString() => _rawToString('_'); + void render(Scene scene) => platformDispatcher.render(scene, this); - // TODO(yjbanov): implement to match flutter native. - String toLanguageTag() => _rawToString('-'); + bool get semanticsEnabled => platformDispatcher.semanticsEnabled; - String _rawToString(String separator) { - final StringBuffer out = StringBuffer(languageCode); - if (scriptCode != null) { - out.write('$separator$scriptCode'); - } - if (_countryCode != null) { - out.write('$separator$countryCode'); - } - return out.toString(); + VoidCallback? get onSemanticsEnabledChanged => platformDispatcher.onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback? callback) { + platformDispatcher.onSemanticsEnabledChanged = callback; } -} -abstract class Window { - double get devicePixelRatio; - Size get physicalSize; - WindowPadding get viewInsets => WindowPadding.zero; - - WindowPadding get viewPadding => WindowPadding.zero; - - WindowPadding get systemGestureInsets => WindowPadding.zero; - WindowPadding get padding => WindowPadding.zero; - double get textScaleFactor => _textScaleFactor; - double _textScaleFactor = 1.0; - bool get alwaysUse24HourFormat => _alwaysUse24HourFormat; - bool _alwaysUse24HourFormat = false; - VoidCallback? get onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback? callback); - Brightness get platformBrightness; - VoidCallback? get onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback? callback); - VoidCallback? get onMetricsChanged; - set onMetricsChanged(VoidCallback? callback); - Locale? get locale; - List? get locales; - Locale? computePlatformResolvedLocale(List supportedLocales) { - // TODO(garyq): Implement on web. - return null; + SemanticsActionCallback? get onSemanticsAction => platformDispatcher.onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback? callback) { + platformDispatcher.onSemanticsAction = callback; } - VoidCallback? get onLocaleChanged; - set onLocaleChanged(VoidCallback? callback); - void scheduleFrame(); - FrameCallback? get onBeginFrame; - set onBeginFrame(FrameCallback? callback); - TimingsCallback? get onReportTimings; - set onReportTimings(TimingsCallback? callback); - VoidCallback? get onDrawFrame; - set onDrawFrame(VoidCallback? callback); - PointerDataPacketCallback? get onPointerDataPacket; - set onPointerDataPacket(PointerDataPacketCallback? callback); - String get defaultRouteName; - bool get semanticsEnabled => engine.EngineSemanticsOwner.instance.semanticsEnabled; - VoidCallback? get onSemanticsEnabledChanged; - set onSemanticsEnabledChanged(VoidCallback? callback); - SemanticsActionCallback? get onSemanticsAction; - set onSemanticsAction(SemanticsActionCallback? callback); - VoidCallback? get onAccessibilityFeaturesChanged; - set onAccessibilityFeaturesChanged(VoidCallback? callback); - PlatformMessageCallback? get onPlatformMessage; - set onPlatformMessage(PlatformMessageCallback? callback); - void updateSemantics(SemanticsUpdate update) { - engine.EngineSemanticsOwner.instance.updateSemantics(update); + FrameData get frameData => const FrameData._(); + + VoidCallback? get onFrameDataChanged => null; + set onFrameDataChanged(VoidCallback? callback) {} + + AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; + + VoidCallback? get onAccessibilityFeaturesChanged => + platformDispatcher.onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback? callback) { + platformDispatcher.onAccessibilityFeaturesChanged = callback; } + void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update); + void sendPlatformMessage( String name, ByteData? data, PlatformMessageResponseCallback? callback, - ); - AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; - AccessibilityFeatures _accessibilityFeatures = AccessibilityFeatures._(0); - void render(Scene scene); - - String get initialLifecycleState => 'AppLifecycleState.resumed'; + ) { + platformDispatcher.sendPlatformMessage(name, data, callback); + } - void setIsolateDebugName(String name) {} + PlatformMessageCallback? get onPlatformMessage => platformDispatcher.onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback? callback) { + platformDispatcher.onPlatformMessage = callback; + } - ByteData? getPersistentIsolateData() => null; + void setIsolateDebugName(String name) => PlatformDispatcher.instance.setIsolateDebugName(name); } class AccessibilityFeatures { @@ -271,6 +147,7 @@ class AccessibilityFeatures { // A bitfield which represents each enabled feature. final int _index; + bool get accessibleNavigation => _kAccessibleNavigation & _index != 0; bool get invertColors => _kInvertColorsIndex & _index != 0; bool get disableAnimations => _kDisableAnimationsIndex & _index != 0; @@ -321,7 +198,7 @@ enum Brightness { } // Unimplemented classes. -// TODO(flutter_web): see https://github.com/flutter/flutter/issues/33614. +// TODO(dit): see https://github.com/flutter/flutter/issues/33614. class CallbackHandle { CallbackHandle.fromRawHandle(this._handle) : assert(_handle != null, "'_handle' must not be null."); // ignore: unnecessary_null_comparison @@ -334,10 +211,11 @@ class CallbackHandle { bool operator ==(Object other) => identical(this, other); @override + // ignore: unnecessary_overrides int get hashCode => super.hashCode; } -// TODO(flutter_web): see https://github.com/flutter/flutter/issues/33615. +// TODO(dit): see https://github.com/flutter/flutter/issues/33615. class PluginUtilities { // This class is only a namespace, and should not be instantiated or // extended directly. @@ -352,7 +230,6 @@ class PluginUtilities { } } -// TODO(flutter_web): probably dont implement this one. class IsolateNameServer { // This class is only a namespace, and should not be instantiated or // extended directly. @@ -371,50 +248,49 @@ class IsolateNameServer { } } -enum FramePhase { - vsyncStart, - buildStart, - buildFinish, - rasterStart, - rasterFinish, -} +SingletonFlutterWindow get window => engine.window; -class FrameTiming { - factory FrameTiming({ - required int vsyncStart, - required int buildStart, - required int buildFinish, - required int rasterStart, - required int rasterFinish, - }) { - return FrameTiming._([ - vsyncStart, - buildStart, - buildFinish, - rasterStart, - rasterFinish - ]); - } - FrameTiming._(List timestamps) - : assert(timestamps.length == FramePhase.values.length), - _timestamps = timestamps; +class FrameData { + const FrameData._({this.frameNumber = -1}); - int timestampInMicroseconds(FramePhase phase) => _timestamps[phase.index]; + const FrameData.webOnly() : frameNumber = -1; - Duration _rawDuration(FramePhase phase) => Duration(microseconds: _timestamps[phase.index]); - Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); - Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); - Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); - Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); + final int frameNumber; +} + +class GestureSettings { + const GestureSettings({ + this.physicalTouchSlop, + this.physicalDoubleTapSlop, + }); - final List _timestamps; // in microseconds + final double? physicalTouchSlop; - String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms'; + final double? physicalDoubleTapSlop; + + GestureSettings copyWith({ + double? physicalTouchSlop, + double? physicalDoubleTapSlop, + }) { + return GestureSettings( + physicalTouchSlop: physicalTouchSlop ?? this.physicalTouchSlop, + physicalDoubleTapSlop: physicalDoubleTapSlop ?? this.physicalDoubleTapSlop, + ); + } @override - String toString() { - return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, vsyncOverhead: ${_formatMS(vsyncOverhead)}, totalSpan: ${_formatMS(totalSpan)})'; + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is GestureSettings && + other.physicalTouchSlop == physicalTouchSlop && + other.physicalDoubleTapSlop == physicalDoubleTapSlop; } -} -Window get window => engine.window; + @override + int get hashCode => hashValues(physicalTouchSlop, physicalDoubleTapSlop); + + @override + String toString() => 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; +} diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index 13d5ee2813b20..7b59a3d2d68dc 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -5,7 +5,6 @@ /// This library defines the web equivalent of the native dart:ui. /// /// All types in this library are public. -// @dart = 2.10 library ui; import 'dart:async'; @@ -24,11 +23,13 @@ part 'src/ui/compositing.dart'; part 'src/ui/geometry.dart'; part 'src/ui/hash_codes.dart'; part 'src/ui/initialization.dart'; +part 'src/ui/key.dart'; part 'src/ui/lerp.dart'; part 'src/ui/natives.dart'; part 'src/ui/painting.dart'; part 'src/ui/path.dart'; part 'src/ui/path_metrics.dart'; +part 'src/ui/platform_dispatcher.dart'; part 'src/ui/pointer.dart'; part 'src/ui/semantics.dart'; part 'src/ui/test_embedding.dart'; @@ -57,93 +58,20 @@ void webOnlySetPluginHandler(Future Function(String, ByteData?, PlatformMe // does not allow exported non-migrated libraries from migrated libraries. When `dart:_engine` // is migrated, we can move it back. +/// A function which takes a unique `id` and creates an HTML element. +typedef PlatformViewFactory = html.Element Function(int viewId); + /// A registry for factories that create platform views. class PlatformViewRegistry { - final Map registeredFactories = - {}; - - final Map _createdViews = {}; - - /// Private constructor so this class can be a singleton. - PlatformViewRegistry._(); - /// Register [viewTypeId] as being creating by the given [factory]. - bool registerViewFactory(String viewTypeId, PlatformViewFactory factory) { - if (registeredFactories.containsKey(viewTypeId)) { - return false; - } - registeredFactories[viewTypeId] = factory; - return true; - } - - /// Returns the view that has been created with the given [id], or `null` if - /// no such view exists. - html.Element? getCreatedView(int id) { - return _createdViews[id]; + bool registerViewFactory(String viewTypeId, PlatformViewFactory viewFactory) { + // TODO(web): Deprecate this once there's another way of calling `registerFactory` (js interop?) + return engine.platformViewManager.registerFactory(viewTypeId, viewFactory); } } -/// A function which takes a unique [id] and creates an HTML element. -typedef PlatformViewFactory = html.Element Function(int viewId); - /// The platform view registry for this app. -final PlatformViewRegistry platformViewRegistry = PlatformViewRegistry._(); - -/// Handles a platform call to `flutter/platform_views`. -/// -/// Used to create platform views. -void handlePlatformViewCall( - ByteData data, - PlatformMessageResponseCallback callback, -) { - const engine.MethodCodec codec = engine.StandardMethodCodec(); - final engine.MethodCall decoded = codec.decodeMethodCall(data); - - switch (decoded.method) { - case 'create': - _createPlatformView(decoded, callback); - return; - case 'dispose': - _disposePlatformView(decoded, callback); - return; - } - callback(null); -} - -void _createPlatformView( - engine.MethodCall methodCall, PlatformMessageResponseCallback callback) { - final Map args = methodCall.arguments; - final int id = args['id']; - final String viewType = args['viewType']; - const engine.MethodCodec codec = engine.StandardMethodCodec(); - - // TODO(het): Use 'direction', 'width', and 'height'. - final PlatformViewFactory? platformViewFactory = platformViewRegistry.registeredFactories[viewType]; - if (platformViewFactory == null) { - callback(codec.encodeErrorEnvelope( - code: 'Unregistered factory', - message: "No factory registered for viewtype '$viewType'", - )); - return; - } - // TODO(het): Use creation parameters. - final html.Element element = platformViewFactory(id); - - platformViewRegistry._createdViews[id] = element; - callback(codec.encodeSuccessEnvelope(null)); -} - -void _disposePlatformView( - engine.MethodCall methodCall, PlatformMessageResponseCallback callback) { - final int id = methodCall.arguments; - const engine.MethodCodec codec = engine.StandardMethodCodec(); - - // Remove the root element of the view from the DOM. - platformViewRegistry._createdViews[id]?.remove(); - platformViewRegistry._createdViews.remove(id); - - callback(codec.encodeSuccessEnvelope(null)); -} +final PlatformViewRegistry platformViewRegistry = PlatformViewRegistry(); // TODO(yjbanov): remove _Callback, _Callbacker, and _futurize. They are here only // because the analyzer wasn't able to infer the correct types during diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index 84e6d33eccde1..64046a798235d 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -1,37 +1,30 @@ name: ui publish_to: none +# Keep the SDK version range in sync with pubspecs under web_sdk environment: - sdk: ">=2.10.0-0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: - meta: 1.1.8 + js: 0.6.3 + meta: 1.3.0 dev_dependencies: - analyzer: 0.39.15 - archive: 2.0.13 - http: 0.12.1 - image: 2.1.13 - js: 0.6.1+1 - mockito: 4.1.1 - path: 1.7.0 - test: 1.14.3 - quiver: 2.1.3 - build_resolvers: 1.3.10 - build_runner: 1.10.0 - build_test: 1.0.0 - build_web_compilers: 2.11.0 - yaml: 2.2.1 - watcher: 0.9.7+15 + archive: 3.1.2 + html: 0.15.0 + http: 0.13.0 + image: 3.0.1 + path: 1.8.0 + quiver: 3.0.0 + test: 1.17.7 + yaml: 3.0.0 + watcher: 1.0.0 + web_test_utils: + path: ../../web_sdk/web_test_utils web_engine_tester: path: ../../web_sdk/web_engine_tester simulators: git: url: git://github.com/flutter/web_installers.git path: packages/simulators/ - ref: 4a9643e55e9bd127f4e4e3cb20e562ed5d5b9aa7 - web_driver_installer: - git: - url: git://github.com/flutter/web_installers.git - path: packages/web_drivers/ - ref: 1cea0d79cad1ebc217c4bcbeba1be41470674a49 + ref: 4a7b0a2c84b8993bf4d19030218de38c18838c26 diff --git a/lib/web_ui/test/alarm_clock_test.dart b/lib/web_ui/test/alarm_clock_test.dart index b56d0dc27d798..4a1c744f68ddf 100644 --- a/lib/web_ui/test/alarm_clock_test.dart +++ b/lib/web_ui/test/alarm_clock_test.dart @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; import 'package:quiver/testing/async.dart'; import 'package:quiver/time.dart'; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/alarm_clock.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -77,13 +76,13 @@ void _alarmClockTests() { expect(fakeAsync.nonPeriodicTimerCount, 1); expect(callCount, 0); - alarm.datetime = alarm.datetime.add(Duration.zero); + alarm.datetime = alarm.datetime!.add(Duration.zero); expect(fakeAsync.nonPeriodicTimerCount, 1); expect(callCount, 0); fakeAsync.elapse(const Duration(seconds: 30)); - alarm.datetime = alarm.datetime.add(Duration.zero); + alarm.datetime = alarm.datetime!.add(Duration.zero); expect(fakeAsync.nonPeriodicTimerCount, 1); expect(callCount, 0); @@ -117,7 +116,7 @@ void _alarmClockTests() { expect(callCount, 0); // Reschedule. - alarm.datetime = alarm.datetime.add(const Duration(minutes: 1)); + alarm.datetime = alarm.datetime!.add(const Duration(minutes: 1)); fakeAsync.elapse(const Duration(minutes: 1)); @@ -142,7 +141,7 @@ void _alarmClockTests() { expect(callCount, 0); // Reschedule to an earlier time that's still in the future. - alarm.datetime = alarm.datetime.subtract(const Duration(seconds: 15)); + alarm.datetime = alarm.datetime!.subtract(const Duration(seconds: 15)); fakeAsync.elapse(const Duration(seconds: 45)); expect(callCount, 1); diff --git a/lib/web_ui/test/browser_detect_test.dart b/lib/web_ui/test/browser_detect_test.dart new file mode 100644 index 0000000000000..bfa1fa7b8c6e6 --- /dev/null +++ b/lib/web_ui/test/browser_detect_test.dart @@ -0,0 +1,164 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine/browser_detection.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('detectBrowserEngineByVendorAgent', () { + test('Should detect Blink', () { + // Chrome Version 89.0.4389.90 (Official Build) (x86_64) / MacOS + final BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + 'Google Inc.', + 'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 ' + '(khtml, like gecko) chrome/89.0.4389.90 safari/537.36'); + expect(browserEngine, BrowserEngine.blink); + }); + + test('Should detect Firefox', () { + // 85.0.2 (64-bit) / MacOS + final BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + '', + 'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) ' + 'gecko/20100101 firefox/85.0'); + expect(browserEngine, BrowserEngine.firefox); + }); + + test('Should detect Safari', () { + final BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + 'Apple Computer, Inc.', + 'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 ' + '(khtml, like gecko) version/14.0.3 safari/605.1.15'); + expect(browserEngine, BrowserEngine.webkit); + }); + + test('Should detect Samsung browser', () { + // Samsung 13.2.1.70 on Galaxy Tab S6. + final BrowserEngine browserEngine = detectBrowserEngineByVendorAgent( + 'Google Inc.', + 'mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko)' + ' samsungbrowser/13.2 chrome/83.0.4103.106 safari/537.36'); + expect(browserEngine, BrowserEngine.samsung); + }); + }); + + group('detectOperatingSystem', () { + void expectOs( + OperatingSystem expectedOs, { + String platform = 'any', + String ua = 'any', + int touchPoints = 0, + }) { + expect( + detectOperatingSystem( + overridePlatform: platform, + overrideUserAgent: ua, + overrideMaxTouchPoints: touchPoints, + ), + expectedOs); + } + + test('Determine unknown for weird values of platform/ua', () { + expectOs(OperatingSystem.unknown); + }); + + test('Determine MacOS if platform starts by Mac', () { + expectOs( + OperatingSystem.macOs, + platform: 'MacIntel', + ); + expectOs( + OperatingSystem.macOs, + platform: 'MacAnythingElse', + ); + }); + + test('Determine iOS if platform contains iPhone/iPad/iPod', () { + expectOs( + OperatingSystem.iOs, + platform: 'iPhone', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPhone Simulator', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPad', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPad Simulator', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPod', + ); + expectOs( + OperatingSystem.iOs, + platform: 'iPod Simulator', + ); + }); + + // See https://github.com/flutter/flutter/issues/81918 + test('Tell apart MacOS from iOS requesting a desktop site.', () { + expectOs( + OperatingSystem.macOs, + platform: 'MacARM', + ); + + expectOs( + OperatingSystem.iOs, + platform: 'MacARM', + touchPoints: 5, + ); + }); + + test('Determine Android if user agent contains Android', () { + expectOs( + OperatingSystem.android, + ua: 'Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', + ); + }); + + test('Determine Linux if the platform begins with Linux', () { + expectOs( + OperatingSystem.linux, + platform: 'Linux', + ); + expectOs( + OperatingSystem.linux, + platform: 'Linux armv8l', + ); + expectOs( + OperatingSystem.linux, + platform: 'Linux x86_64', + ); + }); + + test('Determine Windows if the platform begins with Win', () { + expectOs( + OperatingSystem.windows, + platform: 'Windows', + ); + expectOs( + OperatingSystem.windows, + platform: 'Win32', + ); + expectOs( + OperatingSystem.windows, + platform: 'Win16', + ); + expectOs( + OperatingSystem.windows, + platform: 'WinCE', + ); + }); + }); +} diff --git a/lib/web_ui/test/canvas_test.dart b/lib/web_ui/test/canvas_test.dart index 53635e0e3ccee..9e87bd100d835 100644 --- a/lib/web_ui/test/canvas_test.dart +++ b/lib/web_ui/test/canvas_test.dart @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + import 'mock_engine_canvas.dart'; void main() { @@ -21,25 +20,25 @@ void testMain() { }); group('EngineCanvas', () { - MockEngineCanvas mockCanvas; - ui.Paragraph paragraph; + late MockEngineCanvas mockCanvas; + late ui.Paragraph paragraph; void testCanvas( - String description, void Function(EngineCanvas canvas) testFn, - {ui.Rect canvasSize, ui.VoidCallback whenDone}) { - canvasSize ??= const ui.Rect.fromLTWH(0, 0, 100, 100); + String description, + void Function(EngineCanvas canvas) testFn, { + ui.Rect canvasSize = const ui.Rect.fromLTWH(0, 0, 100, 100), + ui.VoidCallback? whenDone, + }) { test(description, () { - testFn(BitmapCanvas(canvasSize)); - testFn(DomCanvas()); + testFn(BitmapCanvas(canvasSize, RenderStrategy())); + testFn(DomCanvas(domRenderer.createElement('flt-picture'))); testFn(mockCanvas = MockEngineCanvas()); - if (whenDone != null) { - whenDone(); - } + whenDone?.call(); }); } testCanvas('draws laid out paragraph', (EngineCanvas canvas) { - final ui.Rect screenRect = const ui.Rect.fromLTWH(0, 0, 100, 100); + const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 100, 100); final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); @@ -64,7 +63,7 @@ void testMain() { testCanvas('ignores paragraphs that were not laid out', (EngineCanvas canvas) { - final ui.Rect screenRect = const ui.Rect.fromLTWH(0, 0, 100, 100); + const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 100, 100); final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); diff --git a/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart b/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart new file mode 100644 index 0000000000000..54acaa9c759bf --- /dev/null +++ b/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 500); + +void testMain() { + group('BackdropFilter', () { + setUpCanvasKitTest(); + window.debugOverrideDevicePixelRatio(1.0); + + test('blur renders to the edges', () async { + // Make a checkerboard picture so we can see the blur. + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + canvas.drawColor(const ui.Color(0xffffffff), ui.BlendMode.srcOver); + final double sideLength = region.width / 20; + final int rows = (region.height / sideLength).ceil(); + + for (int row = 0; row < rows; row++) { + for (int column = 0; column < 10; column++) { + final ui.Rect rect = ui.Rect.fromLTWH( + row.isEven + ? (column * 2) * sideLength + : (column * 2 + 1) * sideLength, + row * sideLength, + sideLength, + sideLength, + ); + canvas.drawRect(rect, CkPaint()..color = const ui.Color(0xffff0000)); + } + } + final CkPicture checkerboard = recorder.endRecording(); + + final LayerSceneBuilder builder = LayerSceneBuilder(); + builder.pushOffset(0, 0); + builder.addPicture(ui.Offset.zero, checkerboard); + builder.pushBackdropFilter(ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10)); + EnginePlatformDispatcher.instance.rasterizer! + .draw(builder.build().layerTree); + await matchGoldenFile('canvaskit_backdropfilter_blur_edges.png', + region: region); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} diff --git a/lib/web_ui/test/canvaskit/canvas_golden_test.dart b/lib/web_ui/test/canvaskit/canvas_golden_test.dart new file mode 100644 index 0000000000000..414be1ebc793f --- /dev/null +++ b/lib/web_ui/test/canvaskit/canvas_golden_test.dart @@ -0,0 +1,1385 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +// TODO(yjbanov): tests that render using Noto are not hermetic, as those fonts +// come from fonts.google.com, where fonts can change any time. +// These tests are skipped. +// https://github.com/flutter/flutter/issues/86432 +const bool kIssue86432Exists = true; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect kDefaultRegion = ui.Rect.fromLTRB(0, 0, 500, 250); + +Future matchPictureGolden(String goldenFile, CkPicture picture, + {ui.Rect region = kDefaultRegion, bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPicture(ui.Offset.zero, picture); + dispatcher.rasterizer!.draw(sb.build().layerTree); + await matchGoldenFile(goldenFile, + region: region, maxDiffRatePercent: 0.0, write: write); +} + +void testMain() { + group('CkCanvas', () { + setUpCanvasKitTest(); + + setUp(() { + expect(notoDownloadQueue.downloader.debugActiveDownloadCount, 0); + expect(notoDownloadQueue.isPending, isFalse); + }); + + tearDown(() { + expect(notoDownloadQueue.downloader.debugActiveDownloadCount, 0); + expect(notoDownloadQueue.isPending, isFalse); + }); + + test('renders using non-recording canvas if weak refs are supported', + () async { + expect(browserSupportsFinalizationRegistry, isTrue, + reason: 'This test specifically tests non-recording canvas, which ' + 'only works if FinalizationRegistry is available.'); + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(kDefaultRegion); + expect(canvas.runtimeType, CkCanvas); + drawTestPicture(canvas); + await matchPictureGolden( + 'canvaskit_picture.png', recorder.endRecording()); + // Safari does not support weak refs (FinalizationRegistry). + // This test should be revisited when Safari ships weak refs. + // TODO(yjbanov): skip Firefox due to a crash: https://github.com/flutter/flutter/issues/86632 + }, skip: isSafari || isFirefox); + + test('renders using a recording canvas if weak refs are not supported', + () async { + browserSupportsFinalizationRegistry = false; + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(kDefaultRegion); + expect(canvas, isA()); + drawTestPicture(canvas); + + final CkPicture originalPicture = recorder.endRecording(); + await matchPictureGolden('canvaskit_picture.png', originalPicture); + + final ByteData originalPixels = + (await (await originalPicture.toImage(50, 50)).toByteData())!; + + // Test that a picture restored from a snapshot looks the same. + final CkPictureSnapshot? snapshot = canvas.pictureSnapshot; + expect(snapshot, isNotNull); + final SkPicture restoredSkPicture = snapshot!.toPicture(); + expect(restoredSkPicture, isNotNull); + final CkPicture restoredPicture = CkPicture( + restoredSkPicture, const ui.Rect.fromLTRB(0, 0, 50, 50), snapshot); + final ByteData restoredPixels = + (await (await restoredPicture.toImage(50, 50)).toByteData())!; + + await matchPictureGolden('canvaskit_picture.png', restoredPicture); + expect(restoredPixels.buffer.asUint8List(), + originalPixels.buffer.asUint8List()); + }); + + // Regression test for https://github.com/flutter/flutter/issues/51237 + // Draws a grid of shadows at different offsets. Prior to directional + // light the shadows would shift depending on the offset. With directional + // light the cells in the grid must look identical. + test('uses directional shadows', () async { + const ui.Rect region = ui.Rect.fromLTRB(0, 0, 820, 420); + final CkPicture picture = paintPicture(region, (CkCanvas canvas) { + final CkPath shape = CkPath() + ..addRect(const ui.Rect.fromLTRB(0, 0, 40, 40)); + final CkPaint shapePaint = CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const ui.Color(0xFF009900); + final CkPaint shadowBoundsPaint = CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const ui.Color(0xFF000099); + canvas.translate(20, 20); + + for (int row = 0; row < 5; row += 1) { + canvas.save(); + for (int col = 0; col < 10; col += 1) { + final double elevation = 2 * (col % 5).toDouble(); + canvas.drawShadow(shape, const ui.Color(0xFFFF0000), elevation, true); + canvas.drawPath(shape, shapePaint); + + final PhysicalShapeEngineLayer psl = PhysicalShapeEngineLayer( + elevation, + const ui.Color(0xFF000000), + const ui.Color(0xFF000000), + shape, + ui.Clip.antiAlias, + ); + psl.preroll( + PrerollContext( + RasterCache(), + HtmlViewEmbedder.instance, + ), + Matrix4.identity(), + ); + canvas.drawRect(psl.paintBounds, shadowBoundsPaint); + + final CkParagraphBuilder pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.addText('$elevation'); + final CkParagraph p = pb.build(); + p.layout(const ui.ParagraphConstraints(width: 1000)); + canvas.drawParagraph( + p, ui.Offset(20 - p.maxIntrinsicWidth / 2, 20 - p.height / 2)); + canvas.translate(80, 0); + } + canvas.restore(); + canvas.translate(0, 80); + } + }); + await matchPictureGolden('canvaskit_directional_shadows.png', picture, + region: region); + }); + + test('computes shadow bounds correctly with parent transforms', () async { + const double rectSize = 50; + const double halfSize = rectSize / 2; + const double padding = 110; + const ui.Rect region = ui.Rect.fromLTRB( + 0, + 0, + (rectSize + padding) * 3 + padding, + (rectSize + padding) * 2 + padding, + ); + late List physicalShapeLayers; + + LayerTree buildTestScene({required bool paintShadowBounds}) { + final Iterator? shadowBounds = + paintShadowBounds ? physicalShapeLayers.iterator : null; + physicalShapeLayers = []; + + final LayerSceneBuilder builder = LayerSceneBuilder(); + builder.pushOffset(padding + halfSize, padding + halfSize); + + final CkPath shape = CkPath() + ..addRect( + const ui.Rect.fromLTRB(-halfSize, -halfSize, halfSize, halfSize)); + final CkPaint shadowBoundsPaint = CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const ui.Color(0xFF000099); + + for (int row = 0; row < 2; row += 1) { + for (int col = 0; col < 3; col += 1) { + builder.pushOffset( + col * (rectSize + padding), row * (rectSize + padding)); + builder.pushTransform(Float64List.fromList( + Matrix4.rotationZ(row * math.pi / 4).storage)); + final double scale = 1 / (1 + col); + builder.pushTransform(Float64List.fromList( + Matrix4.diagonal3Values(scale, scale, 1).storage)); + physicalShapeLayers.add(builder.pushPhysicalShape( + path: shape, + elevation: 6, + color: const ui.Color(0xFF009900), + shadowColor: const ui.Color(0xFF000000), + )); + if (shadowBounds != null) { + shadowBounds.moveNext(); + final ui.Rect bounds = shadowBounds.current.paintBounds; + builder.addPicture( + ui.Offset.zero, + paintPicture(region, (CkCanvas canvas) { + canvas.drawRect(bounds, shadowBoundsPaint); + })); + } + builder.pop(); + builder.pop(); + builder.pop(); + builder.pop(); + } + } + builder.pop(); + return builder.build().layerTree; + } + + // Render the scene once without painting the shadow bounds just to + // preroll the scene to compute the shadow bounds. + buildTestScene(paintShadowBounds: false).rootLayer.preroll( + PrerollContext( + RasterCache(), + HtmlViewEmbedder.instance, + ), + Matrix4.identity(), + ); + + // Render again, this time with the shadow bounds. + final LayerTree layerTree = buildTestScene(paintShadowBounds: true); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + dispatcher.rasterizer!.draw(layerTree); + await matchGoldenFile('canvaskit_shadow_bounds.png', region: region); + }); + + test('text styles - default', () async { + await testTextStyle('default'); + }); + + test('text styles - center aligned', () async { + await testTextStyle('center aligned', + paragraphTextAlign: ui.TextAlign.center); + }); + + test('text styles - right aligned', () async { + await testTextStyle('right aligned', + paragraphTextAlign: ui.TextAlign.right); + }); + + test('text styles - rtl', () async { + await testTextStyle('rtl', paragraphTextDirection: ui.TextDirection.rtl); + }); + + test('text styles - multiline', () async { + await testTextStyle('multiline', layoutWidth: 50); + }); + + test('text styles - max lines', () async { + await testTextStyle('max lines', paragraphMaxLines: 1, layoutWidth: 50); + }); + + test('text styles - ellipsis', () async { + await testTextStyle('ellipsis', + paragraphMaxLines: 1, paragraphEllipsis: '...', layoutWidth: 60); + }); + + test('text styles - paragraph font family', () async { + await testTextStyle('paragraph font family', paragraphFontFamily: 'Ahem'); + }); + + test('text styles - paragraph font size', () async { + await testTextStyle('paragraph font size', paragraphFontSize: 22); + }); + + test('text styles - paragraph height', () async { + await testTextStyle('paragraph height', + layoutWidth: 50, paragraphHeight: 1.5); + }); + + test('text styles - paragraph text height behavior', () async { + await testTextStyle('paragraph text height behavior', + layoutWidth: 50, + paragraphHeight: 1.5, + paragraphTextHeightBehavior: const ui.TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + )); + }); + + test('text styles - paragraph weight', () async { + await testTextStyle('paragraph weight', + paragraphFontWeight: ui.FontWeight.w900); + }); + + test('text style - paragraph font style', () async { + await testTextStyle( + 'paragraph font style', + paragraphFontStyle: ui.FontStyle.italic, + ); + }); + + // TODO(yjbanov): locales specified in paragraph styles don't work: + // https://github.com/flutter/flutter/issues/74687 + // TODO(yjbanov): spaces are not rendered correctly: + // https://github.com/flutter/flutter/issues/74742 + test('text styles - paragraph locale zh_CN', () async { + await testTextStyle('paragraph locale zh_CN', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('zh', 'CN')); + }, skip: kIssue86432Exists); + + test('text styles - paragraph locale zh_TW', () async { + await testTextStyle('paragraph locale zh_TW', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('zh', 'TW')); + }, skip: kIssue86432Exists); + + test('text styles - paragraph locale ja', () async { + await testTextStyle('paragraph locale ja', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('ja')); + }, skip: kIssue86432Exists); + + test('text styles - paragraph locale ko', () async { + await testTextStyle('paragraph locale ko', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('ko')); + }, skip: kIssue86432Exists); + + test('text styles - color', () async { + await testTextStyle('color', color: const ui.Color(0xFF009900)); + }); + + test('text styles - decoration', () async { + await testTextStyle('decoration', + decoration: ui.TextDecoration.underline); + }); + + test('text styles - decoration style', () async { + await testTextStyle('decoration style', + decoration: ui.TextDecoration.underline, + decorationStyle: ui.TextDecorationStyle.dashed); + }); + + test('text styles - decoration thickness', () async { + await testTextStyle('decoration thickness', + decoration: ui.TextDecoration.underline, decorationThickness: 5.0); + }); + + test('text styles - font weight', () async { + await testTextStyle('font weight', fontWeight: ui.FontWeight.w900); + }); + + test('text styles - font style', () async { + await testTextStyle('font style', fontStyle: ui.FontStyle.italic); + }); + + // TODO(yjbanov): not sure how to test this. + test('text styles - baseline', () async { + await testTextStyle('baseline', + textBaseline: ui.TextBaseline.ideographic); + }); + + test('text styles - font family', () async { + await testTextStyle('font family', fontFamily: 'Ahem'); + }); + + test('text styles - non-existent font family', () async { + await testTextStyle('non-existent font family', + fontFamily: 'DoesNotExist'); + }); + + test('text styles - family fallback', () async { + await testTextStyle('family fallback', + fontFamily: 'DoesNotExist', fontFamilyFallback: ['Ahem']); + }); + + test('text styles - font size', () async { + await testTextStyle('font size', fontSize: 24); + }); + + test('text styles - letter spacing', () async { + await testTextStyle('letter spacing', letterSpacing: 5); + }); + + test('text styles - word spacing', () async { + await testTextStyle('word spacing', + innerText: 'Beautiful World!', wordSpacing: 25); + }); + + test('text styles - height', () async { + await testTextStyle('height', height: 2); + }); + + test('text styles - leading distribution', () async { + await testTextStyle('half leading', + height: 20, + fontSize: 10, + leadingDistribution: ui.TextLeadingDistribution.even); + await testTextStyle( + 'half leading inherited from paragraph', + height: 20, + fontSize: 10, + paragraphTextHeightBehavior: const ui.TextHeightBehavior( + leadingDistribution: ui.TextLeadingDistribution.even, + ), + ); + await testTextStyle( + 'text style half leading overrides paragraph style half leading', + height: 20, + fontSize: 10, + leadingDistribution: ui.TextLeadingDistribution.proportional, + paragraphTextHeightBehavior: const ui.TextHeightBehavior( + leadingDistribution: ui.TextLeadingDistribution.even, + ), + ); + }); + + // TODO(yjbanov): locales specified in text styles don't work: + // https://github.com/flutter/flutter/issues/74687 + // TODO(yjbanov): spaces are not rendered correctly: + // https://github.com/flutter/flutter/issues/74742 + test('text styles - locale zh_CN', () async { + await testTextStyle('locale zh_CN', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('zh', 'CN')); + }, skip: kIssue86432Exists); + + test('text styles - locale zh_TW', () async { + await testTextStyle('locale zh_TW', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('zh', 'TW')); + }, skip: kIssue86432Exists); + + test('text styles - locale ja', () async { + await testTextStyle('locale ja', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('ja')); + }, skip: kIssue86432Exists); + + test('text styles - locale ko', () async { + await testTextStyle('locale ko', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('ko')); + }, skip: kIssue86432Exists); + + test('text styles - background', () async { + await testTextStyle('background', + background: CkPaint()..color = const ui.Color(0xFF00FF00)); + }); + + test('text styles - foreground', () async { + await testTextStyle('foreground', + foreground: CkPaint()..color = const ui.Color(0xFF0000FF)); + }); + + test('text styles - foreground and background', () async { + await testTextStyle( + 'foreground and background', + foreground: CkPaint()..color = const ui.Color(0xFFFF5555), + background: CkPaint()..color = const ui.Color(0xFF007700), + ); + }); + + test('text styles - background and color', () async { + await testTextStyle( + 'background and color', + color: const ui.Color(0xFFFFFF00), + background: CkPaint()..color = const ui.Color(0xFF007700), + ); + }); + + test('text styles - shadows', () async { + await testTextStyle('shadows', shadows: [ + const ui.Shadow( + color: ui.Color(0xFF999900), + offset: ui.Offset(10, 10), + blurRadius: 5, + ), + const ui.Shadow( + color: ui.Color(0xFF009999), + offset: ui.Offset(-10, -10), + blurRadius: 10, + ), + ]); + }); + + test('text styles - old style figures', () async { + await testTextStyle( + 'old style figures', + paragraphFontFamily: 'Roboto', + paragraphFontSize: 24, + outerText: '0 1 2 3 4 5 ', + innerText: '0 1 2 3 4 5', + fontFeatures: [const ui.FontFeature.oldstyleFigures()], + ); + }); + + test('text styles - stylistic set 1', () async { + await testTextStyle( + 'stylistic set 1', + paragraphFontFamily: 'Roboto', + paragraphFontSize: 24, + outerText: 'g', + innerText: 'g', + fontFeatures: [ui.FontFeature.stylisticSet(1)], + ); + }); + + test('text styles - stylistic set 2', () async { + await testTextStyle( + 'stylistic set 2', + paragraphFontFamily: 'Roboto', + paragraphFontSize: 24, + outerText: 'α', + innerText: 'α', + fontFeatures: [ui.FontFeature.stylisticSet(2)], + ); + }); + + test('text styles - override font family', () async { + await testTextStyle( + 'override font family', + paragraphFontFamily: 'Ahem', + fontFamily: 'Roboto', + ); + }); + + test('text styles - override font size', () async { + await testTextStyle( + 'override font size', + paragraphFontSize: 36, + fontSize: 18, + ); + }); + + test('text style - override font weight', () async { + await testTextStyle( + 'override font weight', + paragraphFontWeight: ui.FontWeight.w900, + fontWeight: ui.FontWeight.normal, + ); + }); + + test('text style - override font style', () async { + await testTextStyle( + 'override font style', + paragraphFontStyle: ui.FontStyle.italic, + fontStyle: ui.FontStyle.normal, + ); + }); + + test('text style - characters from multiple fallback fonts', () async { + await testTextStyle( + 'multi-font characters', + // This character is claimed by multiple fonts. This test makes sure + // we can find a font supporting it. + outerText: '欢', + innerText: '', + ); + }, skip: kIssue86432Exists); + + test('text style - symbols', () async { + // One of the CJK fonts loaded in one of the tests above also contains + // some of these symbols. To make sure the test produces predictable + // results we reset the fallback data forcing the engine to reload + // fallbacks, which for this test will only load Noto Symbols. + FontFallbackData.debugReset(); + await testTextStyle( + 'symbols', + outerText: '← ↑ → ↓ ', + innerText: '', + ); + }, skip: kIssue86432Exists); + + test( + 'text style - foreground/background/color do not leak across paragraphs', + () async { + const double testWidth = 440; + const double middle = testWidth / 2; + CkParagraph createTestParagraph( + {ui.Color? color, CkPaint? foreground, CkPaint? background}) { + final CkParagraphBuilder builder = + CkParagraphBuilder(CkParagraphStyle()); + builder.pushStyle(CkTextStyle( + fontSize: 16, + color: color, + foreground: foreground, + background: background, + )); + final StringBuffer text = StringBuffer(); + if (color == null && foreground == null && background == null) { + text.write('Default'); + } else { + if (color != null) { + text.write('Color'); + } + if (foreground != null) { + if (text.isNotEmpty) { + text.write('+'); + } + text.write('Foreground'); + } + if (background != null) { + if (text.isNotEmpty) { + text.write('+'); + } + text.write('Background'); + } + } + builder.addText(text.toString()); + final CkParagraph paragraph = builder.build(); + paragraph.layout(const ui.ParagraphConstraints(width: testWidth)); + return paragraph; + } + + final List variations = [ + () => createTestParagraph(), + () => createTestParagraph(color: const ui.Color(0xFF009900)), + () => createTestParagraph( + foreground: CkPaint()..color = const ui.Color(0xFF990000)), + () => createTestParagraph( + background: CkPaint()..color = const ui.Color(0xFF7777FF)), + () => createTestParagraph( + color: const ui.Color(0xFFFF00FF), + background: CkPaint()..color = const ui.Color(0xFF0000FF), + ), + () => createTestParagraph( + foreground: CkPaint()..color = const ui.Color(0xFF00FFFF), + background: CkPaint()..color = const ui.Color(0xFF0000FF), + ), + ]; + + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + canvas.translate(10, 10); + + for (final ParagraphFactory from in variations) { + for (final ParagraphFactory to in variations) { + canvas.save(); + final CkParagraph fromParagraph = from(); + canvas.drawParagraph(fromParagraph, ui.Offset.zero); + + final ui.Offset leftEnd = ui.Offset( + fromParagraph.maxIntrinsicWidth + 10, fromParagraph.height / 2); + final ui.Offset rightEnd = ui.Offset(middle - 10, leftEnd.dy); + const ui.Offset tipOffset = ui.Offset(-5, -5); + canvas.drawLine(leftEnd, rightEnd, CkPaint()); + canvas.drawLine(rightEnd, rightEnd + tipOffset, CkPaint()); + canvas.drawLine( + rightEnd, rightEnd + tipOffset.scale(1, -1), CkPaint()); + + canvas.translate(middle, 0); + canvas.drawParagraph(to(), ui.Offset.zero); + canvas.restore(); + canvas.translate(0, 22); + } + } + + final CkPicture picture = recorder.endRecording(); + await matchPictureGolden( + 'canvaskit_text_styles_do_not_leak.png', + picture, + region: const ui.Rect.fromLTRB(0, 0, testWidth, 850), + ); + }); + + test('sample Chinese text', () async { + await testSampleText( + 'chinese', + '也称乱数假文或者哑元文本, ' + '是印刷及排版领域所常用的虚拟文字。' + '由于曾经一台匿名的打印机刻意打乱了' + '一盒印刷字体从而造出一本字体样品书', + ); + }); + + test('sample Armenian text', () async { + await testSampleText( + 'armenian', + 'տպագրության և տպագրական արդյունաբերության համար նախատեսված մոդելային տեքստ է', + ); + }); + + test('sample Albanian text', () async { + await testSampleText( + 'albanian', + 'është një tekst shabllon i industrisë së printimit dhe shtypshkronjave Lorem Ipsum ka qenë teksti shabllon', + ); + }); + + test('sample Arabic text', () async { + await testSampleText( + 'arabic', + 'هناك حقيقة مثبتة منذ زمن طويل وهي أن المحتوى المقروء لصفحة ما سيلهي', + textDirection: ui.TextDirection.rtl, + ); + }); + + test('sample Bulgarian text', () async { + await testSampleText( + 'bulgarian', + 'е елементарен примерен текст използван в печатарската и типографската индустрия', + ); + }); + + test('sample Catalan text', () async { + await testSampleText( + 'catalan', + 'és un text de farciment usat per la indústria de la tipografia i la impremta', + ); + }); + + test('sample English text', () async { + await testSampleText( + 'english', + 'Lorem Ipsum is simply dummy text of the printing and typesetting industry', + ); + }); + + test('sample Greek text', () async { + await testSampleText( + 'greek', + 'είναι απλά ένα κείμενο χωρίς νόημα για τους επαγγελματίες της τυπογραφίας και στοιχειοθεσίας', + ); + }); + + test('sample Hebrew text', () async { + await testSampleText( + 'hebrew', + 'זוהי עובדה מבוססת שדעתו של הקורא תהיה מוסחת על ידי טקטס קריא כאשר הוא יביט בפריסתו', + textDirection: ui.TextDirection.rtl, + ); + }); + + test('sample Hindi text', () async { + await testSampleText( + 'hindi', + 'छपाई और अक्षर योजन उद्योग का एक साधारण डमी पाठ है सन १५०० के बाद से अभी तक इस उद्योग का मानक डमी पाठ मन गया जब एक अज्ञात मुद्रक ने नमूना लेकर एक नमूना किताब बनाई', + ); + }); + + test('sample Thai text', () async { + await testSampleText( + 'thai', + 'คือ เนื้อหาจำลองแบบเรียบๆ ที่ใช้กันในธุรกิจงานพิมพ์หรืองานเรียงพิมพ์ มันได้กลายมาเป็นเนื้อหาจำลองมาตรฐานของธุรกิจดังกล่าวมาตั้งแต่ศตวรรษที่', + ); + }); + + test('sample Georgian text', () async { + await testSampleText( + 'georgian', + 'საბეჭდი და ტიპოგრაფიული ინდუსტრიის უშინაარსო ტექსტია. იგი სტანდარტად', + ); + }); + + test('sample Bengali text', () async { + await testSampleText( + 'bengali', + 'ঈদের জামাত মসজিদে, মানতে হবে স্বাস্থ্যবিধি: ধর্ম মন্ত্রণালয়', + ); + }); + + test('hindi svayan test', () async { + await testSampleText('hindi_svayan', 'स्वयं'); + }); + + // We've seen text break when we load many fonts simultaneously. This test + // combines text in multiple languages into one long paragraph to make sure + // we can handle it. + test('sample multilingual text', () async { + await testSampleText( + 'multilingual', + '也称乱数假文或者哑元文本, 是印刷及排版领域所常用的虚拟文字。 ' + 'տպագրության և տպագրական արդյունաբերության համար ' + 'është një tekst shabllon i industrisë së printimit ' + ' زمن طويل وهي أن المحتوى المقروء لصفحة ما سيلهي ' + 'е елементарен примерен текст използван в печатарската ' + 'és un text de farciment usat per la indústria de la ' + 'Lorem Ipsum is simply dummy text of the printing ' + 'είναι απλά ένα κείμενο χωρίς νόημα για τους επαγγελματίες ' + ' זוהי עובדה מבוססת שדעתו של הקורא תהיה מוסחת על ידי טקטס קריא ' + 'छपाई और अक्षर योजन उद्योग का एक साधारण डमी पाठ है सन ' + 'คือ เนื้อหาจำลองแบบเรียบๆ ที่ใช้กันในธุรกิจงานพิมพ์หรืองานเรียงพิมพ์ ' + 'საბეჭდი და ტიპოგრაფიული ინდუსტრიის უშინაარსო ტექსტია ', + ); + }); + + test('emoji text with skin tone', () async { + await testSampleText('emoji_with_skin_tone', '👋🏿 👋🏾 👋🏽 👋🏼 👋🏻'); + }); + + // Make sure we clear the canvas in between frames. + test('empty frame after contentful frame', () async { + // First draw a frame with a red rectangle + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + canvas.drawRect(const ui.Rect.fromLTRB(20, 20, 100, 100), + CkPaint()..color = const ui.Color(0xffff0000)); + final CkPicture picture = recorder.endRecording(); + final LayerSceneBuilder builder = LayerSceneBuilder(); + builder.pushOffset(0, 0); + builder.addPicture(ui.Offset.zero, picture); + final LayerTree layerTree = builder.build().layerTree; + EnginePlatformDispatcher.instance.rasterizer!.draw(layerTree); + + // Now draw an empty layer tree and confirm that the red rectangle is + // no longer drawn. + final LayerSceneBuilder emptySceneBuilder = LayerSceneBuilder(); + emptySceneBuilder.pushOffset(0, 0); + final LayerTree emptyLayerTree = emptySceneBuilder.build().layerTree; + EnginePlatformDispatcher.instance.rasterizer!.draw(emptyLayerTree); + + await matchGoldenFile('canvaskit_empty_scene.png', + region: const ui.Rect.fromLTRB(0, 0, 100, 100)); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} + +Future testSampleText(String language, String text, + {ui.TextDirection textDirection = ui.TextDirection.ltr, + bool write = false}) async { + FontFallbackData.debugReset(); + const double testWidth = 300; + double paragraphHeight = 0; + final CkPicture picture = await generatePictureWhenFontsStable(() { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + final CkParagraphBuilder paragraphBuilder = + CkParagraphBuilder(CkParagraphStyle( + textDirection: textDirection, + )); + paragraphBuilder.addText(text); + final CkParagraph paragraph = paragraphBuilder.build(); + paragraph.layout(const ui.ParagraphConstraints(width: testWidth - 20)); + canvas.drawParagraph(paragraph, const ui.Offset(10, 10)); + paragraphHeight = paragraph.height; + return recorder.endRecording(); + }); + if (!kIssue86432Exists) { + await matchPictureGolden( + 'canvaskit_sample_text_$language.png', + picture, + region: ui.Rect.fromLTRB(0, 0, testWidth, paragraphHeight + 20), + write: write, + ); + } +} + +typedef ParagraphFactory = CkParagraph Function(); + +void drawTestPicture(CkCanvas canvas) { + canvas.clear(const ui.Color(0xFFFFFFF)); + + canvas.translate(10, 10); + + // Row 1 + canvas.save(); + + canvas.save(); + canvas.clipRect( + const ui.Rect.fromLTRB(0, 0, 45, 45), + ui.ClipOp.intersect, + true, + ); + canvas.clipRRect( + ui.RRect.fromLTRBR(5, 5, 50, 50, const ui.Radius.circular(8)), + true, + ); + canvas.clipPath( + CkPath() + ..moveTo(5, 5) + ..lineTo(25, 5) + ..lineTo(45, 45) + ..lineTo(5, 45) + ..close(), + true, + ); + canvas.drawColor(const ui.Color.fromARGB(255, 100, 100, 0), ui.BlendMode.srcOver); + canvas.restore(); // remove clips + + canvas.translate(60, 0); + canvas.drawCircle( + const ui.Offset(30, 25), + 15, + CkPaint()..color = const ui.Color(0xFF0000AA), + ); + + canvas.translate(60, 0); + canvas.drawArc( + const ui.Rect.fromLTRB(10, 20, 50, 40), + math.pi / 4, + 3 * math.pi / 2, + true, + CkPaint()..color = const ui.Color(0xFF00AA00), + ); + + canvas.translate(60, 0); + canvas.drawImage( + generateTestImage(), + const ui.Offset(20, 20), + CkPaint(), + ); + + canvas.translate(60, 0); + final ui.RSTransform transform = ui.RSTransform.fromComponents( + rotation: 0, + scale: 1, + anchorX: 0, + anchorY: 0, + translateX: 0, + translateY: 0, + ); + canvas.drawAtlasRaw( + CkPaint(), + generateTestImage(), + Float32List(4) + ..[0] = transform.scos + ..[1] = transform.ssin + ..[2] = transform.tx + 20 + ..[3] = transform.ty + 20, + Float32List(4) + ..[0] = 0 + ..[1] = 0 + ..[2] = 15 + ..[3] = 15, + Uint32List.fromList([0x00000000]), + ui.BlendMode.srcOver, + ); + + canvas.translate(60, 0); + canvas.drawDRRect( + ui.RRect.fromLTRBR(0, 0, 40, 30, const ui.Radius.elliptical(16, 8)), + ui.RRect.fromLTRBR(10, 10, 30, 20, const ui.Radius.elliptical(4, 8)), + CkPaint(), + ); + + canvas.translate(60, 0); + canvas.drawImageRect( + generateTestImage(), + const ui.Rect.fromLTRB(0, 0, 15, 15), + const ui.Rect.fromLTRB(10, 10, 40, 40), + CkPaint(), + ); + + canvas.translate(60, 0); + canvas.drawImageNine( + generateTestImage(), + const ui.Rect.fromLTRB(5, 5, 15, 15), + const ui.Rect.fromLTRB(10, 10, 50, 40), + CkPaint(), + ); + + canvas.restore(); + + // Row 2 + canvas.translate(0, 60); + canvas.save(); + + canvas.drawLine(const ui.Offset(0, 0), const ui.Offset(40, 30), CkPaint()); + + canvas.translate(60, 0); + canvas.drawOval( + const ui.Rect.fromLTRB(0, 0, 40, 30), + CkPaint(), + ); + + canvas.translate(60, 0); + canvas.save(); + canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 50, 30), ui.ClipOp.intersect, true); + canvas.drawPaint(CkPaint()..color = const ui.Color(0xFF6688AA)); + canvas.restore(); + + canvas.translate(60, 0); + { + final CkPictureRecorder otherRecorder = CkPictureRecorder(); + final CkCanvas otherCanvas = + otherRecorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 40, 20)); + otherCanvas.drawCircle( + const ui.Offset(30, 15), + 10, + CkPaint()..color = const ui.Color(0xFFAABBCC), + ); + canvas.drawPicture(otherRecorder.endRecording()); + } + + canvas.translate(60, 0); + // TODO(yjbanov): CanvasKit.drawPoints is currently broken + // https://github.com/flutter/flutter/issues/71489 + // But keeping this anyway as it's a good test-case that + // will ensure it's fixed when we have the fix. + canvas.drawPoints( + CkPaint() + ..color = const ui.Color(0xFF0000FF) + ..strokeWidth = 5 + ..strokeCap = ui.StrokeCap.round, + ui.PointMode.polygon, + offsetListToFloat32List(const [ + ui.Offset(10, 10), + ui.Offset(20, 10), + ui.Offset(30, 20), + ui.Offset(40, 20) + ]), + ); + + canvas.translate(60, 0); + canvas.drawRRect( + ui.RRect.fromLTRBR(0, 0, 40, 30, const ui.Radius.circular(10)), + CkPaint(), + ); + + canvas.translate(60, 0); + canvas.drawRect( + const ui.Rect.fromLTRB(0, 0, 40, 30), + CkPaint(), + ); + + canvas.translate(60, 0); + canvas.drawShadow( + CkPath()..addRect(const ui.Rect.fromLTRB(0, 0, 40, 30)), + const ui.Color(0xFF00FF00), + 4, + true, + ); + + canvas.restore(); + + // Row 3 + canvas.translate(0, 60); + canvas.save(); + + canvas.drawVertices( + CkVertices( + ui.VertexMode.triangleFan, + const [ + ui.Offset(10, 30), + ui.Offset(30, 50), + ui.Offset(10, 60), + ], + ), + ui.BlendMode.srcOver, + CkPaint(), + ); + + canvas.translate(60, 0); + final int restorePoint = canvas.save(); + for (int i = 0; i < 5; i++) { + canvas.save(); + canvas.translate(10, 10); + canvas.drawCircle(ui.Offset.zero, 5, CkPaint()); + } + canvas.restoreToCount(restorePoint); + canvas.drawCircle(ui.Offset.zero, 7, CkPaint()..color = const ui.Color(0xFFFF0000)); + + canvas.translate(60, 0); + canvas.drawLine(ui.Offset.zero, const ui.Offset(30, 30), CkPaint()); + canvas.save(); + canvas.rotate(-math.pi / 8); + canvas.drawLine(ui.Offset.zero, const ui.Offset(30, 30), CkPaint()); + canvas.drawCircle( + const ui.Offset(30, 30), 7, CkPaint()..color = const ui.Color(0xFF00AA00)); + canvas.restore(); + + canvas.translate(60, 0); + final CkPaint thickStroke = CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 20; + final CkPaint semitransparent = CkPaint()..color = const ui.Color(0x66000000); + + canvas.saveLayer(kDefaultRegion, semitransparent); + canvas.drawLine(const ui.Offset(10, 10), const ui.Offset(50, 50), thickStroke); + canvas.drawLine(const ui.Offset(50, 10), const ui.Offset(10, 50), thickStroke); + canvas.restore(); + + canvas.translate(60, 0); + canvas.saveLayerWithoutBounds(semitransparent); + canvas.drawLine(const ui.Offset(10, 10), const ui.Offset(50, 50), thickStroke); + canvas.drawLine(const ui.Offset(50, 10), const ui.Offset(10, 50), thickStroke); + canvas.restore(); + + // To test saveLayerWithFilter we draw three circles with only the middle one + // blurred using the layer image filter. + canvas.translate(60, 0); + canvas.saveLayer(kDefaultRegion, CkPaint()); + canvas.drawCircle(const ui.Offset(30, 30), 10, CkPaint()); + { + canvas.saveLayerWithFilter( + kDefaultRegion, ui.ImageFilter.blur(sigmaX: 5, sigmaY: 10)); + canvas.drawCircle(const ui.Offset(10, 10), 10, CkPaint()); + canvas.drawCircle(const ui.Offset(50, 50), 10, CkPaint()); + canvas.restore(); + } + canvas.restore(); + + canvas.translate(60, 0); + canvas.save(); + canvas.translate(30, 30); + canvas.scale(2, 1.5); + canvas.drawCircle(ui.Offset.zero, 10, CkPaint()); + canvas.restore(); + + canvas.translate(60, 0); + canvas.save(); + canvas.translate(30, 30); + canvas.skew(2, 1.5); + canvas.drawRect(const ui.Rect.fromLTRB(-10, -10, 10, 10), CkPaint()); + canvas.restore(); + + canvas.restore(); + + // Row 4 + canvas.translate(0, 60); + canvas.save(); + + canvas.save(); + final Matrix4 matrix = Matrix4.identity(); + matrix.translate(30, 30); + matrix.scale(2, 1.5); + canvas.transform(matrix.storage); + canvas.drawCircle(ui.Offset.zero, 10, CkPaint()); + canvas.restore(); + + canvas.translate(60, 0); + final CkParagraphBuilder pb = CkParagraphBuilder(CkParagraphStyle( + fontFamily: 'Roboto', + fontStyle: ui.FontStyle.normal, + fontWeight: ui.FontWeight.normal, + fontSize: 18, + )); + pb.pushStyle(CkTextStyle( + color: const ui.Color(0xFF0000AA), + )); + pb.addText('Hello'); + pb.pop(); + final CkParagraph p = pb.build(); + p.layout(const ui.ParagraphConstraints(width: 1000)); + canvas.drawParagraph( + p, + const ui.Offset(10, 20), + ); + + canvas.translate(60, 0); + canvas.drawPath( + CkPath() + ..moveTo(30, 20) + ..lineTo(50, 50) + ..lineTo(10, 50) + ..close(), + CkPaint()..color = const ui.Color(0xFF0000AA), + ); + + canvas.restore(); +} + +CkImage generateTestImage() { + final html.CanvasElement canvas = html.CanvasElement() + ..width = 20 + ..height = 20; + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#FF0000'; + ctx.fillRect(0, 0, 10, 10); + ctx.fillStyle = '#00FF00'; + ctx.fillRect(0, 10, 10, 10); + ctx.fillStyle = '#0000FF'; + ctx.fillRect(10, 0, 10, 10); + ctx.fillStyle = '#FF00FF'; + ctx.fillRect(10, 10, 10, 10); + final Uint8List imageData = + ctx.getImageData(0, 0, 20, 20).data.buffer.asUint8List(); + final SkImage skImage = canvasKit.MakeImage( + SkImageInfo( + width: 20, + height: 20, + alphaType: canvasKit.AlphaType.Premul, + colorType: canvasKit.ColorType.RGBA_8888, + colorSpace: SkColorSpaceSRGB, + ), + imageData, + 4 * 20); + return CkImage(skImage); +} + +/// A convenience function for testing paragraph and text styles. +/// +/// Renders a paragraph with two pieces of text, [outerText] and [innerText]. +/// [outerText] is added to the root of the paragraph where only paragraph +/// style applies. [innerText] is added under a text style with properties +/// set from the arguments to this method. Parameters with prefix "paragraph" +/// are applied to the paragraph style. Others are applied to the text style. +/// +/// [name] is the name of the test used as the description on the golden as +/// well as in the golden file name. Avoid special characters. Spaces are OK; +/// they are replaced by "_" in the file name. +/// +/// Set [write] to true to overwrite the golden file. +/// +/// Use [layoutWidth] to customize the width of the paragraph constraints. +Future testTextStyle( + // Test properties + String name, { + bool write = false, + double? layoutWidth, + // Top-level text where only paragraph style applies + String outerText = 'Hello ', + // Second-level text where paragraph and text styles both apply. + String innerText = 'World!', + + // ParagraphStyle properties + ui.TextAlign? paragraphTextAlign, + ui.TextDirection? paragraphTextDirection, + int? paragraphMaxLines, + String? paragraphFontFamily, + double? paragraphFontSize, + double? paragraphHeight, + ui.TextHeightBehavior? paragraphTextHeightBehavior, + ui.FontWeight? paragraphFontWeight, + ui.FontStyle? paragraphFontStyle, + ui.StrutStyle? paragraphStrutStyle, + String? paragraphEllipsis, + ui.Locale? paragraphLocale, + + // TextStyle properties + ui.Color? color, + ui.TextDecoration? decoration, + ui.Color? decorationColor, + ui.TextDecorationStyle? decorationStyle, + double? decorationThickness, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.TextBaseline? textBaseline, + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + ui.Locale? locale, + CkPaint? background, + CkPaint? foreground, + List? shadows, + List? fontFeatures, +}) async { + late ui.Rect region; + CkPicture renderPicture() { + const double testWidth = 512; + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + canvas.translate(30, 10); + final CkParagraphBuilder descriptionBuilder = + CkParagraphBuilder(CkParagraphStyle()); + descriptionBuilder.addText(name); + final CkParagraph descriptionParagraph = descriptionBuilder.build(); + descriptionParagraph + .layout(const ui.ParagraphConstraints(width: testWidth / 2 - 70)); + const ui.Offset descriptionOffset = ui.Offset(testWidth / 2 + 30, 0); + canvas.drawParagraph(descriptionParagraph, descriptionOffset); + + final CkParagraphBuilder pb = CkParagraphBuilder(CkParagraphStyle( + textAlign: paragraphTextAlign, + textDirection: paragraphTextDirection, + maxLines: paragraphMaxLines, + fontFamily: paragraphFontFamily, + fontSize: paragraphFontSize, + height: paragraphHeight, + textHeightBehavior: paragraphTextHeightBehavior, + fontWeight: paragraphFontWeight, + fontStyle: paragraphFontStyle, + strutStyle: paragraphStrutStyle, + ellipsis: paragraphEllipsis, + locale: paragraphLocale, + )); + + pb.addText(outerText); + + pb.pushStyle(CkTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + leadingDistribution: leadingDistribution, + locale: locale, + background: background, + foreground: foreground, + shadows: shadows, + fontFeatures: fontFeatures, + )); + pb.addText(innerText); + pb.pop(); + final CkParagraph p = pb.build(); + p.layout(ui.ParagraphConstraints(width: layoutWidth ?? testWidth / 2)); + canvas.drawParagraph(p, ui.Offset.zero); + + canvas.drawPath( + CkPath() + ..moveTo(-10, 0) + ..lineTo(-20, 0) + ..lineTo(-20, p.height) + ..lineTo(-10, p.height), + CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 1.0, + ); + canvas.drawPath( + CkPath() + ..moveTo(testWidth / 2 + 10, 0) + ..lineTo(testWidth / 2 + 20, 0) + ..lineTo(testWidth / 2 + 20, p.height) + ..lineTo(testWidth / 2 + 10, p.height), + CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 1.0, + ); + const double padding = 20; + region = ui.Rect.fromLTRB( + 0, + 0, + testWidth, + math.max( + descriptionOffset.dy + descriptionParagraph.height + padding, + p.height + padding, + ), + ); + return recorder.endRecording(); + } + + // Render once to trigger font downloads. + final CkPicture picture = await generatePictureWhenFontsStable(renderPicture); + await matchPictureGolden( + 'canvaskit_text_styles_${name.replaceAll(' ', '_')}.png', + picture, + region: region, + write: write, + ); + expect(notoDownloadQueue.debugIsLoadingFonts, isFalse); + expect(notoDownloadQueue.pendingSubsets, isEmpty); + expect(notoDownloadQueue.downloader.debugActiveDownloadCount, 0); +} + +typedef PictureGenerator = CkPicture Function(); + +Future generatePictureWhenFontsStable( + PictureGenerator generator) async { + CkPicture picture = generator(); + // Fallback fonts start downloading as a post-frame callback. + EnginePlatformDispatcher.instance.rasterizer!.debugRunPostFrameCallbacks(); + // Font downloading begins asynchronously so we inject a timer before checking the download queue. + await Future.delayed(Duration.zero); + while (notoDownloadQueue.isPending || + notoDownloadQueue.downloader.debugActiveDownloadCount > 0) { + await notoDownloadQueue.debugWhenIdle(); + await notoDownloadQueue.downloader.debugWhenIdle(); + picture = generator(); + EnginePlatformDispatcher.instance.rasterizer!.debugRunPostFrameCallbacks(); + // Dummy timer for the same reason as above. + await Future.delayed(Duration.zero); + } + return picture; +} diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index b03f3526db149..55cda12acdf92 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; @@ -11,6 +10,7 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../matchers.dart'; import 'common.dart'; import 'test_data.dart'; @@ -20,13 +20,7 @@ void main() { void testMain() { group('CanvasKit API', () { - setUpAll(() async { - await ui.webOnlyInitializePlatform(); - }); - - test('Using CanvasKit', () { - expect(experimentalUseSkia, true); - }); + setUpCanvasKitTest(); _blendModeTests(); _paintStyleTests(); @@ -51,15 +45,19 @@ void testMain() { _toSkPointTests(); _toSkColorStopsTests(); _toSkMatrixFromFloat32Tests(); - _skSkRectTests(); + _toSkRectTests(); _skVerticesTests(); + _paragraphTests(); group('SkPath', () { _pathTests(); }); group('SkCanvas', () { _canvasTests(); }); - // TODO: https://github.com/flutter/flutter/issues/60040 + group('SkParagraph', () { + _textStyleTests(); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } @@ -97,7 +95,7 @@ void _blendModeTests() { }); test('ui.BlendMode converts to SkBlendMode', () { - for (ui.BlendMode blendMode in ui.BlendMode.values) { + for (final ui.BlendMode blendMode in ui.BlendMode.values) { expect(toSkBlendMode(blendMode).value, blendMode.index); } }); @@ -110,7 +108,7 @@ void _paintStyleTests() { }); test('ui.PaintingStyle converts to SkPaintStyle', () { - for (ui.PaintingStyle style in ui.PaintingStyle.values) { + for (final ui.PaintingStyle style in ui.PaintingStyle.values) { expect(toSkPaintStyle(style).value, style.index); } }); @@ -124,7 +122,7 @@ void _strokeCapTests() { }); test('ui.StrokeCap converts to SkStrokeCap', () { - for (ui.StrokeCap cap in ui.StrokeCap.values) { + for (final ui.StrokeCap cap in ui.StrokeCap.values) { expect(toSkStrokeCap(cap).value, cap.index); } }); @@ -138,7 +136,7 @@ void _strokeJoinTests() { }); test('ui.StrokeJoin converts to SkStrokeJoin', () { - for (ui.StrokeJoin join in ui.StrokeJoin.values) { + for (final ui.StrokeJoin join in ui.StrokeJoin.values) { expect(toSkStrokeJoin(join).value, join.index); } }); @@ -153,7 +151,7 @@ void _filterQualityTests() { }); test('ui.FilterQuality converts to SkFilterQuality', () { - for (ui.FilterQuality cap in ui.FilterQuality.values) { + for (final ui.FilterQuality cap in ui.FilterQuality.values) { expect(toSkFilterQuality(cap).value, cap.index); } }); @@ -168,7 +166,7 @@ void _blurStyleTests() { }); test('ui.BlurStyle converts to SkBlurStyle', () { - for (ui.BlurStyle style in ui.BlurStyle.values) { + for (final ui.BlurStyle style in ui.BlurStyle.values) { expect(toSkBlurStyle(style).value, style.index); } }); @@ -182,7 +180,7 @@ void _tileModeTests() { }); test('ui.TileMode converts to SkTileMode', () { - for (ui.TileMode mode in ui.TileMode.values) { + for (final ui.TileMode mode in ui.TileMode.values) { expect(toSkTileMode(mode).value, mode.index); } }); @@ -195,7 +193,7 @@ void _fillTypeTests() { }); test('ui.PathFillType converts to SkFillType', () { - for (ui.PathFillType type in ui.PathFillType.values) { + for (final ui.PathFillType type in ui.PathFillType.values) { expect(toSkFillType(type).value, type.index); } }); @@ -203,15 +201,17 @@ void _fillTypeTests() { void _pathOpTests() { test('path op mapping is correct', () { - expect(canvasKit.PathOp.Difference.value, ui.PathOperation.difference.index); + expect( + canvasKit.PathOp.Difference.value, ui.PathOperation.difference.index); expect(canvasKit.PathOp.Intersect.value, ui.PathOperation.intersect.index); expect(canvasKit.PathOp.Union.value, ui.PathOperation.union.index); expect(canvasKit.PathOp.XOR.value, ui.PathOperation.xor.index); - expect(canvasKit.PathOp.ReverseDifference.value, ui.PathOperation.reverseDifference.index); + expect(canvasKit.PathOp.ReverseDifference.value, + ui.PathOperation.reverseDifference.index); }); test('ui.PathOperation converts to SkPathOp', () { - for (ui.PathOperation op in ui.PathOperation.values) { + for (final ui.PathOperation op in ui.PathOperation.values) { expect(toSkPathOp(op).value, op.index); } }); @@ -219,13 +219,13 @@ void _pathOpTests() { test('Path.combine test', () { final ui.Path path1 = ui.Path(); expect(path1, isA()); - path1.addRect(ui.Rect.fromLTRB(0, 0, 10, 10)); - path1.addOval(ui.Rect.fromLTRB(10, 10, 100, 100)); + path1.addRect(const ui.Rect.fromLTRB(0, 0, 10, 10)); + path1.addOval(const ui.Rect.fromLTRB(10, 10, 100, 100)); final ui.Path path2 = ui.Path(); expect(path2, isA()); - path2.addRect(ui.Rect.fromLTRB(5, 5, 15, 15)); - path2.addOval(ui.Rect.fromLTRB(15, 15, 105, 105)); + path2.addRect(const ui.Rect.fromLTRB(5, 5, 15, 15)); + path2.addOval(const ui.Rect.fromLTRB(15, 15, 105, 105)); final ui.Path union = ui.Path.combine(ui.PathOperation.union, path1, path2); expect(union, isA()); @@ -246,7 +246,7 @@ void _clipOpTests() { }); test('ui.ClipOp converts to SkClipOp', () { - for (ui.ClipOp op in ui.ClipOp.values) { + for (final ui.ClipOp op in ui.ClipOp.values) { expect(toSkClipOp(op).value, op.index); } }); @@ -260,7 +260,7 @@ void _pointModeTests() { }); test('ui.PointMode converts to SkPointMode', () { - for (ui.PointMode op in ui.PointMode.values) { + for (final ui.PointMode op in ui.PointMode.values) { expect(toSkPointMode(op).value, op.index); } }); @@ -269,12 +269,14 @@ void _pointModeTests() { void _vertexModeTests() { test('vertex mode mapping is correct', () { expect(canvasKit.VertexMode.Triangles.value, ui.VertexMode.triangles.index); - expect(canvasKit.VertexMode.TrianglesStrip.value, ui.VertexMode.triangleStrip.index); - expect(canvasKit.VertexMode.TriangleFan.value, ui.VertexMode.triangleFan.index); + expect(canvasKit.VertexMode.TrianglesStrip.value, + ui.VertexMode.triangleStrip.index); + expect(canvasKit.VertexMode.TriangleFan.value, + ui.VertexMode.triangleFan.index); }); test('ui.VertexMode converts to SkVertexMode', () { - for (ui.VertexMode op in ui.VertexMode.values) { + for (final ui.VertexMode op in ui.VertexMode.values) { expect(toSkVertexMode(op).value, op.index); } }); @@ -282,21 +284,24 @@ void _vertexModeTests() { void _imageTests() { test('MakeAnimatedImageFromEncoded makes a non-animated image', () { - final SkAnimatedImage nonAnimated = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage nonAnimated = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; expect(nonAnimated.getFrameCount(), 1); expect(nonAnimated.getRepetitionCount(), 0); expect(nonAnimated.width(), 1); expect(nonAnimated.height(), 1); - final SkImage frame = nonAnimated.getCurrentFrame(); + final SkImage frame = nonAnimated.makeImageAtCurrentFrame(); expect(frame.width(), 1); expect(frame.height(), 1); expect(nonAnimated.decodeNextFrame(), -1); expect( - frame.makeShader( + frame.makeShaderOptions( canvasKit.TileMode.Repeat, canvasKit.TileMode.Mirror, + canvasKit.FilterMode.Linear, + canvasKit.MipmapMode.Nearest, toSkMatrixFromFloat32(Matrix4.identity().storage), ), isNotNull, @@ -304,13 +309,14 @@ void _imageTests() { }); test('MakeAnimatedImageFromEncoded makes an animated image', () { - final SkAnimatedImage animated = canvasKit.MakeAnimatedImageFromEncoded(kAnimatedGif); + final SkAnimatedImage animated = + canvasKit.MakeAnimatedImageFromEncoded(kAnimatedGif)!; expect(animated.getFrameCount(), 3); - expect(animated.getRepetitionCount(), -1); // animates forever + expect(animated.getRepetitionCount(), -1); // animates forever expect(animated.width(), 1); expect(animated.height(), 1); for (int i = 0; i < 100; i++) { - final SkImage frame = animated.getCurrentFrame(); + final SkImage frame = animated.makeImageAtCurrentFrame(); expect(frame.width(), 1); expect(frame.height(), 1); expect(animated.decodeNextFrame(), 100); @@ -324,47 +330,44 @@ void _shaderTests() { }); test('MakeRadialGradient', () { - expect(canvasKit.SkShader.MakeRadialGradient( - Float32List.fromList([1, 1]), - 10.0, - [ - Float32List.fromList([0, 0, 0, 1]), - Float32List.fromList([1, 1, 1, 1]), - ], - Float32List.fromList([0, 1]), - canvasKit.TileMode.Repeat, - toSkMatrixFromFloat32(Matrix4.identity().storage), - 0, - ), isNotNull); + expect( + canvasKit.Shader.MakeRadialGradient( + Float32List.fromList([1, 1]), + 10.0, + Uint32List.fromList([0xff000000, 0xffffffff]), + Float32List.fromList([0, 1]), + canvasKit.TileMode.Repeat, + toSkMatrixFromFloat32(Matrix4.identity().storage), + 0, + ), + isNotNull); }); test('MakeTwoPointConicalGradient', () { - expect(canvasKit.SkShader.MakeTwoPointConicalGradient( - Float32List.fromList([1, 1]), - 10.0, - Float32List.fromList([1, 1]), - 10.0, - [ - Float32List.fromList([0, 0, 0, 1]), - Float32List.fromList([1, 1, 1, 1]), - ], - Float32List.fromList([0, 1]), - canvasKit.TileMode.Repeat, - toSkMatrixFromFloat32(Matrix4.identity().storage), - 0, - ), isNotNull); + expect( + canvasKit.Shader.MakeTwoPointConicalGradient( + Float32List.fromList([1, 1]), + 10.0, + Float32List.fromList([1, 1]), + 10.0, + Uint32List.fromList([0xff000000, 0xffffffff]), + Float32List.fromList([0, 1]), + canvasKit.TileMode.Repeat, + toSkMatrixFromFloat32(Matrix4.identity().storage), + 0, + ), + isNotNull); }); } SkShader _makeTestShader() { - return canvasKit.SkShader.MakeLinearGradient( - Float32List.fromList([0, 0]), - Float32List.fromList([1, 1]), - [ - Float32List.fromList([255, 0, 0, 255]), - ], - Float32List.fromList([0, 1]), + return canvasKit.Shader.MakeLinearGradient( + Float32List.fromList([0, 0]), + Float32List.fromList([1, 1]), + Uint32List.fromList([0xff0000ff]), + Float32List.fromList([0, 1]), canvasKit.TileMode.Repeat, + null, ); } @@ -379,15 +382,14 @@ void _paintTests() { paint.setAntiAlias(true); paint.setColorInt(0x00FFCCAA); paint.setShader(_makeTestShader()); - paint.setMaskFilter(canvasKit.MakeBlurMaskFilter( + paint.setMaskFilter(canvasKit.MaskFilter.MakeBlur( canvasKit.BlurStyle.Outer, 2.0, true, )); - paint.setFilterQuality(canvasKit.FilterQuality.High); - paint.setColorFilter(canvasKit.SkColorFilter.MakeLinearToSRGBGamma()); + paint.setColorFilter(canvasKit.ColorFilter.MakeLinearToSRGBGamma()); paint.setStrokeMiter(1.4); - paint.setImageFilter(canvasKit.SkImageFilter.MakeBlur( + paint.setImageFilter(canvasKit.ImageFilter.MakeBlur( 1, 2, canvasKit.TileMode.Repeat, @@ -397,20 +399,32 @@ void _paintTests() { } void _maskFilterTests() { - test('MakeBlurMaskFilter', () { - expect(canvasKit.MakeBlurMaskFilter( - canvasKit.BlurStyle.Outer, - 5.0, - false, - ), isNotNull); + test('MaskFilter.MakeBlur', () { + expect( + canvasKit.MaskFilter.MakeBlur( + canvasKit.BlurStyle.Outer, + 5.0, + false, + ), + isNotNull); + }); + test('MaskFilter.MakeBlur with 0 sigma returns null', () { + expect( + canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Normal, 0.0, false), + isNull); + }); + test('MaskFilter.MakeBlur with NaN sigma returns null', () { + expect( + canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Normal, double.nan, false), + isNull); }); } void _colorFilterTests() { test('MakeBlend', () { expect( - canvasKit.SkColorFilter.MakeBlend( - Float32List.fromList([0, 0, 0, 1]), + canvasKit.ColorFilter.MakeBlend( + Float32List.fromList([0, 0, 0, 1]), canvasKit.BlendMode.SrcATop, ), isNotNull, @@ -419,7 +433,7 @@ void _colorFilterTests() { test('MakeMatrix', () { expect( - canvasKit.SkColorFilter.MakeMatrix( + canvasKit.ColorFilter.MakeMatrix( Float32List(20), ), isNotNull, @@ -428,14 +442,14 @@ void _colorFilterTests() { test('MakeSRGBToLinearGamma', () { expect( - canvasKit.SkColorFilter.MakeSRGBToLinearGamma(), + canvasKit.ColorFilter.MakeSRGBToLinearGamma(), isNotNull, ); }); test('MakeLinearToSRGBGamma', () { expect( - canvasKit.SkColorFilter.MakeLinearToSRGBGamma(), + canvasKit.ColorFilter.MakeLinearToSRGBGamma(), isNotNull, ); }); @@ -444,14 +458,14 @@ void _colorFilterTests() { void _imageFilterTests() { test('MakeBlur', () { expect( - canvasKit.SkImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), + canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), isNotNull, ); }); test('MakeMatrixTransform', () { expect( - canvasKit.SkImageFilter.MakeMatrixTransform( + canvasKit.ImageFilter.MakeMatrixTransform( toSkMatrixFromFloat32(Matrix4.identity().storage), canvasKit.FilterQuality.Medium, null, @@ -459,6 +473,26 @@ void _imageFilterTests() { isNotNull, ); }); + + test('MakeColorFilter', () { + expect( + canvasKit.ImageFilter.MakeColorFilter( + canvasKit.ColorFilter.MakeLinearToSRGBGamma(), + null, + ), + isNotNull, + ); + }); + + test('MakeCompose', () { + expect( + canvasKit.ImageFilter.MakeCompose( + canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), + canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), + ), + isNotNull, + ); + }); } void _mallocTests() { @@ -543,18 +577,44 @@ void _toSkMatrixFromFloat32Tests() { ..translate(1, 2, 3) ..rotateZ(4); expect( - toSkMatrixFromFloat32(matrix.storage), - Float32List.fromList([ - -0.6536436080932617, - 0.756802499294281, + toSkMatrixFromFloat32(matrix.storage), + Float32List.fromList([ + -0.6536436080932617, + 0.756802499294281, + 1, + -0.756802499294281, + -0.6536436080932617, + 2, + -0.0, + 0, + 1, + ])); + }); +} + +void _toSkRectTests() { + test('toSkRect', () { + expect(toSkRect(const ui.Rect.fromLTRB(1, 2, 3, 4)), [1, 2, 3, 4]); + }); + + test('fromSkRect', () { + expect(fromSkRect(Float32List.fromList([1, 2, 3, 4])), + const ui.Rect.fromLTRB(1, 2, 3, 4)); + }); + + test('toSkRRect', () { + expect( + toSkRRect(ui.RRect.fromLTRBAndCorners( 1, - -0.756802499294281, - -0.6536436080932617, 2, - -0.0, - 0, - 1, - ]) + 3, + 4, + topLeft: const ui.Radius.elliptical(5, 6), + topRight: const ui.Radius.elliptical(7, 8), + bottomRight: const ui.Radius.elliptical(9, 10), + bottomLeft: const ui.Radius.elliptical(11, 12), + )), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ); }); } @@ -569,7 +629,7 @@ SkPath _testClosedSkPath() { } void _pathTests() { - SkPath path; + late SkPath path; setUp(() { path = SkPath(); @@ -581,7 +641,7 @@ void _pathTests() { test('addArc', () { path.addArc( - SkRect(fLeft: 10, fTop: 20, fRight: 30, fBottom: 40), + toSkRect(const ui.Rect.fromLTRB(10, 20, 30, 40)), 1, 5, ); @@ -589,7 +649,7 @@ void _pathTests() { test('addOval', () { path.addOval( - SkRect(fLeft: 10, fTop: 20, fRight: 30, fBottom: 40), + toSkRect(const ui.Rect.fromLTRB(10, 20, 30, 40)), false, 1, ); @@ -608,36 +668,24 @@ void _pathTests() { freeFloat32List(encodedPoints); }); - test('addRoundRect', () { + test('addRRect', () { final ui.RRect rrect = ui.RRect.fromRectAndRadius( - ui.Rect.fromLTRB(10, 10, 20, 20), - ui.Radius.circular(3), + const ui.Rect.fromLTRB(10, 10, 20, 20), + const ui.Radius.circular(3), ); - final SkFloat32List skRadii = mallocFloat32List(8); - final Float32List radii = skRadii.toTypedArray(); - radii[0] = rrect.tlRadiusX; - radii[1] = rrect.tlRadiusY; - radii[2] = rrect.trRadiusX; - radii[3] = rrect.trRadiusY; - radii[4] = rrect.brRadiusX; - radii[5] = rrect.brRadiusY; - radii[6] = rrect.blRadiusX; - radii[7] = rrect.blRadiusY; - path.addRoundRect( - toOuterSkRect(rrect), - radii, + path.addRRect( + toSkRRect(rrect), false, ); - freeFloat32List(skRadii); }); test('addRect', () { - path.addRect(SkRect(fLeft: 1, fTop: 2, fRight: 3, fBottom: 4)); + path.addRect(toSkRect(const ui.Rect.fromLTRB(1, 2, 3, 4))); }); test('arcTo', () { path.arcToOval( - SkRect(fLeft: 1, fTop: 2, fRight: 3, fBottom: 4), + toSkRect(const ui.Rect.fromLTRB(1, 2, 3, 4)), 5, 40, false, @@ -666,8 +714,8 @@ void _pathTests() { test('contains', () { final SkPath testPath = _testClosedSkPath(); - expect(testPath.contains(15, 15), true); - expect(testPath.contains(100, 100), false); + expect(testPath.contains(15, 15), isTrue); + expect(testPath.contains(100, 100), isFalse); }); test('cubicTo', () { @@ -676,7 +724,7 @@ void _pathTests() { test('getBounds', () { final SkPath testPath = _testClosedSkPath(); - final ui.Rect bounds = testPath.getBounds().toRect(); + final ui.Rect bounds = fromSkRect(testPath.getBounds()); expect(bounds, const ui.Rect.fromLTRB(10, 10, 20, 20)); }); @@ -726,40 +774,44 @@ void _pathTests() { test('reset', () { final SkPath testPath = _testClosedSkPath(); - expect(testPath.getBounds().toRect(), const ui.Rect.fromLTRB(10, 10, 20, 20)); + expect(fromSkRect(testPath.getBounds()), + const ui.Rect.fromLTRB(10, 10, 20, 20)); testPath.reset(); - expect(testPath.getBounds().toRect(), ui.Rect.zero); + expect(fromSkRect(testPath.getBounds()), ui.Rect.zero); }); test('toSVGString', () { - expect(_testClosedSkPath().toSVGString(), 'M10 10L20 10L20 20L10 20L10 10Z'); + expect( + _testClosedSkPath().toSVGString(), 'M10 10L20 10L20 20L10 20L10 10Z'); }); test('isEmpty', () { - expect(SkPath().isEmpty(), true); - expect(_testClosedSkPath().isEmpty(), false); + expect(SkPath().isEmpty(), isTrue); + expect(_testClosedSkPath().isEmpty(), isFalse); }); test('copy', () { final SkPath original = _testClosedSkPath(); final SkPath copy = original.copy(); - expect(original.getBounds().toRect(), copy.getBounds().toRect()); + expect(fromSkRect(original.getBounds()), fromSkRect(copy.getBounds())); }); test('transform', () { path = _testClosedSkPath(); path.transform(2, 0, 10, 0, 2, 10, 0, 0, 0); - final ui.Rect transformedBounds = path.getBounds().toRect(); - expect(transformedBounds, ui.Rect.fromLTRB(30, 30, 50, 50)); + final ui.Rect transformedBounds = fromSkRect(path.getBounds()); + expect(transformedBounds, const ui.Rect.fromLTRB(30, 30, 50, 50)); }); test('SkContourMeasureIter/SkContourMeasure', () { - final SkContourMeasureIter iter = SkContourMeasureIter(_testClosedSkPath(), false, 0); - final SkContourMeasure measure1 = iter.next(); + final SkContourMeasureIter iter = + SkContourMeasureIter(_testClosedSkPath(), false, 1.0); + final SkContourMeasure measure1 = iter.next()!; expect(measure1.length(), 40); expect(measure1.getPosTan(5), Float32List.fromList([15, 10, 1, 0])); - expect(measure1.getPosTan(15), Float32List.fromList([20, 15, 0, 1])); - expect(measure1.isClosed(), true); + expect( + measure1.getPosTan(15), Float32List.fromList([20, 15, 0, 1])); + expect(measure1.isClosed(), isTrue); // Starting with a box path: // @@ -783,48 +835,36 @@ void _pathTests() { // | | // 20 +-----------+ final SkPath segment = measure1.getSegment(5, 15, true); - expect(segment.getBounds().toRect(), ui.Rect.fromLTRB(15, 10, 20, 15)); + expect(fromSkRect(segment.getBounds()), const ui.Rect.fromLTRB(15, 10, 20, 15)); - final SkContourMeasure measure2 = iter.next(); + final SkContourMeasure? measure2 = iter.next(); expect(measure2, isNull); }); -} -void _skSkRectTests() { - test('SkRect', () { - final SkRect rect = SkRect(fLeft: 1, fTop: 2, fRight: 3, fBottom: 4); - expect(rect.fLeft, 1); - expect(rect.fTop, 2); - expect(rect.fRight, 3); - expect(rect.fBottom, 4); + test('SkPath.toCmds and CanvasKit.Path.MakeFromCmds', () { + const ui.Rect rect = ui.Rect.fromLTRB(0, 0, 10, 10); + final SkPath path = SkPath(); + path.addRect(toSkRect(rect)); + expect(path.toCmds(), [ + 0, 0, 0, // moveTo + 1, 10, 0, // lineTo + 1, 10, 10, // lineTo + 1, 0, 10, // lineTo + 5, // close + ]); - final ui.Rect uiRect = rect.toRect(); - expect(uiRect.left, 1); - expect(uiRect.top, 2); - expect(uiRect.right, 3); - expect(uiRect.bottom, 4); + final SkPath copy = canvasKit.Path.MakeFromCmds(path.toCmds()); + expect(fromSkRect(copy.getBounds()), rect); }); } SkVertices _testVertices() { - return canvasKit.MakeSkVertices( + return canvasKit.MakeVertices( canvasKit.VertexMode.Triangles, - [ - Float32List.fromList([0, 0]), - Float32List.fromList([10, 10]), - Float32List.fromList([0, 20]), - ], - [ - Float32List.fromList([0, 0]), - Float32List.fromList([10, 10]), - Float32List.fromList([0, 20]), - ], - [ - Float32List.fromList([255, 0, 0, 255]), - Float32List.fromList([0, 255, 0, 255]), - Float32List.fromList([0, 0, 255, 255]), - ], - Uint16List.fromList([0, 1, 2]), + Float32List.fromList([0, 0, 10, 10, 0, 20]), + Float32List.fromList([0, 0, 10, 10, 0, 20]), + Uint32List.fromList([0xffff0000, 0xff00ff00, 0xff0000ff]), + Uint16List.fromList([0, 1, 2]), ); } @@ -835,17 +875,13 @@ void _skVerticesTests() { } void _canvasTests() { - SkPictureRecorder recorder; - SkCanvas canvas; + late SkPictureRecorder recorder; + late SkCanvas canvas; setUp(() { recorder = SkPictureRecorder(); - canvas = recorder.beginRecording(SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - )); + canvas = + recorder.beginRecording(toSkRect(const ui.Rect.fromLTRB(0, 0, 100, 100))); }); tearDown(() { @@ -866,38 +902,28 @@ void _canvasTests() { test('saveLayer', () { canvas.saveLayer( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), SkPaint(), + toSkRect(const ui.Rect.fromLTRB(0, 0, 100, 100)), + null, + null, ); }); - test('SkCanvasSaveLayerWithoutBoundsOverload.saveLayer', () { - final SkCanvasSaveLayerWithoutBoundsOverload override = canvas as SkCanvasSaveLayerWithoutBoundsOverload; - override.saveLayer(SkPaint()); + test('saveLayer without bounds', () { + canvas.saveLayer(SkPaint(), null, null, null); }); - test('SkCanvasSaveLayerWithFilterOverload.saveLayer', () { - final SkCanvasSaveLayerWithFilterOverload override = canvas as SkCanvasSaveLayerWithFilterOverload; - override.saveLayer( + test('saveLayer with filter', () { + canvas.saveLayer( SkPaint(), - canvasKit.SkImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), + toSkRect(const ui.Rect.fromLTRB(0, 0, 100, 100)), + canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), 0, - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), ); }); test('clear', () { - canvas.clear(Float32List.fromList([0, 0, 0, 0])); + canvas.clear(Float32List.fromList([0, 0, 0, 0])); }); test('clipPath', () { @@ -910,22 +936,7 @@ void _canvasTests() { test('clipRRect', () { canvas.clipRRect( - SkRRect( - rect: SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), + Float32List.fromList([0, 0, 100, 100, 1, 2, 3, 4, 5, 6, 7, 8]), canvasKit.ClipOp.Intersect, true, ); @@ -933,12 +944,7 @@ void _canvasTests() { test('clipRect', () { canvas.clipRect( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), + Float32List.fromList([0, 0, 100, 100]), canvasKit.ClipOp.Intersect, true, ); @@ -946,12 +952,7 @@ void _canvasTests() { test('drawArc', () { canvas.drawArc( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 50, - ), + Float32List.fromList([0, 0, 100, 50]), 0, 100, true, @@ -960,17 +961,15 @@ void _canvasTests() { }); test('drawAtlas', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawAtlas( - image.getCurrentFrame(), - Float32List.fromList([0, 0, 1, 1]), - Float32List.fromList([1, 0, 2, 3]), + image.makeImageAtCurrentFrame(), + Float32List.fromList([0, 0, 1, 1]), + Float32List.fromList([1, 0, 2, 3]), SkPaint(), canvasKit.BlendMode.SrcOver, - [ - Float32List.fromList([0, 0, 0, 1]), - Float32List.fromList([1, 1, 1, 1]), - ], + Uint32List.fromList([0xff000000, 0xffffffff]), ); }); @@ -984,69 +983,72 @@ void _canvasTests() { test('drawDRRect', () { canvas.drawDRRect( - SkRRect( - rect: SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), - SkRRect( - rect: SkRect( - fLeft: 20, - fTop: 20, - fRight: 80, - fBottom: 80, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), + Float32List.fromList([0, 0, 100, 100, 1, 2, 3, 4, 5, 6, 7, 8]), + Float32List.fromList([20, 20, 80, 80, 1, 2, 3, 4, 5, 6, 7, 8]), SkPaint(), ); }); - test('drawImage', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); - canvas.drawImage( - image.getCurrentFrame(), + test('drawImageOptions', () { + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + canvas.drawImageOptions( + image.makeImageAtCurrentFrame(), 10, 20, + canvasKit.FilterMode.Linear, + canvasKit.MipmapMode.None, SkPaint(), ); }); - test('drawImageRect', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); - canvas.drawImageRect( - image.getCurrentFrame(), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), + test('drawImageCubic', () { + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + canvas.drawImageCubic( + image.makeImageAtCurrentFrame(), + 10, + 20, + 0.3, + 0.3, + SkPaint(), + ); + }); + + test('drawImageRectOptions', () { + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + canvas.drawImageRectOptions( + image.makeImageAtCurrentFrame(), + Float32List.fromList([0, 0, 1, 1]), + Float32List.fromList([0, 0, 1, 1]), + canvasKit.FilterMode.Linear, + canvasKit.MipmapMode.None, + SkPaint(), + ); + }); + + test('drawImageRectCubic', () { + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + canvas.drawImageRectCubic( + image.makeImageAtCurrentFrame(), + Float32List.fromList([0, 0, 1, 1]), + Float32List.fromList([0, 0, 1, 1]), + 0.3, + 0.3, SkPaint(), - false, ); }); test('drawImageNine', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawImageNine( - image.getCurrentFrame(), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), + image.makeImageAtCurrentFrame(), + Float32List.fromList([0, 0, 1, 1]), + Float32List.fromList([0, 0, 1, 1]), + canvasKit.FilterMode.Linear, SkPaint(), ); }); @@ -1056,7 +1058,7 @@ void _canvasTests() { }); test('drawOval', () { - canvas.drawOval(SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), SkPaint()); + canvas.drawOval(Float32List.fromList([0, 0, 1, 1]), SkPaint()); }); test('drawPaint', () { @@ -1073,60 +1075,42 @@ void _canvasTests() { test('drawPoints', () { canvas.drawPoints( canvasKit.PointMode.Lines, - Float32List.fromList([0, 0, 10, 10, 0, 10]), + Float32List.fromList([0, 0, 10, 10, 0, 10]), SkPaint(), ); }); test('drawRRect', () { canvas.drawRRect( - SkRRect( - rect: SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), + Float32List.fromList([0, 0, 100, 100, 1, 2, 3, 4, 5, 6, 7, 8]), SkPaint(), ); }); test('drawRect', () { canvas.drawRect( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), + Float32List.fromList([0, 0, 100, 100]), SkPaint(), ); }); test('drawShadow', () { - for (int flags in const [0x01, 0x00]) { + for (final int flags in const [0x01, 0x00]) { const double devicePixelRatio = 2.0; const double elevation = 4.0; const double ambientAlpha = 0.039; const double spotAlpha = 0.25; final SkPath path = _testClosedSkPath(); - final ui.Rect bounds = path.getBounds().toRect(); + final ui.Rect bounds = fromSkRect(path.getBounds()); final double shadowX = (bounds.left + bounds.right) / 2.0; final double shadowY = bounds.top - 600.0; const ui.Color color = ui.Color(0xAABBCCDD); - ui.Color inAmbient = color.withAlpha((color.alpha * ambientAlpha).round()); - ui.Color inSpot = color.withAlpha((color.alpha * spotAlpha).round()); + final ui.Color inAmbient = + color.withAlpha((color.alpha * ambientAlpha).round()); + final ui.Color inSpot = + color.withAlpha((color.alpha * spotAlpha).round()); final SkTonalColors inTonalColors = SkTonalColors( ambient: makeFreshSkColor(inAmbient), @@ -1138,8 +1122,7 @@ void _canvasTests() { canvas.drawShadow( path, - Float32List(3) - ..[2] = devicePixelRatio * elevation, + Float32List(3)..[2] = devicePixelRatio * elevation, Float32List(3) ..[0] = shadowX ..[1] = shadowY @@ -1180,18 +1163,10 @@ void _canvasTests() { canvas.translate(4, 5); }); - test('flush', () { - canvas.flush(); - }); - test('drawPicture', () { final SkPictureRecorder otherRecorder = SkPictureRecorder(); - final SkCanvas otherCanvas = otherRecorder.beginRecording(SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - )); + final SkCanvas otherCanvas = otherRecorder + .beginRecording(Float32List.fromList([0, 0, 100, 100])); otherCanvas.drawLine(0, 0, 10, 10, SkPaint()); canvas.drawPicture(otherRecorder.finishRecordingAsPicture()); }); @@ -1211,27 +1186,288 @@ void _canvasTests() { }); test('toImage.toByteData', () async { + // Pretend that FinalizationRegistry is supported, so we can run this + // test in older browsers (the test will use a TestCollector instead of + // ProductionCollector) + browserSupportsFinalizationRegistry = true; final SkPictureRecorder otherRecorder = SkPictureRecorder(); - final SkCanvas otherCanvas = otherRecorder.beginRecording(SkRect( - fLeft: 0, - fTop: 0, - fRight: 1, - fBottom: 1, - )); + final SkCanvas otherCanvas = otherRecorder + .beginRecording(Float32List.fromList([0, 0, 1, 1])); otherCanvas.drawRect( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 1, - fBottom: 1, - ), + Float32List.fromList([0, 0, 1, 1]), SkPaint(), ); - final CkPicture picture = CkPicture(otherRecorder.finishRecordingAsPicture(), null); - final CkImage image = await picture.toImage(1, 1); - final ByteData rawData = await image.toByteData(format: ui.ImageByteFormat.rawRgba); - expect(rawData, isNotNull); - final ByteData pngData = await image.toByteData(format: ui.ImageByteFormat.png); - expect(pngData, isNotNull); + final CkPicture picture = + CkPicture(otherRecorder.finishRecordingAsPicture(), null, null); + final CkImage image = await picture.toImage(1, 1) as CkImage; + final ByteData rawData = + await image.toByteData(format: ui.ImageByteFormat.rawRgba); + expect(rawData.lengthInBytes, greaterThan(0)); + final ByteData pngData = + await image.toByteData(format: ui.ImageByteFormat.png); + expect(pngData.lengthInBytes, greaterThan(0)); + }); +} + +void _textStyleTests() { + test('SkTextDecorationStyle mapping is correct', () { + expect(canvasKit.DecorationStyle.Solid.value, + ui.TextDecorationStyle.solid.index); + expect(canvasKit.DecorationStyle.Double.value, + ui.TextDecorationStyle.double.index); + expect(canvasKit.DecorationStyle.Dotted.value, + ui.TextDecorationStyle.dotted.index); + expect(canvasKit.DecorationStyle.Dashed.value, + ui.TextDecorationStyle.dashed.index); + expect(canvasKit.DecorationStyle.Wavy.value, + ui.TextDecorationStyle.wavy.index); + }); + + test('ui.TextDecorationStyle converts to SkTextDecorationStyle', () { + for (final ui.TextDecorationStyle decorationStyle + in ui.TextDecorationStyle.values) { + expect(toSkTextDecorationStyle(decorationStyle).value, + decorationStyle.index); + } + }); + + test('SkTextBaseline mapping is correct', () { + expect(canvasKit.TextBaseline.Alphabetic.value, + ui.TextBaseline.alphabetic.index); + expect(canvasKit.TextBaseline.Ideographic.value, + ui.TextBaseline.ideographic.index); + }); + + test('ui.TextBaseline converts to SkTextBaseline', () { + for (final ui.TextBaseline textBaseline in ui.TextBaseline.values) { + expect(toSkTextBaseline(textBaseline).value, textBaseline.index); + } + }); + + test('SkPlaceholderAlignment mapping is correct', () { + expect(canvasKit.PlaceholderAlignment.Baseline.value, + ui.PlaceholderAlignment.baseline.index); + expect(canvasKit.PlaceholderAlignment.AboveBaseline.value, + ui.PlaceholderAlignment.aboveBaseline.index); + expect(canvasKit.PlaceholderAlignment.BelowBaseline.value, + ui.PlaceholderAlignment.belowBaseline.index); + expect(canvasKit.PlaceholderAlignment.Top.value, + ui.PlaceholderAlignment.top.index); + expect(canvasKit.PlaceholderAlignment.Bottom.value, + ui.PlaceholderAlignment.bottom.index); + expect(canvasKit.PlaceholderAlignment.Middle.value, + ui.PlaceholderAlignment.middle.index); + }); + + test('ui.PlaceholderAlignment converts to SkPlaceholderAlignment', () { + for (final ui.PlaceholderAlignment placeholderAlignment + in ui.PlaceholderAlignment.values) { + expect(toSkPlaceholderAlignment(placeholderAlignment).value, + placeholderAlignment.index); + } + }); +} + +void _paragraphTests() { + // This test is just a kitchen sink that blasts CanvasKit with all paragraph + // properties all at once, making sure CanvasKit doesn't choke on anything. + // In particular, this tests that our JS bindings are correct, such as that + // arguments are of acceptable types and passed in the correct order. + test('SkParagraph API kitchensink', () { + final SkParagraphStyleProperties props = SkParagraphStyleProperties(); + props.textAlign = canvasKit.TextAlign.Center; + props.textDirection = canvasKit.TextDirection.RTL; + props.heightMultiplier = 3; + props.textHeightBehavior = canvasKit.TextHeightBehavior.All; + props.maxLines = 4; + props.ellipsis = '___'; + props.textStyle = SkTextStyleProperties() + ..backgroundColor = Float32List.fromList([1, 2, 3, 4]) + ..color = Float32List.fromList([5, 6, 7, 8]) + ..foregroundColor = Float32List.fromList([9, 10, 11, 12]) + ..decoration = 0x2 + ..decorationThickness = 2.0 + ..decorationColor = Float32List.fromList([13, 14, 15, 16]) + ..decorationStyle = canvasKit.DecorationStyle.Dotted + ..textBaseline = canvasKit.TextBaseline.Ideographic + ..fontSize = 24 + ..letterSpacing = 5 + ..wordSpacing = 10 + ..heightMultiplier = 2.5 + ..halfLeading = true + ..locale = 'en_CA' + ..fontFamilies = ['Roboto', 'serif'] + ..fontStyle = (SkFontStyle() + ..slant = canvasKit.FontSlant.Upright + ..weight = canvasKit.FontWeight.Normal) + ..shadows = [] + ..fontFeatures = [ + SkFontFeature() + ..name = 'pnum' + ..value = 1, + SkFontFeature() + ..name = 'tnum' + ..value = 1, + ]; + props.strutStyle = SkStrutStyleProperties() + ..fontFamilies = ['Roboto', 'Noto'] + ..fontStyle = (SkFontStyle() + ..slant = canvasKit.FontSlant.Italic + ..weight = canvasKit.FontWeight.Bold) + ..fontSize = 23 + ..heightMultiplier = 5 + ..halfLeading = true + ..leading = 6 + ..strutEnabled = true + ..forceStrutHeight = false; + + final SkParagraphStyle paragraphStyle = canvasKit.ParagraphStyle(props); + final SkParagraphBuilder builder = canvasKit.ParagraphBuilder.Make( + paragraphStyle, + skiaFontCollection.skFontMgr, + ); + + builder.addText('Hello'); + builder.addPlaceholder( + 50, + 25, + canvasKit.PlaceholderAlignment.Middle, + canvasKit.TextBaseline.Ideographic, + 4.0, + ); + builder + .pushStyle(canvasKit.TextStyle(SkTextStyleProperties()..fontSize = 12)); + builder.addText('World'); + builder.pop(); + builder.pushPaintStyle( + canvasKit.TextStyle(SkTextStyleProperties()..fontSize = 12), + SkPaint(), + SkPaint()); + builder.addText('!'); + builder.pop(); + builder.pushStyle( + canvasKit.TextStyle(SkTextStyleProperties()..halfLeading = true)); + builder.pop(); + final SkParagraph paragraph = builder.build(); + paragraph.layout(55); + expect(paragraph.getAlphabeticBaseline(), + within(distance: 0.5, from: 20.7)); + expect(paragraph.didExceedMaxLines(), isFalse); + expect(paragraph.getHeight(), 25); + expect(paragraph.getIdeographicBaseline(), + within(distance: 0.5, from: 25)); + expect(paragraph.getLongestLine(), 50); + expect(paragraph.getMaxIntrinsicWidth(), 50); + expect(paragraph.getMinIntrinsicWidth(), 50); + expect(paragraph.getMaxWidth(), 55); + expect( + paragraph.getRectsForRange(1, 3, canvasKit.RectHeightStyle.Tight, + canvasKit.RectWidthStyle.Max), + []); + expect(paragraph.getRectsForPlaceholders(), hasLength(1)); + expect(paragraph.getLineMetrics(), hasLength(1)); + + final SkLineMetrics lineMetrics = paragraph.getLineMetrics().single; + expect(lineMetrics.ascent, within(distance: 0.5, from: 20.7)); + expect(lineMetrics.descent, within(distance: 0.2, from: 4.3)); + expect(lineMetrics.isHardBreak, isTrue); + expect(lineMetrics.baseline, within(distance: 0.5, from: 20.7)); + expect(lineMetrics.height, 25); + expect(lineMetrics.left, 2.5); + expect(lineMetrics.width, 50); + expect(lineMetrics.lineNumber, 0); + + expect(paragraph.getGlyphPositionAtCoordinate(5, 5).affinity, + canvasKit.Affinity.Downstream); + + // "Hello" + for (int i = 0; i < 5; i++) { + expect(paragraph.getWordBoundary(i).start, 0); + expect(paragraph.getWordBoundary(i).end, 5); + } + // Placeholder + expect(paragraph.getWordBoundary(5).start, 5); + expect(paragraph.getWordBoundary(5).end, 6); + // "World" + for (int i = 6; i < 11; i++) { + expect(paragraph.getWordBoundary(i).start, 6); + expect(paragraph.getWordBoundary(i).end, 11); + } + // "!" + expect(paragraph.getWordBoundary(11).start, 11); + expect(paragraph.getWordBoundary(11).end, 12); + + paragraph.delete(); + }); + + test('RectHeightStyle', () { + final SkParagraphStyleProperties props = SkParagraphStyleProperties(); + props.heightMultiplier = 3; + props.textAlign = canvasKit.TextAlign.Start; + props.textDirection = canvasKit.TextDirection.LTR; + props.textStyle = SkTextStyleProperties() + ..fontSize = 25 + ..fontFamilies = ['Roboto'] + ..fontStyle = (SkFontStyle()..weight = canvasKit.FontWeight.Normal); + props.strutStyle = SkStrutStyleProperties() + ..strutEnabled = true + ..forceStrutHeight = true + ..fontSize = 25 + ..fontFamilies = ['Roboto'] + ..heightMultiplier = 3 + ..fontStyle = (SkFontStyle()..weight = canvasKit.FontWeight.Normal); + final SkParagraphStyle paragraphStyle = canvasKit.ParagraphStyle(props); + final SkParagraphBuilder builder = + canvasKit.ParagraphBuilder.MakeFromFontProvider( + paragraphStyle, + skiaFontCollection.fontProvider, + ); + builder.addText('hello'); + + final SkParagraph paragraph = builder.build(); + paragraph.layout(500); + + expect( + paragraph.getRectsForRange( + 0, + 1, + canvasKit.RectHeightStyle.Strut, + canvasKit.RectWidthStyle.Tight, + ), + >[ + [0, 0, 13.770000457763672, 75], + ], + ); + }); + + test('TextHeightBehavior', () { + expect( + toSkTextHeightBehavior(const ui.TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: true, + )), + canvasKit.TextHeightBehavior.All, + ); + expect( + toSkTextHeightBehavior(const ui.TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: true, + )), + canvasKit.TextHeightBehavior.DisableFirstAscent, + ); + expect( + toSkTextHeightBehavior(const ui.TextHeightBehavior( + applyHeightToFirstAscent: true, + applyHeightToLastDescent: false, + )), + canvasKit.TextHeightBehavior.DisableLastDescent, + ); + expect( + toSkTextHeightBehavior(const ui.TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + )), + canvasKit.TextHeightBehavior.DisableAll, + ); }); } diff --git a/lib/web_ui/test/canvaskit/color_filter_golden_test.dart b/lib/web_ui/test/canvaskit/color_filter_golden_test.dart new file mode 100644 index 0000000000000..b52b1e660497f --- /dev/null +++ b/lib/web_ui/test/canvaskit/color_filter_golden_test.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); + +Future matchSceneGolden(String goldenFile, LayerScene scene, + {bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + dispatcher.rasterizer!.draw(scene.layerTree); + await matchGoldenFile(goldenFile, region: region, write: write); +} + +void testMain() { + group('ColorFilter', () { + setUpCanvasKitTest(); + + test('ColorFilter.matrix applies a color filter', () async { + final LayerSceneBuilder builder = LayerSceneBuilder(); + + builder.pushOffset(0, 0); + + // Draw a red circle and apply it to the scene. + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + canvas.drawCircle( + const ui.Offset(75, 125), + 50, + CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture redCircle = recorder.endRecording(); + + builder.addPicture(ui.Offset.zero, redCircle); + + // Apply a "greyscale" color filter. + builder.pushColorFilter(const ui.ColorFilter.matrix([ + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0, 0, 0, 1, 0, // + ])); + + // Draw another red circle and apply it to the scene. + // This one should be grey since we have the color filter. + final CkPictureRecorder recorder2 = CkPictureRecorder(); + final CkCanvas canvas2 = recorder2.beginRecording(region); + + canvas2.drawCircle( + const ui.Offset(425, 125), + 50, + CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture greyCircle = recorder2.endRecording(); + + builder.addPicture(ui.Offset.zero, greyCircle); + + await matchSceneGolden('canvaskit_colorfilter.png', builder.build()); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} diff --git a/lib/web_ui/test/canvaskit/common.dart b/lib/web_ui/test/canvaskit/common.dart index 1d7731c9abdcb..32f4f01d6a158 100644 --- a/lib/web_ui/test/canvaskit/common.dart +++ b/lib/web_ui/test/canvaskit/common.dart @@ -2,10 +2,171 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +import 'package:test/test.dart'; + import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +export '../common.dart'; + +/// Used in tests instead of [ProductionCollector] to control Skia object +/// collection explicitly, and to prevent leaks across tests. +/// +/// See [TestCollector] for usage. +late TestCollector testCollector; + +/// Common test setup for all CanvasKit unit-tests. +void setUpCanvasKitTest() { + setUpAll(() async { + expect(useCanvasKit, true, reason: 'This test must run in CanvasKit mode.'); + debugResetBrowserSupportsFinalizationRegistry(); + await ui.webOnlyInitializePlatform(assetManager: WebOnlyMockAssetManager()); + }); + + setUp(() async { + testCollector = TestCollector(); + Collector.debugOverrideCollector(testCollector); + }); + + tearDown(() { + testCollector.cleanUpAfterTest(); + debugResetBrowserSupportsFinalizationRegistry(); + HtmlViewEmbedder.instance.debugClear(); + SurfaceFactory.instance.debugClear(); + }); + + tearDownAll(() { + debugResetBrowserSupportsFinalizationRegistry(); + }); +} + +/// Utility function for CanvasKit tests to draw pictures without +/// the [CkPictureRecorder] boilerplate. +CkPicture paintPicture( + ui.Rect cullRect, void Function(CkCanvas canvas) painter) { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(cullRect); + painter(canvas); + return recorder.endRecording(); +} + +class _TestFinalizerRegistration { + _TestFinalizerRegistration(this.wrapper, this.deletable, this.stackTrace); + + final Object wrapper; + final SkDeletable deletable; + final StackTrace stackTrace; +} + +class _TestCollection { + _TestCollection(this.deletable, this.stackTrace); + + final SkDeletable deletable; + final StackTrace stackTrace; +} + +/// Provides explicit synchronous API for collecting Skia objects in tests. +/// +/// [ProductionCollector] relies on `FinalizationRegistry` and timers to +/// delete Skia objects, which makes it more precise and efficient. However, +/// it also makes it unpredictable. For example, an object created in one +/// test may be collected while running another test because the timing is +/// subject to browser-specific GC scheduling. +/// +/// Tests should use [collectNow] and [collectAfterTest] to trigger collections. +class TestCollector implements Collector { + final List<_TestFinalizerRegistration> _activeRegistrations = + <_TestFinalizerRegistration>[]; + final List<_TestFinalizerRegistration> _collectedRegistrations = + <_TestFinalizerRegistration>[]; + + final List<_TestCollection> _pendingCollections = <_TestCollection>[]; + final List<_TestCollection> _completedCollections = <_TestCollection>[]; + + @override + void register(Object wrapper, SkDeletable deletable) { + _activeRegistrations.add( + _TestFinalizerRegistration(wrapper, deletable, StackTrace.current), + ); + } + + @override + void collect(SkDeletable deletable) { + _pendingCollections.add( + _TestCollection(deletable, StackTrace.current), + ); + } + + /// Deletes all Skia objects scheduled for collection. + void collectNow() { + for (final _TestCollection collection in _pendingCollections) { + late final _TestFinalizerRegistration? activeRegistration; + for (final _TestFinalizerRegistration registration in _activeRegistrations) { + if (identical(registration.deletable, collection.deletable)) { + activeRegistration = registration; + break; + } + } + if (activeRegistration == null) { + late final _TestFinalizerRegistration? collectedRegistration; + for (final _TestFinalizerRegistration registration + in _collectedRegistrations) { + if (identical(registration.deletable, collection.deletable)) { + collectedRegistration = registration; + break; + } + } + if (collectedRegistration == null) { + fail( + 'Attempted to collect an object that was never registered for finalization.\n' + 'The collection was requested here:\n' + '${collection.stackTrace}'); + } else { + final _TestCollection firstCollection = _completedCollections + .firstWhere((_TestCollection completedCollection) { + return identical( + completedCollection.deletable, collection.deletable); + }); + fail( + 'Attempted to collect an object that was previously collected.\n' + 'The object was registered for finalization here:\n' + '${collection.stackTrace}\n\n' + 'The first collection was requested here:\n' + '${firstCollection.stackTrace}\n\n' + 'The second collection was requested here:\n' + '${collection.stackTrace}', + ); + } + } else { + _collectedRegistrations.add(activeRegistration); + _activeRegistrations.remove(activeRegistration); + _completedCollections.add(collection); + if (!collection.deletable.isDeleted()) { + collection.deletable.delete(); + } + } + } + _pendingCollections.clear(); + } -/// Whether we are running on iOS Safari. -// TODO: https://github.com/flutter/flutter/issues/60040 -bool get isIosSafari => browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs; + /// Deletes all Skia objects with registered finalizers. + /// + /// This also deletes active objects that have not been scheduled for + /// collection, to prevent objects leaking across tests. + void cleanUpAfterTest() { + for (final _TestCollection collection in _pendingCollections) { + if (!collection.deletable.isDeleted()) { + collection.deletable.delete(); + } + } + for (final _TestFinalizerRegistration registration in _activeRegistrations) { + if (!registration.deletable.isDeleted()) { + registration.deletable.delete(); + } + } + _activeRegistrations.clear(); + _collectedRegistrations.clear(); + _pendingCollections.clear(); + _completedCollections.clear(); + } +} diff --git a/lib/web_ui/test/canvaskit/embedded_views_test.dart b/lib/web_ui/test/canvaskit/embedded_views_test.dart new file mode 100644 index 0000000000000..8f0669be05a52 --- /dev/null +++ b/lib/web_ui/test/canvaskit/embedded_views_test.dart @@ -0,0 +1,530 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +const MethodCodec codec = StandardMethodCodec(); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$HtmlViewEmbedder', () { + setUpCanvasKitTest(); + + setUp(() { + window.debugOverrideDevicePixelRatio(1); + }); + + test('embeds interactive platform views', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + // The platform view is now split in two parts. The contents live + // as a child of the glassPane, and the slot lives in the glassPane + // shadow root. The slot is the one that has pointer events auto. + final html.Element contents = + domRenderer.glassPaneElement!.querySelector('#view-0')!; + final html.Element slot = + domRenderer.sceneElement!.querySelector('slot')!; + final html.Element contentsHost = contents.parent!; + final html.Element slotHost = slot.parent!; + + expect(contents, isNotNull, + reason: 'The view from the factory is injected in the DOM.'); + + expect(contentsHost.tagName, equalsIgnoringCase('flt-platform-view')); + expect(slotHost.tagName, equalsIgnoringCase('flt-platform-view-slot')); + + expect(slotHost.style.pointerEvents, 'auto', + reason: 'The slot reenables pointer events.'); + expect(contentsHost.getAttribute('slot'), slot.getAttribute('name'), + reason: 'The contents and slot are correctly related.'); + }); + + test('clips platform views with RRects', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.pushClipRRect( + ui.RRect.fromLTRBR(0, 0, 10, 10, const ui.Radius.circular(3))); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelectorAll('#sk_path_defs').single, + isNotNull, + ); + expect( + domRenderer.sceneElement! + .querySelectorAll('#sk_path_defs') + .single + .querySelectorAll('clipPath') + .single, + isNotNull, + ); + expect( + domRenderer.sceneElement! + .querySelectorAll('flt-clip') + .single + .style + .clipPath, + 'url("#svgClip1")', + ); + }); + + test('correctly transforms platform views', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + final Matrix4 scaleMatrix = Matrix4.identity() + ..scale(5, 5) + ..translate(100, 100); + sb.pushTransform(scaleMatrix.toFloat64()); + sb.pushOffset(3, 3); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + // Transformations happen on the slot element. + final html.Element slotHost = + domRenderer.sceneElement!.querySelector('flt-platform-view-slot')!; + + expect( + slotHost.style.transform, + // We should apply the scale matrix first, then the offset matrix. + // So the translate should be 515 (5 * 100 + 5 * 3), and not + // 503 (5 * 100 + 3). + 'matrix3d(5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 515, 515, 0, 1)', + ); + }); + + // Returns the list of CSS transforms applied to the ancestor chain of + // elements starting from `viewHost`, up until and excluding . + List getTransformChain(html.Element viewHost) { + final List chain = []; + html.Element? element = viewHost; + while (element != null && element.tagName.toLowerCase() != 'flt-scene') { + chain.add(element.style.transform); + element = element.parent; + } + return chain; + } + + test('converts device pixels to logical pixels (no clips)', () async { + window.debugOverrideDevicePixelRatio(4); + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(1, 1); + sb.pushOffset(2, 2); + sb.pushOffset(3, 3); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + // Transformations happen on the slot element. + final html.Element slotHost = + domRenderer.sceneElement!.querySelector('flt-platform-view-slot')!; + + expect( + getTransformChain(slotHost), + ['matrix(0.25, 0, 0, 0.25, 1.5, 1.5)'], + ); + }); + + test('converts device pixels to logical pixels (with clips)', () async { + window.debugOverrideDevicePixelRatio(4); + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(3, 3); + sb.pushClipRect(ui.Rect.largest); + sb.pushOffset(6, 6); + sb.pushClipRect(ui.Rect.largest); + sb.pushOffset(9, 9); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + // Transformations happen on the slot element. + final html.Element slotHost = + domRenderer.sceneElement!.querySelector('flt-platform-view-slot')!; + + expect( + getTransformChain(slotHost), + [ + 'matrix(1, 0, 0, 1, 9, 9)', + 'matrix(1, 0, 0, 1, 6, 6)', + 'matrix(0.25, 0, 0, 0.25, 0.75, 0.75)', + ], + ); + }); + + test('renders overlays on top of platform views', () async { + expect(SurfaceFactory.instance.debugCacheSize, 0); + final CkPicture testPicture = + paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); + }); + + // Initialize all platform views to be used in the test. + final List platformViewIds = []; + for (int i = 0; i < HtmlViewEmbedder.maximumOverlaySurfaces * 2; i++) { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-$i', + ); + await _createPlatformView(i, 'test-platform-view'); + platformViewIds.add(i); + } + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + void renderTestScene({required int viewCount}) { + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + for (int i = 0; i < viewCount; i++) { + sb.addPicture(ui.Offset.zero, testPicture); + sb.addPlatformView(i, width: 10, height: 10); + } + dispatcher.rasterizer!.draw(sb.build().layerTree); + } + + int countCanvases() { + return domRenderer.sceneElement!.querySelectorAll('canvas').length; + } + + // Frame 1: + // Render: up to cache size platform views. + // Expect: main canvas plus platform view overlays. + renderTestScene(viewCount: HtmlViewEmbedder.maximumOverlaySurfaces); + expect(countCanvases(), HtmlViewEmbedder.maximumOverlaySurfaces); + + // Frame 2: + // Render: zero platform views. + // Expect: main canvas, no overlays. + await Future.delayed(Duration.zero); + renderTestScene(viewCount: 0); + expect(countCanvases(), 1); + + // Frame 3: + // Render: less than cache size platform views. + // Expect: overlays reused. + await Future.delayed(Duration.zero); + renderTestScene(viewCount: HtmlViewEmbedder.maximumOverlaySurfaces - 2); + expect(countCanvases(), HtmlViewEmbedder.maximumOverlaySurfaces - 1); + + // Frame 4: + // Render: more platform views than max cache size. + // Expect: main canvas, backup overlay, maximum overlays. + await Future.delayed(Duration.zero); + renderTestScene(viewCount: HtmlViewEmbedder.maximumOverlaySurfaces * 2); + expect(countCanvases(), HtmlViewEmbedder.maximumOverlaySurfaces); + + // Frame 5: + // Render: zero platform views. + // Expect: main canvas, no overlays. + await Future.delayed(Duration.zero); + renderTestScene(viewCount: 0); + expect(countCanvases(), 1); + + // Frame 6: + // Render: deleted platform views. + // Expect: error. + for (final int id in platformViewIds) { + const StandardMethodCodec codec = StandardMethodCodec(); + final Completer completer = Completer(); + ui.window.sendPlatformMessage( + 'flutter/platform_views', + codec.encodeMethodCall(MethodCall( + 'dispose', + id, + )), + completer.complete, + ); + await completer.future; + } + + try { + renderTestScene(viewCount: platformViewIds.length); + fail('Expected to throw'); + } on AssertionError catch (error) { + expect( + error.toString(), + 'Assertion failed: "Cannot render platform views: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15. These views have not been created, or they have been deleted."', + ); + } + + // Frame 7: + // Render: a platform view after error. + // Expect: success. Just checking the system is not left in a corrupted state. + await _createPlatformView(0, 'test-platform-view'); + renderTestScene(viewCount: 0); + // TODO(yjbanov): skipped due to https://github.com/flutter/flutter/issues/73867 + }, skip: isSafari); + + test('correctly reuses overlays', () async { + final CkPicture testPicture = + paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); + }); + + // Initialize all platform views to be used in the test. + final List platformViewIds = []; + for (int i = 0; i < 20; i++) { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-$i', + ); + await _createPlatformView(i, 'test-platform-view'); + platformViewIds.add(i); + } + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + void renderTestScene(List views) { + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + for (final int view in views) { + sb.addPicture(ui.Offset.zero, testPicture); + sb.addPlatformView(view, width: 10, height: 10); + } + dispatcher.rasterizer!.draw(sb.build().layerTree); + } + + int countCanvases() { + return domRenderer.sceneElement!.querySelectorAll('canvas').length; + } + + // Frame 1: + // Render: Views 1-10 + // Expect: main canvas plus platform view overlays; empty cache. + renderTestScene([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect(countCanvases(), HtmlViewEmbedder.maximumOverlaySurfaces); + + // Frame 2: + // Render: Views 2-11 + // Expect: main canvas plus platform view overlays; empty cache. + await Future.delayed(Duration.zero); + renderTestScene([2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + expect(countCanvases(), HtmlViewEmbedder.maximumOverlaySurfaces); + + // Frame 3: + // Render: Views 3-12 + // Expect: main canvas plus platform view overlays; empty cache. + await Future.delayed(Duration.zero); + renderTestScene([3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + expect(countCanvases(), HtmlViewEmbedder.maximumOverlaySurfaces); + + // TODO(yjbanov): skipped due to https://github.com/flutter/flutter/issues/73867 + }, skip: isSafari); + + test('embeds and disposes of a platform view', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelector('flt-platform-view-slot'), + isNotNull, + ); + expect( + domRenderer.glassPaneElement!.querySelector('flt-platform-view'), + isNotNull, + ); + + await _disposePlatformView(0); + + sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelector('flt-platform-view-slot'), + isNull, + ); + expect( + domRenderer.glassPaneElement!.querySelector('flt-platform-view'), + isNull, + ); + }); + + test('removed the DOM node of an unrendered platform view', () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'view-0', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelector('flt-platform-view-slot'), + isNotNull, + ); + expect( + domRenderer.glassPaneElement!.querySelector('flt-platform-view'), + isNotNull, + ); + + // Render a frame with a different platform view. + await _createPlatformView(1, 'test-platform-view'); + sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPlatformView(1, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelectorAll('flt-platform-view-slot'), + hasLength(1)); + expect( + domRenderer.glassPaneElement!.querySelectorAll('flt-platform-view'), + hasLength(2)); + + // Render a frame without a platform view, but also without disposing of + // the platform view. + sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + dispatcher.rasterizer!.draw(sb.build().layerTree); + + expect( + domRenderer.sceneElement!.querySelector('flt-platform-view-slot'), + isNull, + ); + // The actual contents of the platform view are kept in the dom, until + // it's actually disposed of! + expect( + domRenderer.glassPaneElement!.querySelector('flt-platform-view'), + isNotNull, + ); + }); + + test( + 'removes old SVG clip definitions from the DOM when the view is recomposited', + () async { + ui.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => html.DivElement()..id = 'test-view', + ); + await _createPlatformView(0, 'test-platform-view'); + + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + void renderTestScene() { + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.pushClipRRect( + ui.RRect.fromLTRBR(0, 0, 10, 10, const ui.Radius.circular(3))); + sb.addPlatformView(0, width: 10, height: 10); + dispatcher.rasterizer!.draw(sb.build().layerTree); + } + + final html.Node skPathDefs = + domRenderer.sceneElement!.querySelector('#sk_path_defs')!; + + expect(skPathDefs.childNodes, hasLength(0)); + + renderTestScene(); + expect(skPathDefs.childNodes, hasLength(1)); + + await Future.delayed(Duration.zero); + renderTestScene(); + expect(skPathDefs.childNodes, hasLength(1)); + + await Future.delayed(Duration.zero); + renderTestScene(); + expect(skPathDefs.childNodes, hasLength(1)); + }); + // TODO(dit): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} + +// Sends a platform message to create a Platform View with the given id and viewType. +Future _createPlatformView(int id, String viewType) { + final Completer completer = Completer(); + window.sendPlatformMessage( + 'flutter/platform_views', + codec.encodeMethodCall(MethodCall( + 'create', + { + 'id': id, + 'viewType': viewType, + }, + )), + (dynamic _) => completer.complete(), + ); + return completer.future; +} + +Future _disposePlatformView(int id) { + final Completer completer = Completer(); + window.sendPlatformMessage( + 'flutter/platform_views', + codec.encodeMethodCall(MethodCall('dispose', id)), + (dynamic _) => completer.complete(), + ); + return completer.future; +} diff --git a/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart b/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart new file mode 100644 index 0000000000000..7cc2c57aa4fe2 --- /dev/null +++ b/lib/web_ui/test/canvaskit/fallback_fonts_golden_test.dart @@ -0,0 +1,410 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect kDefaultRegion = ui.Rect.fromLTRB(0, 0, 100, 50); + +Future matchPictureGolden(String goldenFile, CkPicture picture, + {ui.Rect region = kDefaultRegion, bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPicture(ui.Offset.zero, picture); + dispatcher.rasterizer!.draw(sb.build().layerTree); + await matchGoldenFile(goldenFile, + region: region, maxDiffRatePercent: 0.0, write: write); +} + +void testMain() { + group('Font fallbacks', () { + setUpCanvasKitTest(); + + /// Used to save and restore [ui.window.onPlatformMessage] after each test. + ui.PlatformMessageCallback? savedCallback; + + setUp(() { + notoDownloadQueue.downloader = TestDownloader(); + TestDownloader.mockDownloads.clear(); + savedCallback = ui.window.onPlatformMessage; + FontFallbackData.debugReset(); + }); + + tearDown(() { + ui.window.onPlatformMessage = savedCallback; + }); + + test('Roboto is always a fallback font', () { + expect(FontFallbackData.instance.globalFontFallbacks, contains('Roboto')); + }); + + test('will download Noto Naskh Arabic if Arabic text is added', () async { + TestDownloader.mockDownloads[ + 'https://fonts.googleapis.com/css2?family=Noto+Naskh+Arabic+UI'] = + ''' +/* arabic */ +@font-face { + font-family: 'Noto Naskh Arabic UI'; + font-style: normal; + font-weight: 400; + src: url(packages/ui/assets/NotoNaskhArabic-Regular.ttf) format('ttf'); + unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC; +} +'''; + + expect(FontFallbackData.instance.globalFontFallbacks, ['Roboto']); + + // Creating this paragraph should cause us to start to download the + // fallback font. + CkParagraphBuilder pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.addText('مرحبا'); + + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await notoDownloadQueue.debugWhenIdle(); + + expect(FontFallbackData.instance.globalFontFallbacks, + contains('Noto Naskh Arabic UI 0')); + + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(kDefaultRegion); + + pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.pushStyle(ui.TextStyle(fontSize: 32)); + pb.addText('مرحبا'); + pb.pop(); + final CkParagraph paragraph = pb.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + + canvas.drawParagraph(paragraph, const ui.Offset(0, 0)); + + await matchPictureGolden( + 'canvaskit_font_fallback_arabic.png', recorder.endRecording()); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); + + test('will put the Noto Emoji font before other fallback fonts in the list', + () async { + TestDownloader.mockDownloads[ + 'https://fonts.googleapis.com/css2?family=Noto+Color+Emoji+Compat'] = + ''' +@font-face { + font-family: 'Noto Color Emoji'; + src: url(packages/ui/assets/NotoColorEmoji.ttf) format('ttf'); +} +'''; + + TestDownloader.mockDownloads[ + 'https://fonts.googleapis.com/css2?family=Noto+Naskh+Arabic+UI'] = + ''' +/* arabic */ +@font-face { + font-family: 'Noto Naskh Arabic UI'; + font-style: normal; + font-weight: 400; + src: url(packages/ui/assets/NotoNaskhArabic-Regular.ttf) format('ttf'); + unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC; +} +'''; + + expect(FontFallbackData.instance.globalFontFallbacks, ['Roboto']); + + // Creating this paragraph should cause us to start to download the + // Arabic fallback font. + CkParagraphBuilder pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.addText('مرحبا'); + + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await notoDownloadQueue.debugWhenIdle(); + + expect(FontFallbackData.instance.globalFontFallbacks, + ['Roboto', 'Noto Naskh Arabic UI 0']); + + pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.pushStyle(ui.TextStyle(fontSize: 26)); + pb.addText('Hello 😊 مرحبا'); + pb.pop(); + final CkParagraph paragraph = pb.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await notoDownloadQueue.debugWhenIdle(); + + expect(FontFallbackData.instance.globalFontFallbacks, [ + 'Roboto', + 'Noto Color Emoji Compat 0', + 'Noto Naskh Arabic UI 0', + ]); + }); + + test('will download Noto Emojis and Noto Symbols if no matching Noto Font', + () async { + TestDownloader.mockDownloads[ + 'https://fonts.googleapis.com/css2?family=Noto+Color+Emoji+Compat'] = + ''' +@font-face { + font-family: 'Noto Color Emoji'; + src: url(packages/ui/assets/NotoColorEmoji.ttf) format('ttf'); +} +'''; + + expect(FontFallbackData.instance.globalFontFallbacks, ['Roboto']); + + // Creating this paragraph should cause us to start to download the + // fallback font. + CkParagraphBuilder pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.addText('Hello 😊'); + + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await notoDownloadQueue.debugWhenIdle(); + + expect(FontFallbackData.instance.globalFontFallbacks, + contains('Noto Color Emoji Compat 0')); + + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(kDefaultRegion); + + pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.pushStyle(ui.TextStyle(fontSize: 26)); + pb.addText('Hello 😊'); + pb.pop(); + final CkParagraph paragraph = pb.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + + canvas.drawParagraph(paragraph, const ui.Offset(0, 0)); + + await matchPictureGolden( + 'canvaskit_font_fallback_emoji.png', recorder.endRecording()); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); + + test('will gracefully fail if we cannot parse the Google Fonts CSS', + () async { + TestDownloader.mockDownloads[ + 'https://fonts.googleapis.com/css2?family=Noto+Naskh+Arabic+UI'] = + 'invalid CSS... this should cause our parser to fail'; + + expect(FontFallbackData.instance.globalFontFallbacks, ['Roboto']); + + // Creating this paragraph should cause us to start to download the + // fallback font. + final CkParagraphBuilder pb = CkParagraphBuilder( + CkParagraphStyle(), + ); + pb.addText('مرحبا'); + + // Flush microtasks and test that we didn't start any downloads. + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await Future.delayed(Duration.zero); + + expect(notoDownloadQueue.isPending, isFalse); + expect(FontFallbackData.instance.globalFontFallbacks, ['Roboto']); + }); + + // Regression test for https://github.com/flutter/flutter/issues/75836 + // When we had this bug our font fallback resolution logic would end up in an + // infinite loop and this test would freeze and time out. + test( + 'Can find fonts for two adjacent unmatched code units from different fonts', + () async { + final LoggingDownloader loggingDownloader = + LoggingDownloader(NotoDownloader()); + notoDownloadQueue.downloader = loggingDownloader; + // Try rendering text that requires fallback fonts, initially before the fonts are loaded. + + CkParagraphBuilder(CkParagraphStyle()).addText('ヽಠ'); + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await notoDownloadQueue.downloader.debugWhenIdle(); + expect( + loggingDownloader.log, + [ + 'https://fonts.googleapis.com/css2?family=Noto+Sans+SC', + 'https://fonts.googleapis.com/css2?family=Noto+Sans+JP', + 'https://fonts.googleapis.com/css2?family=Noto+Sans+Kannada+UI', + 'Noto Sans SC', + 'Noto Sans JP', + 'Noto Sans Kannada UI', + ], + ); + + // Do the same thing but this time with loaded fonts. + loggingDownloader.log.clear(); + CkParagraphBuilder(CkParagraphStyle()).addText('ヽಠ'); + EnginePlatformDispatcher.instance.rasterizer! + .debugRunPostFrameCallbacks(); + await notoDownloadQueue.downloader.debugWhenIdle(); + expect(loggingDownloader.log, isEmpty); + }); + + test('findMinimumFontsForCodeunits for all supported code units', () async { + final LoggingDownloader loggingDownloader = + LoggingDownloader(NotoDownloader()); + notoDownloadQueue.downloader = loggingDownloader; + + // Collect all supported code units from all fallback fonts in the Noto + // font tree. + final Set testedFonts = {}; + final Set supportedUniqueCodeUnits = {}; + final IntervalTree notoTree = + FontFallbackData.instance.notoTree; + for (final NotoFont font in notoTree.root.enumerateAllElements()) { + testedFonts.add(font.name); + for (final CodeunitRange range in font.approximateUnicodeRanges) { + for (int codeUnit = range.start; + codeUnit < range.end; + codeUnit += 1) { + supportedUniqueCodeUnits.add(codeUnit); + } + } + } + + expect( + supportedUniqueCodeUnits.length, greaterThan(10000)); // sanity check + expect( + testedFonts, + unorderedEquals({ + 'Noto Sans', + 'Noto Sans Malayalam UI', + 'Noto Sans Armenian', + 'Noto Sans Georgian', + 'Noto Sans Hebrew', + 'Noto Naskh Arabic UI', + 'Noto Sans Devanagari UI', + 'Noto Sans Telugu UI', + 'Noto Sans Tamil UI', + 'Noto Sans Kannada UI', + 'Noto Sans Sinhala', + 'Noto Sans Gurmukhi UI', + 'Noto Sans Gujarati UI', + 'Noto Sans Bengali UI', + 'Noto Sans Thai UI', + 'Noto Sans Lao UI', + 'Noto Sans Myanmar UI', + 'Noto Sans Ethiopic', + 'Noto Sans Khmer UI', + 'Noto Sans SC', + 'Noto Sans JP', + 'Noto Sans TC', + 'Noto Sans HK', + 'Noto Sans KR', + 'Noto Sans Egyptian Hieroglyphs', + })); + + // Construct random paragraphs out of supported code units. + final math.Random random = math.Random(0); + final List supportedCodeUnits = supportedUniqueCodeUnits.toList() + ..shuffle(random); + const int paragraphLength = 3; + + for (int batchStart = 0; + batchStart < supportedCodeUnits.length; + batchStart += paragraphLength) { + final int batchEnd = + math.min(batchStart + paragraphLength, supportedCodeUnits.length); + final Set codeUnits = {}; + for (int i = batchStart; i < batchEnd; i += 1) { + codeUnits.add(supportedCodeUnits[i]); + } + final Set fonts = {}; + for (final int codeUnit in codeUnits) { + final List fontsForUnit = notoTree.intersections(codeUnit); + + // All code units are extracted from the same tree, so there must + // be at least one font supporting each code unit + expect(fontsForUnit, isNotEmpty); + fonts.addAll(fontsForUnit); + } + + try { + findMinimumFontsForCodeUnits(codeUnits, fonts); + } catch (e) { + print( + 'findMinimumFontsForCodeunits failed:\n' + ' Code units: ${codeUnits.join(', ')}\n' + ' Fonts: ${fonts.map((NotoFont f) => f.name).join(', ')}', + ); + rethrow; + } + } + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} + +class TestDownloader extends NotoDownloader { + static final Map mockDownloads = {}; + @override + Future downloadAsString(String url, + {String? debugDescription}) async { + if (mockDownloads.containsKey(url)) { + return mockDownloads[url]!; + } else { + return ''; + } + } +} + +class LoggingDownloader implements NotoDownloader { + final List log = []; + + LoggingDownloader(this.delegate); + + final NotoDownloader delegate; + + @override + Future debugWhenIdle() { + return delegate.debugWhenIdle(); + } + + @override + Future downloadAsBytes(String url, {String? debugDescription}) { + log.add(debugDescription ?? url); + return delegate.downloadAsBytes(url); + } + + @override + Future downloadAsString(String url, {String? debugDescription}) { + log.add(debugDescription ?? url); + return delegate.downloadAsString(url); + } + + @override + int get debugActiveDownloadCount => delegate.debugActiveDownloadCount; +} diff --git a/lib/web_ui/test/canvaskit/filter_test.dart b/lib/web_ui/test/canvaskit/filter_test.dart new file mode 100644 index 0000000000000..45c8866348e50 --- /dev/null +++ b/lib/web_ui/test/canvaskit/filter_test.dart @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + List createColorFilters() { + return [ + const EngineColorFilter.mode(ui.Color(0x12345678), ui.BlendMode.srcOver) as CkColorFilter, + const EngineColorFilter.mode(ui.Color(0x12345678), ui.BlendMode.dstOver) as CkColorFilter, + const EngineColorFilter.mode(ui.Color(0x87654321), ui.BlendMode.dstOver) as CkColorFilter, + const EngineColorFilter.matrix([ + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, + ]) as CkColorFilter, + EngineColorFilter.matrix(Float32List.fromList([ + 2, 0, 0, 0, 0, + 0, 2, 0, 0, 0, + 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, + ])) as CkColorFilter, + const EngineColorFilter.linearToSrgbGamma() as CkColorFilter, + const EngineColorFilter.srgbToLinearGamma() as CkColorFilter, + ]; + } + + List createImageFilters() { + return [ + CkImageFilter.blur(sigmaX: 5, sigmaY: 6, tileMode: ui.TileMode.clamp), + CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.clamp), + CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.decal), + for (final CkColorFilter colorFilter in createColorFilters()) CkImageFilter.color(colorFilter: colorFilter), + ]; + } + + group('ImageFilters', () { + setUpCanvasKitTest(); + + test('can be constructed', () { + final CkImageFilter imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); + expect(imageFilter, isA()); + expect(imageFilter.createDefault(), isNotNull); + expect(imageFilter.resurrect(), isNotNull); + }); + + + test('== operator', () { + final List filters1 = [ + ...createImageFilters(), + ...createColorFilters(), + ]; + final List filters2 = [ + ...createImageFilters(), + ...createColorFilters(), + ]; + + for (int index1 = 0; index1 < filters1.length; index1 += 1) { + final ui.ImageFilter imageFilter1 = filters1[index1]; + expect(imageFilter1 == imageFilter1, isTrue); + for (int index2 = 0; index2 < filters2.length; index2 += 1) { + final ui.ImageFilter imageFilter2 = filters2[index2]; + expect(imageFilter1 == imageFilter2, imageFilter2 == imageFilter1); + expect(imageFilter1 == imageFilter2, index1 == index2); + } + } + }); + + test('reuses the Skia filter', () { + final CkPaint paint = CkPaint(); + paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); + + final ManagedSkiaObject managedFilter = paint.imageFilter! as ManagedSkiaObject; + final Object skiaFilter = managedFilter.skiaObject; + + paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); + expect((paint.imageFilter! as ManagedSkiaObject).skiaObject, same(skiaFilter)); + }); + + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); + + group('MaskFilter', () { + setUpCanvasKitTest(); + + test('with 0 sigma can be set on a Paint', () { + final ui.Paint paint = ui.Paint(); + const ui.MaskFilter filter = ui.MaskFilter.blur(ui.BlurStyle.normal, 0); + + expect(() => paint.maskFilter = filter, isNot(throwsException)); + }); + + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/frame_timings_test.dart b/lib/web_ui/test/canvaskit/frame_timings_test.dart new file mode 100644 index 0000000000000..f6910f4c74dd9 --- /dev/null +++ b/lib/web_ui/test/canvaskit/frame_timings_test.dart @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import '../frame_timings_common.dart'; +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('frame timings', () { + setUpCanvasKitTest(); + + test('collects frame timings', () async { + await runFrameTimingsTest(); + }); + }, skip: isIosSafari); // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 +} diff --git a/lib/web_ui/test/canvaskit/hot_restart_test.dart b/lib/web_ui/test/canvaskit/hot_restart_test.dart new file mode 100644 index 0000000000000..d187fbb7a8284 --- /dev/null +++ b/lib/web_ui/test/canvaskit/hot_restart_test.dart @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('initialize', () { + test( + 're-uses the same initialized instance if it is already set on the window', + () async { + expect(windowFlutterCanvasKit, isNull); + + DomRenderer(); + await ui.webOnlyInitializePlatform( + assetManager: WebOnlyMockAssetManager()); + expect(windowFlutterCanvasKit, isNotNull); + + final CanvasKit? firstCanvasKitInstance = windowFlutterCanvasKit; + + // Triggers a reset of the CanvasKit script element. + DomRenderer(); + await ui.webOnlyInitializePlatform( + assetManager: WebOnlyMockAssetManager()); + // The instance is the same. + expect(firstCanvasKitInstance, windowFlutterCanvasKit); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/image_test.dart b/lib/web_ui/test/canvaskit/image_test.dart index 11ed6e2a4b741..8d17b02e69bf3 100644 --- a/lib/web_ui/test/canvaskit/image_test.dart +++ b/lib/web_ui/test/canvaskit/image_test.dart @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +import 'dart:html' as html; +import 'dart:typed_data'; + import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; +import '../matchers.dart'; import 'common.dart'; import 'test_data.dart'; @@ -17,43 +20,350 @@ void main() { void testMain() { group('CanvasKit image', () { - setUpAll(() async { - await ui.webOnlyInitializePlatform(); - }); + setUpCanvasKitTest(); - test('CkAnimatedImage toString', () { - final SkAnimatedImage skAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); - final CkAnimatedImage image = CkAnimatedImage(skAnimatedImage); - expect(image.toString(), '[1×1]'); - image.dispose(); + tearDown(() { + debugRestoreHttpRequestFactory(); }); test('CkAnimatedImage can be explicitly disposed of', () { - final SkAnimatedImage skAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); - final CkAnimatedImage image = CkAnimatedImage(skAnimatedImage); - expect(image.box.isDeleted, false); - image.dispose(); - expect(image.box.isDeleted, true); + final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kTransparentImage, 'test'); + expect(image.debugDisposed, isFalse); image.dispose(); - expect(image.box.isDeleted, true); + expect(image.debugDisposed, isTrue); + + // Disallow usage after disposal + expect(() => image.frameCount, throwsAssertionError); + expect(() => image.repetitionCount, throwsAssertionError); + expect(() => image.getNextFrame(), throwsAssertionError); + + // Disallow double-dispose. + expect(() => image.dispose(), throwsAssertionError); + testCollector.collectNow(); + }); + + test('CkAnimatedImage remembers last animation position after resurrection', () async { + browserSupportsFinalizationRegistry = false; + + Future expectFrameData(ui.FrameInfo frame, List data) async { + final ByteData frameData = (await frame.image.toByteData())!; + expect(frameData.buffer.asUint8List(), Uint8List.fromList(data)); + } + + final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test'); + expect(image.frameCount, 3); + expect(image.repetitionCount, -1); + + final ui.FrameInfo frame1 = await image.getNextFrame(); + expectFrameData(frame1, [0, 255, 0, 255]); + final ui.FrameInfo frame2 = await image.getNextFrame(); + expectFrameData(frame2, [0, 0, 255, 255]); + + // Pretend that the image is temporarily deleted. + image.delete(); + image.didDelete(); + + // Check that we got the 3rd frame after resurrection. + final ui.FrameInfo frame3 = await image.getNextFrame(); + expectFrameData(frame3, [255, 0, 0, 255]); + + testCollector.collectNow(); }); test('CkImage toString', () { - final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame(); + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)! + .makeImageAtCurrentFrame(); final CkImage image = CkImage(skImage); expect(image.toString(), '[1×1]'); image.dispose(); + testCollector.collectNow(); }); test('CkImage can be explicitly disposed of', () { - final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame(); + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)! + .makeImageAtCurrentFrame(); final CkImage image = CkImage(skImage); - expect(image.box.isDeleted, false); + expect(image.debugDisposed, isFalse); + expect(image.box.isDeletedPermanently, isFalse); image.dispose(); - expect(image.box.isDeleted, true); + expect(image.debugDisposed, isTrue); + expect(image.box.isDeletedPermanently, isTrue); + + // Disallow double-dispose. + expect(() => image.dispose(), throwsAssertionError); + testCollector.collectNow(); + }); + + test('CkImage can be explicitly disposed of when cloned', () async { + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)! + .makeImageAtCurrentFrame(); + final CkImage image = CkImage(skImage); + final SkiaObjectBox box = image.box; + expect(box.refCount, 1); + expect(box.debugGetStackTraces().length, 1); + + final CkImage clone = image.clone(); + expect(box.refCount, 2); + expect(box.debugGetStackTraces().length, 2); + + expect(image.isCloneOf(clone), isTrue); + expect(box.isDeletedPermanently, isFalse); + + testCollector.collectNow(); + expect(skImage.isDeleted(), isFalse); image.dispose(); - expect(image.box.isDeleted, true); + expect(box.refCount, 1); + expect(box.isDeletedPermanently, isFalse); + + testCollector.collectNow(); + expect(skImage.isDeleted(), isFalse); + clone.dispose(); + expect(box.refCount, 0); + expect(box.isDeletedPermanently, isTrue); + + testCollector.collectNow(); + expect(skImage.isDeleted(), isTrue); + expect(box.debugGetStackTraces().length, 0); + testCollector.collectNow(); + }); + + test('CkImage toByteData', () async { + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)! + .makeImageAtCurrentFrame(); + final CkImage image = CkImage(skImage); + expect((await image.toByteData()).lengthInBytes, greaterThan(0)); + expect((await image.toByteData(format: ui.ImageByteFormat.png)).lengthInBytes, greaterThan(0)); + testCollector.collectNow(); + }); + + // Regression test for https://github.com/flutter/flutter/issues/72469 + test('CkImage can be resurrected', () { + browserSupportsFinalizationRegistry = false; + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)! + .makeImageAtCurrentFrame(); + final CkImage image = CkImage(skImage); + expect(image.box.rawSkiaObject, isNotNull); + + // Pretend that the image is temporarily deleted. + image.box.delete(); + image.box.didDelete(); + expect(image.box.rawSkiaObject, isNull); + + // Attempting to access the skia object here would previously throw + // "Stack Overflow" in Safari. + expect(image.box.skiaObject, isNotNull); + testCollector.collectNow(); + }); + + test('skiaInstantiateWebImageCodec loads an image from the network', + () async { + httpRequestFactory = () { + return TestHttpRequest() + ..status = 200 + ..onLoad = Stream.fromIterable([ + html.ProgressEvent('test error'), + ]) + ..response = kTransparentImage.buffer; + }; + final ui.Codec codec = await skiaInstantiateWebImageCodec('http://image-server.com/picture.jpg', null); + expect(codec.frameCount, 1); + final ui.Image image = (await codec.getNextFrame()).image; + expect(image.height, 1); + expect(image.width, 1); + testCollector.collectNow(); + }); + + test('skiaInstantiateWebImageCodec throws exception on request error', + () async { + httpRequestFactory = () { + return TestHttpRequest() + ..onError = Stream.fromIterable([ + html.ProgressEvent('test error'), + ]); + }; + try { + await skiaInstantiateWebImageCodec('url-does-not-matter', null); + fail('Expected to throw'); + } on ImageCodecException catch (exception) { + expect( + exception.toString(), + 'ImageCodecException: Failed to load network image.\n' + 'Image URL: url-does-not-matter\n' + 'Trying to load an image from another domain? Find answers at:\n' + 'https://flutter.dev/docs/development/platform-integration/web-images', + ); + } + testCollector.collectNow(); + }); + + test('skiaInstantiateWebImageCodec throws exception on HTTP error', + () async { + try { + await skiaInstantiateWebImageCodec('/does-not-exist.jpg', null); + fail('Expected to throw'); + } on ImageCodecException catch (exception) { + expect( + exception.toString(), + 'ImageCodecException: Failed to load network image.\n' + 'Image URL: /does-not-exist.jpg\n' + 'Server response code: 404', + ); + } + testCollector.collectNow(); + }); + + test('skiaInstantiateWebImageCodec includes URL in the error for malformed image', + () async { + httpRequestFactory = () { + return TestHttpRequest() + ..status = 200 + ..onLoad = Stream.fromIterable([ + html.ProgressEvent('test error'), + ]) + ..response = Uint8List(0).buffer; + }; + try { + await skiaInstantiateWebImageCodec('http://image-server.com/picture.jpg', null); + fail('Expected to throw'); + } on ImageCodecException catch (exception) { + expect( + exception.toString(), + 'ImageCodecException: Failed to decode image data.\n' + 'Image source: http://image-server.com/picture.jpg', + ); + } + testCollector.collectNow(); + }); + + test('Reports error when failing to decode image data', () async { + try { + await ui.instantiateImageCodec(Uint8List(0)); + fail('Expected to throw'); + } on ImageCodecException catch (exception) { + expect( + exception.toString(), + 'ImageCodecException: Failed to decode image data.\n' + 'Image source: encoded image bytes' + ); + } }); - // TODO: https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } + +class TestHttpRequest implements html.HttpRequest { + @override + String responseType = 'invalid'; + + @override + int? timeout = 10; + + @override + bool? withCredentials = false; + + @override + void abort() { + throw UnimplementedError(); + } + + @override + void addEventListener(String type, html.EventListener? listener, [bool? useCapture]) { + throw UnimplementedError(); + } + + @override + bool dispatchEvent(html.Event event) { + throw UnimplementedError(); + } + + @override + String getAllResponseHeaders() { + throw UnimplementedError(); + } + + @override + String getResponseHeader(String name) { + throw UnimplementedError(); + } + + @override + html.Events get on => throw UnimplementedError(); + + @override + Stream get onAbort => throw UnimplementedError(); + + @override + Stream onError = Stream.fromIterable([]); + + @override + Stream onLoad = Stream.fromIterable([]); + + @override + Stream get onLoadEnd => throw UnimplementedError(); + + @override + Stream get onLoadStart => throw UnimplementedError(); + + @override + Stream get onProgress => throw UnimplementedError(); + + @override + Stream get onReadyStateChange => throw UnimplementedError(); + + @override + Stream get onTimeout => throw UnimplementedError(); + + @override + void open(String method, String url, {bool? async, String? user, String? password}) {} + + @override + void overrideMimeType(String mime) { + throw UnimplementedError(); + } + + @override + int get readyState => throw UnimplementedError(); + + @override + void removeEventListener(String type, html.EventListener? listener, [bool? useCapture]) { + throw UnimplementedError(); + } + + @override + dynamic response; + + @override + Map get responseHeaders => throw UnimplementedError(); + + @override + String get responseText => throw UnimplementedError(); + + @override + String get responseUrl => throw UnimplementedError(); + + @override + html.Document get responseXml => throw UnimplementedError(); + + @override + void send([dynamic bodyOrData]) { + } + + @override + void setRequestHeader(String name, String value) { + throw UnimplementedError(); + } + + @override + int status = -1; + + @override + String get statusText => throw UnimplementedError(); + + @override + html.HttpRequestUpload get upload => throw UnimplementedError(); +} diff --git a/lib/web_ui/test/canvaskit/initialization_test.dart b/lib/web_ui/test/canvaskit/initialization_test.dart new file mode 100644 index 0000000000000..49c614b07537f --- /dev/null +++ b/lib/web_ui/test/canvaskit/initialization_test.dart @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CanvasKit', () { + setUpCanvasKitTest(); + + test('populates flt-renderer and flt-build-mode', () { + DomRenderer(); + expect(html.document.body!.attributes['flt-renderer'], + 'canvaskit (requested explicitly)'); + expect(html.document.body!.attributes['flt-build-mode'], 'debug'); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/interval_tree_test.dart b/lib/web_ui/test/canvaskit/interval_tree_test.dart new file mode 100644 index 0000000000000..39eb3ff917fd4 --- /dev/null +++ b/lib/web_ui/test/canvaskit/interval_tree_test.dart @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$IntervalTree', () { + test('is balanced', () { + final Map> ranges = >{ + 'A': const [CodeunitRange(0, 5), CodeunitRange(6, 10)], + 'B': const [CodeunitRange(4, 6)], + }; + + // Should create a balanced 3-node tree with a root with a left and right + // child. + final IntervalTree tree = IntervalTree.createFromRanges(ranges); + final IntervalTreeNode root = tree.root; + expect(root.left, isNotNull); + expect(root.right, isNotNull); + expect(root.left!.left, isNull); + expect(root.left!.right, isNull); + expect(root.right!.left, isNull); + expect(root.right!.right, isNull); + + // Should create a balanced 15-node tree (4 layers deep). + final Map> ranges2 = >{ + 'A': const [ + CodeunitRange(1, 1), + CodeunitRange(2, 2), + CodeunitRange(3, 3), + CodeunitRange(4, 4), + CodeunitRange(5, 5), + CodeunitRange(6, 6), + CodeunitRange(7, 7), + CodeunitRange(8, 8), + CodeunitRange(9, 9), + CodeunitRange(10, 10), + CodeunitRange(11, 11), + CodeunitRange(12, 12), + CodeunitRange(13, 13), + CodeunitRange(14, 14), + CodeunitRange(15, 15), + ], + }; + + // Should create a balanced 3-node tree with a root with a left and right + // child. + final IntervalTree tree2 = IntervalTree.createFromRanges(ranges2); + final IntervalTreeNode root2 = tree2.root; + + expect(root2.left!.left!.left, isNotNull); + expect(root2.left!.left!.right, isNotNull); + expect(root2.left!.right!.left, isNotNull); + expect(root2.left!.right!.right, isNotNull); + expect(root2.right!.left!.left, isNotNull); + expect(root2.right!.left!.right, isNotNull); + expect(root2.right!.right!.left, isNotNull); + expect(root2.right!.right!.right, isNotNull); + }); + + test('finds values whose intervals overlap with a given point', () { + final Map> ranges = >{ + 'A': const [CodeunitRange(0, 5), CodeunitRange(7, 10)], + 'B': const [CodeunitRange(4, 6)], + }; + final IntervalTree tree = IntervalTree.createFromRanges(ranges); + + expect(tree.intersections(1), ['A']); + expect(tree.intersections(4), ['A', 'B']); + expect(tree.intersections(6), ['B']); + expect(tree.intersections(7), ['A']); + expect(tree.intersections(11), []); + }); + }); +} diff --git a/lib/web_ui/test/canvaskit/layer_test.dart b/lib/web_ui/test/canvaskit/layer_test.dart new file mode 100644 index 0000000000000..dfc4f7ea73349 --- /dev/null +++ b/lib/web_ui/test/canvaskit/layer_test.dart @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CanvasKit', () { + setUpCanvasKitTest(); + + // Regression test for https://github.com/flutter/flutter/issues/63715 + test('TransformLayer prerolls correctly', () async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + + final CkPicture picture = + paintPicture(const ui.Rect.fromLTRB(0, 0, 60, 60), (CkCanvas canvas) { + canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 60, 60), + CkPaint()..style = ui.PaintingStyle.fill); + }); + + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushClipRect(const ui.Rect.fromLTRB(15, 15, 30, 30)); + + // Intentionally use a perspective transform, which triggered the + // https://github.com/flutter/flutter/issues/63715 bug. + sb.pushTransform( + Float64List.fromList(Matrix4.identity().storage + ..[15] = 2, + )); + + sb.addPicture(ui.Offset.zero, picture); + final LayerTree layerTree = sb.build().layerTree; + dispatcher.rasterizer!.draw(layerTree); + final ClipRectEngineLayer clipRect = layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer; + expect(clipRect.paintBounds, const ui.Rect.fromLTRB(15, 15, 30, 30)); + + final TransformEngineLayer transform = clipRect.debugLayers.single as TransformEngineLayer; + expect(transform.paintBounds, const ui.Rect.fromLTRB(0, 0, 30, 30)); + }); + + test('can push a leaf layer without a container layer', () async { + final CkPictureRecorder recorder = CkPictureRecorder(); + recorder.beginRecording(ui.Rect.zero); + LayerSceneBuilder().addPicture(ui.Offset.zero, recorder.endRecording()); + }); + + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart b/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart new file mode 100644 index 0000000000000..352f8de8ffb7b --- /dev/null +++ b/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); + +Future matchPictureGolden(String goldenFile, CkPicture picture, + {bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPicture(ui.Offset.zero, picture); + dispatcher.rasterizer!.draw(sb.build().layerTree); + await matchGoldenFile(goldenFile, region: region, write: write); +} + +void testMain() { + group('Linear', () { + setUpCanvasKitTest(); + + test('is correctly rendered', () async { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + final CkGradientLinear gradient = CkGradientLinear( + ui.Offset(region.left + region.width / 4, region.height / 2), + ui.Offset(region.right - region.width / 8, region.height / 2), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0, + ], + ui.TileMode.clamp, + null); + + final CkPaint paint = CkPaint()..shader = gradient; + + canvas.drawRect(region, paint); + + await matchPictureGolden( + 'canvaskit_linear_gradient.png', + recorder.endRecording(), + ); + }); + + test('is correctly rendered when rotated', () async { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + final CkGradientLinear gradient = CkGradientLinear( + ui.Offset(region.left + region.width / 4, region.height / 2), + ui.Offset(region.right - region.width / 8, region.height / 2), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0, + ], + ui.TileMode.clamp, + Matrix4.rotationZ(math.pi / 6.0).storage); + + final CkPaint paint = CkPaint()..shader = gradient; + + canvas.drawRect(region, paint); + + await matchPictureGolden( + 'canvaskit_linear_gradient_rotated.png', + recorder.endRecording(), + ); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} diff --git a/lib/web_ui/test/canvaskit/path_metrics_test.dart b/lib/web_ui/test/canvaskit/path_metrics_test.dart deleted file mode 100644 index eece12c15b572..0000000000000 --- a/lib/web_ui/test/canvaskit/path_metrics_test.dart +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; - -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import 'common.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('Path Metrics', () { - setUpAll(() async { - await ui.webOnlyInitializePlatform(); - }); - - test('Using CanvasKit', () { - expect(experimentalUseSkia, true); - }); - - test(CkPathMetrics, () { - final ui.Path path = ui.Path(); - expect(path, isA()); - expect(path.computeMetrics().length, 0); - - path.addRect(ui.Rect.fromLTRB(0, 0, 10, 10)); - final ui.PathMetric metric = path.computeMetrics().single; - expect(metric.contourIndex, 0); - expect(metric.extractPath(0, 0.5).computeMetrics().length, 1); - - final ui.Tangent tangent1 = metric.getTangentForOffset(5); - expect(tangent1.position, ui.Offset(5, 0)); - expect(tangent1.vector, ui.Offset(1, 0)); - - final ui.Tangent tangent2 = metric.getTangentForOffset(15); - expect(tangent2.position, ui.Offset(10, 5)); - expect(tangent2.vector, ui.Offset(0, 1)); - - expect(metric.isClosed, true); - - path.addOval(ui.Rect.fromLTRB(10, 10, 100, 100)); - expect(path.computeMetrics().length, 2); - - // Path metrics can be iterated over multiple times. - final ui.PathMetrics metrics = path.computeMetrics(); - expect(metrics.toList().length, 2); - expect(metrics.toList().length, 2); - expect(metrics.toList().length, 2); - - // Can simultaneously iterate over multiple metrics from the same path. - final ui.PathMetrics metrics1 = path.computeMetrics(); - final ui.PathMetrics metrics2 = path.computeMetrics(); - final Iterator iter1 = metrics1.iterator; - final Iterator iter2 = metrics2.iterator; - expect(iter1.moveNext(), true); - expect(iter2.moveNext(), true); - expect(iter1.current, isNotNull); - expect(iter2.current, isNotNull); - expect(iter1.moveNext(), true); - expect(iter2.moveNext(), true); - expect(iter1.current, isNotNull); - expect(iter2.current, isNotNull); - expect(iter1.moveNext(), false); - expect(iter2.moveNext(), false); - expect(() => iter1.current, throwsRangeError); - expect(() => iter2.current, throwsRangeError); - }); - }, skip: isIosSafari); // TODO: https://github.com/flutter/flutter/issues/60040 -} diff --git a/lib/web_ui/test/canvaskit/path_test.dart b/lib/web_ui/test/canvaskit/path_test.dart new file mode 100644 index 0000000000000..40f93595678ab --- /dev/null +++ b/lib/web_ui/test/canvaskit/path_test.dart @@ -0,0 +1,181 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CkPath', () { + setUpCanvasKitTest(); + + test('Using CanvasKit', () { + expect(useCanvasKit, isTrue); + }); + + test(CkPathMetrics, () { + final ui.Path path = ui.Path(); + expect(path, isA()); + expect(path.computeMetrics().length, 0); + + path.addRect(const ui.Rect.fromLTRB(0, 0, 10, 10)); + final ui.PathMetric metric = path.computeMetrics().single; + expect(metric.contourIndex, 0); + expect(metric.extractPath(0, 0.5).computeMetrics().length, 1); + + final ui.Tangent tangent1 = metric.getTangentForOffset(5)!; + expect(tangent1.position, const ui.Offset(5, 0)); + expect(tangent1.vector, const ui.Offset(1, 0)); + + final ui.Tangent tangent2 = metric.getTangentForOffset(15)!; + expect(tangent2.position, const ui.Offset(10, 5)); + expect(tangent2.vector, const ui.Offset(0, 1)); + + expect(metric.isClosed, isTrue); + + path.addOval(const ui.Rect.fromLTRB(10, 10, 100, 100)); + expect(path.computeMetrics().length, 2); + + // Path metrics can be iterated over multiple times. + final ui.PathMetrics metrics = path.computeMetrics(); + expect(metrics.toList().length, 2); + expect(metrics.toList().length, 2); + expect(metrics.toList().length, 2); + + // Can simultaneously iterate over multiple metrics from the same path. + final ui.PathMetrics metrics1 = path.computeMetrics(); + final ui.PathMetrics metrics2 = path.computeMetrics(); + final Iterator iter1 = metrics1.iterator; + final Iterator iter2 = metrics2.iterator; + expect(iter1.moveNext(), isTrue); + expect(iter2.moveNext(), isTrue); + expect(iter1.current, isNotNull); + expect(iter2.current, isNotNull); + expect(iter1.moveNext(), isTrue); + expect(iter2.moveNext(), isTrue); + expect(iter1.current, isNotNull); + expect(iter2.current, isNotNull); + expect(iter1.moveNext(), isFalse); + expect(iter2.moveNext(), isFalse); + expect(() => iter1.current, throwsRangeError); + expect(() => iter2.current, throwsRangeError); + }); + + test('CkPath.reset', () { + final ui.Path path = ui.Path(); + expect(path, isA()); + path.addRect(const ui.Rect.fromLTRB(0, 0, 10, 10)); + expect(path.contains(const ui.Offset(5, 5)), isTrue); + + expect(path.fillType, ui.PathFillType.nonZero); + path.fillType = ui.PathFillType.evenOdd; + expect(path.fillType, ui.PathFillType.evenOdd); + + path.reset(); + expect(path.fillType, ui.PathFillType.nonZero); + expect(path.contains(const ui.Offset(5, 5)), isFalse); + }); + + test('CkPath resurrection', () { + const ui.Rect rect = ui.Rect.fromLTRB(0, 0, 10, 10); + final CkPath path = CkPath(); + path.addRect(rect); + path.delete(); + + final SkPath resurrectedCopy = path.resurrect(); + expect(fromSkRect(resurrectedCopy.getBounds()), rect); + }); + + test('Resurrect CkContourMeasure in the middle of iteration', () { + browserSupportsFinalizationRegistry = false; + final ui.Path path = ui.Path(); + expect(path, isA()); + path.addRect(const ui.Rect.fromLTRB(0, 0, 10, 10)); + path.addRect(const ui.Rect.fromLTRB(20, 20, 30, 30)); + path.addRect(const ui.Rect.fromLTRB(40, 40, 50, 50)); + + final ui.PathMetrics metrics = path.computeMetrics(); + final CkContourMeasureIter iterator = metrics.iterator as CkContourMeasureIter; + + expect(iterator.moveNext(), isTrue); + expect(iterator.current.contourIndex, 0); + expect(iterator.moveNext(), isTrue); + expect(iterator.current.contourIndex, 1); + + // Delete iterator in the middle of iteration + iterator.delete(); + iterator.rawSkiaObject = null; + + // Check that the iterator can continue from the last position. + expect(iterator.moveNext(), isTrue); + expect(iterator.current.contourIndex, 2); + expect(iterator.moveNext(), isFalse); + }); + + test('Resurrect CkContourMeasure', () { + browserSupportsFinalizationRegistry = false; + final ui.Path path = ui.Path(); + expect(path, isA()); + path.addRect(const ui.Rect.fromLTRB(0, 0, 10, 10)); + path.addRect(const ui.Rect.fromLTRB(20, 20, 30, 30)); + path.addRect(const ui.Rect.fromLTRB(40, 40, 50, 50)); + + final ui.PathMetrics metrics = path.computeMetrics(); + final CkContourMeasureIter iterator = metrics.iterator as CkContourMeasureIter; + + expect(iterator.moveNext(), isTrue); + final CkContourMeasure measure0 = iterator.current as CkContourMeasure; + expect(measure0.contourIndex, 0); + expect(measure0.extractPath(0, 15).getBounds(), const ui.Rect.fromLTRB(0, 0, 10, 5)); + + expect(iterator.moveNext(), isTrue); + final CkContourMeasure measure1 = iterator.current as CkContourMeasure; + expect(measure1.contourIndex, 1); + expect(measure1.extractPath(0, 15).getBounds(), const ui.Rect.fromLTRB(20, 20, 30, 25)); + + // Delete iterator and the measure in the middle of iteration + iterator.delete(); + iterator.rawSkiaObject = null; + measure0.delete(); + measure0.rawSkiaObject = null; + measure1.delete(); + measure1.rawSkiaObject = null; + + // Check that the measure is still value after resurrection. + expect(measure0.contourIndex, 0); + expect(measure0.extractPath(0, 15).getBounds(), const ui.Rect.fromLTRB(0, 0, 10, 5)); + expect(measure1.contourIndex, 1); + expect(measure1.extractPath(0, 15).getBounds(), const ui.Rect.fromLTRB(20, 20, 30, 25)); + }); + + test('Path.from', () { + const ui.Rect rect1 = ui.Rect.fromLTRB(0, 0, 10, 10); + const ui.Rect rect2 = ui.Rect.fromLTRB(10, 10, 20, 20); + + final ui.Path original = ui.Path(); + original.addRect(rect1); + expect(original, isA()); + expect(original.getBounds(), rect1); + + final ui.Path copy = ui.Path.from(original); + expect(copy, isA()); + expect(copy.getBounds(), rect1); + + // Test that when copy is mutated, the original is not affected + copy.addRect(rect2); + expect(original.getBounds(), rect1); + expect(copy.getBounds(), rect1.expandToInclude(rect2)); + }); + }, + skip: + isIosSafari); // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 +} diff --git a/lib/web_ui/test/canvaskit/picture_test.dart b/lib/web_ui/test/canvaskit/picture_test.dart new file mode 100644 index 0000000000000..250618a79605b --- /dev/null +++ b/lib/web_ui/test/canvaskit/picture_test.dart @@ -0,0 +1,69 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import '../matchers.dart'; +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CkPicture', () { + setUpCanvasKitTest(); + + group('in browsers that do not support FinalizationRegistry', () { + test('can be disposed of manually', () { + browserSupportsFinalizationRegistry = false; + + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + canvas.drawPaint(ui.Paint()); + final CkPicture picture = recorder.endRecording() as CkPicture; + expect(picture.rawSkiaObject, isNotNull); + expect(picture.debugIsDisposed, isFalse); + picture.dispose(); + expect(picture.rawSkiaObject, isNull); + expect(picture.debugIsDisposed, isTrue); + + // Emulate SkiaObjectCache deleting the picture + picture.delete(); + picture.didDelete(); + expect(picture.rawSkiaObject, isNull); + + // A Picture that's been disposed of can no longer be resurrected + expect(() => picture.resurrect(), throwsAssertionError); + expect(() => picture.toImage(10, 10), throwsAssertionError); + expect(() => picture.dispose(), throwsAssertionError); + }); + + test('can be deleted by SkiaObjectCache', () { + browserSupportsFinalizationRegistry = false; + + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + canvas.drawPaint(ui.Paint()); + final CkPicture picture = recorder.endRecording() as CkPicture; + expect(picture.rawSkiaObject, isNotNull); + + // Emulate SkiaObjectCache deleting the picture + picture.delete(); + picture.didDelete(); + expect(picture.rawSkiaObject, isNull); + + // Deletion is softer than disposal. An object may still be resurrected + // if it was deleted prematurely. + expect(picture.debugIsDisposed, isFalse); + expect(picture.resurrect(), isNotNull); + }); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart b/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart new file mode 100644 index 0000000000000..e13eff9584f92 --- /dev/null +++ b/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('PlatformDispatcher', () { + setUpCanvasKitTest(); + + test('responds to flutter/skia Skia.setResourceCacheMaxBytes', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/skia', + codec.encodeMethodCall(const MethodCall( + 'Skia.setResourceCacheMaxBytes', + 512 * 1000 * 1000, + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + [true], + ); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/scene_test.dart b/lib/web_ui/test/canvaskit/scene_test.dart new file mode 100644 index 0000000000000..70af8e7f74e0d --- /dev/null +++ b/lib/web_ui/test/canvaskit/scene_test.dart @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$LayerScene', () { + setUpAll(() async { + await ui.webOnlyInitializePlatform(); + }); + + test('toImage returns an image', () async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + expect(recorder, isA()); + + final ui.Canvas canvas = ui.Canvas(recorder); + expect(canvas, isA()); + + final ui.Paint paint = ui.Paint(); + expect(paint, isA()); + paint.color = const ui.Color.fromARGB(255, 255, 0, 0); + + // Draw a red circle. + canvas.drawCircle(const ui.Offset(20, 20), 10, paint); + + final ui.Picture picture = recorder.endRecording(); + expect(picture, isA()); + + final ui.SceneBuilder builder = ui.SceneBuilder(); + expect(builder, isA()); + + builder.pushOffset(0, 0); + builder.addPicture(const ui.Offset(0, 0), picture); + + final ui.Scene scene = builder.build(); + + final ui.Image sceneImage = await scene.toImage(100, 100); + expect(sceneImage, isA()); + }); + + test('pushColorFilter does not throw', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + expect(builder, isA()); + + builder.pushOffset(0, 0); + builder.pushColorFilter(const ui.ColorFilter.srgbToLinearGamma()); + + final ui.Scene scene = builder.build(); + expect(scene, isNotNull); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart b/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart new file mode 100644 index 0000000000000..9d282ab1316a4 --- /dev/null +++ b/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart @@ -0,0 +1,164 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); + +Future matchSceneGolden(String goldenFile, LayerScene scene, + {bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + dispatcher.rasterizer!.draw(scene.layerTree); + await matchGoldenFile(goldenFile, region: region, write: write); +} + +void testMain() { + group('ShaderMask', () { + setUpCanvasKitTest(); + + test('Renders sweep gradient with color blend', () async { + final LayerSceneBuilder builder = LayerSceneBuilder(); + + builder.pushOffset(0, 0); + + // Draw a red circle and apply it to the scene. + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + canvas.drawCircle( + const ui.Offset(425, 125), + 50, + CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture redCircle = recorder.endRecording(); + + builder.addPicture(ui.Offset.zero, redCircle); + + final CkGradientSweep shader = CkGradientSweep( + const ui.Offset(250, 125), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0, + ], + ui.TileMode.clamp, + -(math.pi / 2), + math.pi * 2 - (math.pi / 2), + null); + + final ui.Path clipPath = ui.Path() + ..addOval(const ui.Rect.fromLTWH(25, 75, 100, 100)); + builder.pushClipPath(clipPath); + + // Apply a shader mask. + builder.pushShaderMask(shader, const ui.Rect.fromLTRB(0, 0, 200, 250), + ui.BlendMode.color); + + // Draw another red circle and apply it to the scene. + // This one should be grey since we have the color filter. + final CkPictureRecorder recorder2 = CkPictureRecorder(); + final CkCanvas canvas2 = recorder2.beginRecording(region); + + canvas2.drawRect(const ui.Rect.fromLTWH(25, 75, 100, 100), + CkPaint()..color = const ui.Color.fromARGB(255, 0, 255, 0), + ); + + final CkPicture sweepCircle = recorder2.endRecording(); + + builder.addPicture(ui.Offset.zero, sweepCircle); + + await matchSceneGolden('canvaskit_shadermask_linear.png', + builder.build()); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/78959 + test('Renders sweep gradient with color blend translated', () async { + final LayerSceneBuilder builder = LayerSceneBuilder(); + + builder.pushOffset(0, 0); + + // Draw a red circle and apply it to the scene. + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + canvas.drawCircle( + const ui.Offset(425, 125), + 50, + CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); + final CkPicture redCircle = recorder.endRecording(); + + builder.addPicture(ui.Offset.zero, redCircle); + + final CkGradientSweep shader = CkGradientSweep( + const ui.Offset(250, 125), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0, + ], + ui.TileMode.clamp, + -(math.pi / 2), + math.pi * 2 - (math.pi / 2), + null); + + final ui.Path clipPath = ui.Path() + ..addOval(const ui.Rect.fromLTWH(25, 75, 100, 100)); + builder.pushClipPath(clipPath); + + // Apply a shader mask. + builder.pushShaderMask(shader, const ui.Rect.fromLTRB(50, 50, 200, 250), + ui.BlendMode.color); + + // Draw another red circle and apply it to the scene. + // This one should be grey since we have the color filter. + final CkPictureRecorder recorder2 = CkPictureRecorder(); + final CkCanvas canvas2 = recorder2.beginRecording(region); + + canvas2.drawRect(const ui.Rect.fromLTWH(25, 75, 100, 100), + CkPaint()..color = const ui.Color.fromARGB(255, 0, 255, 0), + ); + + final CkPicture sweepCircle = recorder2.endRecording(); + + builder.addPicture(ui.Offset.zero, sweepCircle); + + await matchSceneGolden('canvaskit_shadermask_linear_translated.png', + builder.build()); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} diff --git a/lib/web_ui/test/canvaskit/shader_test.dart b/lib/web_ui/test/canvaskit/shader_test.dart index dfa3f2dd12b04..2f44ed7e7a837 100644 --- a/lib/web_ui/test/canvaskit/shader_test.dart +++ b/lib/web_ui/test/canvaskit/shader_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; @@ -19,15 +18,13 @@ void main() { void testMain() { group('CanvasKit shaders', () { - setUpAll(() async { - await ui.webOnlyInitializePlatform(); - }); + setUpCanvasKitTest(); test('Sweep gradient', () { final CkGradientSweep gradient = ui.Gradient.sweep( ui.Offset.zero, testColors, - ); + ) as CkGradientSweep; expect(gradient.createDefault(), isNotNull); }); @@ -36,7 +33,7 @@ void testMain() { ui.Offset.zero, const ui.Offset(0, 1), testColors, - ); + ) as CkGradientLinear; expect(gradient.createDefault(), isNotNull); }); @@ -45,7 +42,7 @@ void testMain() { ui.Offset.zero, 10, testColors, - ); + ) as CkGradientRadial; expect(gradient.createDefault(), isNotNull); }); @@ -59,22 +56,22 @@ void testMain() { null, const ui.Offset(10, 10), 40, - ); + ) as CkGradientConical; expect(gradient.createDefault(), isNotNull); }); test('Image shader', () { - final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame(); + final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!.makeImageAtCurrentFrame(); final CkImage image = CkImage(skImage); final CkImageShader imageShader = ui.ImageShader( image, ui.TileMode.clamp, ui.TileMode.repeated, Float64List.fromList(Matrix4.diagonal3Values(1, 2, 3).storage), - ); + ) as CkImageShader; expect(imageShader, isA()); }); - // TODO: https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } diff --git a/lib/web_ui/test/canvaskit/skia_font_collection_test.dart b/lib/web_ui/test/canvaskit/skia_font_collection_test.dart new file mode 100644 index 0000000000000..b69a778d1ebb7 --- /dev/null +++ b/lib/web_ui/test/canvaskit/skia_font_collection_test.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$SkiaFontCollection', () { + final List warnings = []; + late void Function(String) oldPrintWarning; + + setUpAll(() async { + await initializeCanvasKit(); + oldPrintWarning = printWarning; + printWarning = (String warning) { + warnings.add(warning); + }; + }); + + tearDownAll(() { + printWarning = oldPrintWarning; + }); + + setUp(() { + warnings.clear(); + }); + + test('logs no warnings with the default mock asset manager', () { + final SkiaFontCollection fontCollection = SkiaFontCollection(); + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + expect(fontCollection.registerFonts(mockAssetManager), completes); + expect(fontCollection.ensureFontsLoaded(), completes); + expect(warnings, isEmpty); + }); + + test('logs a warning if one of the registered fonts is invalid', () async { + final SkiaFontCollection fontCollection = SkiaFontCollection(); + final WebOnlyMockAssetManager mockAssetManager = + WebOnlyMockAssetManager(); + mockAssetManager.defaultFontManifest = ''' +[ + { + "family":"Roboto", + "fonts":[{"asset":"packages/ui/assets/Roboto-Regular.ttf"}] + }, + { + "family": "BrokenFont", + "fonts":[{"asset":"packages/bogus/BrokenFont.ttf"}] + } + ] + '''; + // It should complete without error, but emit a warning about BrokenFont. + await fontCollection.registerFonts(mockAssetManager); + await fontCollection.ensureFontsLoaded(); + expect( + warnings, + containsAllInOrder( + [ + 'Failed to load font BrokenFont at packages/bogus/BrokenFont.ttf', + 'Verify that packages/bogus/BrokenFont.ttf contains a valid font.', + ], + ), + ); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart b/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart index 08e0cb3d55837..f1c092e5d55ee 100644 --- a/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart +++ b/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart @@ -2,15 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 - -import 'package:mockito/mockito.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart' as ui; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; +import '../matchers.dart'; +import '../spy.dart'; import 'common.dart'; void main() { @@ -20,36 +19,25 @@ void main() { void testMain() { group('skia_objects_cache', () { _tests(); - // TODO: https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } void _tests() { SkiaObjects.maximumCacheSize = 4; - bool originalBrowserSupportsFinalizationRegistry; - setUpAll(() async { - await ui.webOnlyInitializePlatform(); + setUpCanvasKitTest(); + setUp(() async { // Pretend the browser does not support FinalizationRegistry so we can test the // resurrection logic. - originalBrowserSupportsFinalizationRegistry = browserSupportsFinalizationRegistry; browserSupportsFinalizationRegistry = false; }); - tearDownAll(() { - browserSupportsFinalizationRegistry = originalBrowserSupportsFinalizationRegistry; - }); - group(ManagedSkiaObject, () { test('implements create, cache, delete, resurrect, delete lifecycle', () { - int addPostFrameCallbackCount = 0; - - MockRasterizer mockRasterizer = MockRasterizer(); - when(mockRasterizer.addPostFrameCallback(any)).thenAnswer((_) { - addPostFrameCallbackCount++; - }); - window.rasterizer = mockRasterizer; + final FakeRasterizer fakeRasterizer = FakeRasterizer(); + EnginePlatformDispatcher.instance.rasterizer = fakeRasterizer; // Trigger first create final TestSkiaObject testObject = TestSkiaObject(); @@ -69,7 +57,7 @@ void _tests() { // Trigger first delete SkiaObjects.postFrameCleanUp(); expect(SkiaObjects.resurrectableObjects, isEmpty); - expect(addPostFrameCallbackCount, 1); + expect(fakeRasterizer.addPostFrameCallbackCount, 1); expect(testObject.createDefaultCount, 1); expect(testObject.resurrectCount, 0); expect(testObject.deleteCount, 1); @@ -79,7 +67,7 @@ void _tests() { expect(skiaObject2, isNotNull); expect(skiaObject2, isNot(same(skiaObject1))); expect(SkiaObjects.resurrectableObjects.single, testObject); - expect(addPostFrameCallbackCount, 1); + expect(fakeRasterizer.addPostFrameCallbackCount, 1); expect(testObject.createDefaultCount, 1); expect(testObject.resurrectCount, 1); expect(testObject.deleteCount, 1); @@ -87,18 +75,18 @@ void _tests() { // Trigger final delete SkiaObjects.postFrameCleanUp(); expect(SkiaObjects.resurrectableObjects, isEmpty); - expect(addPostFrameCallbackCount, 1); + expect(fakeRasterizer.addPostFrameCallbackCount, 1); expect(testObject.createDefaultCount, 1); expect(testObject.resurrectCount, 1); expect(testObject.deleteCount, 2); }); test('is added to SkiaObjects cache if expensive', () { - TestSkiaObject object1 = TestSkiaObject(isExpensive: true); + final TestSkiaObject object1 = TestSkiaObject(isExpensive: true); expect(SkiaObjects.expensiveCache.length, 1); expect(SkiaObjects.expensiveCache.debugContains(object1), isTrue); - TestSkiaObject object2 = TestSkiaObject(isExpensive: true); + final TestSkiaObject object2 = TestSkiaObject(isExpensive: true); expect(SkiaObjects.expensiveCache.length, 2); expect(SkiaObjects.expensiveCache.debugContains(object2), isTrue); @@ -123,48 +111,268 @@ void _tests() { }); }); - group(OneShotSkiaObject, () { - test('is added to SkiaObjects cache', () { - TestOneShotSkiaObject.deleteCount = 0; - OneShotSkiaObject object1 = TestOneShotSkiaObject(); - expect(SkiaObjects.oneShotCache.length, 1); - expect(SkiaObjects.oneShotCache.debugContains(object1), isTrue); + group(SkiaObjectBox, () { + test('Records stack traces and respects refcounts', () async { + final ZoneSpy spy = ZoneSpy(); + spy.run(() { + Instrumentation.enabled = true; + TestSkDeletable.deleteCount = 0; + TestBoxWrapper.resurrectCount = 0; + final TestBoxWrapper original = TestBoxWrapper(); + + expect(original.box.debugGetStackTraces().length, 1); + expect(original.box.refCount, 1); + expect(original.box.isDeletedPermanently, isFalse); + + final TestBoxWrapper clone = original.clone(); + expect(clone.box, same(original.box)); + expect(clone.box.debugGetStackTraces().length, 2); + expect(clone.box.refCount, 2); + expect(original.box.debugGetStackTraces().length, 2); + expect(original.box.refCount, 2); + expect(original.box.isDeletedPermanently, isFalse); + + original.dispose(); + + testCollector.collectNow(); + expect(TestSkDeletable.deleteCount, 0); + + spy.fakeAsync.elapse(const Duration(seconds: 2)); + expect( + spy.printLog, + [ + 'Engine counters:\n TestSkDeletable created: 1\n' + ], + ); + + expect(clone.box.debugGetStackTraces().length, 1); + expect(clone.box.refCount, 1); + expect(original.box.debugGetStackTraces().length, 1); + expect(original.box.refCount, 1); + + clone.dispose(); + expect(clone.box.debugGetStackTraces().length, 0); + expect(clone.box.refCount, 0); + expect(original.box.debugGetStackTraces().length, 0); + expect(original.box.refCount, 0); + expect(original.box.isDeletedPermanently, isTrue); + + testCollector.collectNow(); + expect(TestSkDeletable.deleteCount, 1); + expect(TestBoxWrapper.resurrectCount, 0); + + expect(() => clone.box.unref(clone), throwsAssertionError); + spy.printLog.clear(); + spy.fakeAsync.elapse(const Duration(seconds: 2)); + expect( + spy.printLog, + [ + 'Engine counters:\n TestSkDeletable created: 1\n TestSkDeletable deleted: 1\n' + ], + ); + Instrumentation.enabled = false; + }); + }); - OneShotSkiaObject object2 = TestOneShotSkiaObject(); - expect(SkiaObjects.oneShotCache.length, 2); - expect(SkiaObjects.oneShotCache.debugContains(object2), isTrue); + test('Can resurrect Skia objects', () async { + TestSkDeletable.deleteCount = 0; + TestBoxWrapper.resurrectCount = 0; + final TestBoxWrapper object = TestBoxWrapper(); + expect(TestSkDeletable.deleteCount, 0); + expect(TestBoxWrapper.resurrectCount, 0); + + // Test 3 cycles of delete/resurrect. + for (int i = 0; i < 3; i++) { + object.box.delete(); + object.box.didDelete(); + expect(TestSkDeletable.deleteCount, i + 1); + expect(TestBoxWrapper.resurrectCount, i); + expect(object.box.isDeletedTemporarily, isTrue); + expect(object.box.isDeletedPermanently, isFalse); + + expect(object.box.skiaObject, isNotNull); + expect(TestSkDeletable.deleteCount, i + 1); + expect(TestBoxWrapper.resurrectCount, i + 1); + expect(object.box.isDeletedTemporarily, isFalse); + expect(object.box.isDeletedPermanently, isFalse); + } + + object.dispose(); + expect(object.box.isDeletedPermanently, isTrue); + }); - SkiaObjects.postFrameCleanUp(); - expect(SkiaObjects.oneShotCache.length, 2); - expect(SkiaObjects.oneShotCache.debugContains(object1), isTrue); - expect(SkiaObjects.oneShotCache.debugContains(object2), isTrue); - - // Add 3 more objects to the cache to overflow it. - TestOneShotSkiaObject(); - TestOneShotSkiaObject(); - TestOneShotSkiaObject(); - expect(SkiaObjects.oneShotCache.length, 5); - expect(SkiaObjects.cachesToResize.length, 1); + test('Can dispose temporarily deleted object', () async { + TestSkDeletable.deleteCount = 0; + TestBoxWrapper.resurrectCount = 0; + final TestBoxWrapper object = TestBoxWrapper(); + expect(TestSkDeletable.deleteCount, 0); + expect(TestBoxWrapper.resurrectCount, 0); + + object.box.delete(); + object.box.didDelete(); + expect(TestSkDeletable.deleteCount, 1); + expect(TestBoxWrapper.resurrectCount, 0); + expect(object.box.isDeletedTemporarily, isTrue); + expect(object.box.isDeletedPermanently, isFalse); + + object.dispose(); + expect(object.box.isDeletedPermanently, isTrue); + }); + }); - SkiaObjects.postFrameCleanUp(); - expect(TestOneShotSkiaObject.deleteCount, 2); - expect(SkiaObjects.oneShotCache.length, 3); - expect(SkiaObjects.oneShotCache.debugContains(object1), isFalse); - expect(SkiaObjects.oneShotCache.debugContains(object2), isFalse); + group('$SynchronousSkiaObjectCache', () { + test('is initialized empty', () { + expect(SynchronousSkiaObjectCache(10), hasLength(0)); + }); + + test('adds objects', () { + final SynchronousSkiaObjectCache cache = SynchronousSkiaObjectCache(2); + cache.add(TestSelfManagedObject()); + expect(cache, hasLength(1)); + cache.add(TestSelfManagedObject()); + expect(cache, hasLength(2)); + }); + + test('forbids adding the same object twice', () { + final SynchronousSkiaObjectCache cache = SynchronousSkiaObjectCache(2); + final TestSelfManagedObject object = TestSelfManagedObject(); + cache.add(object); + expect(cache, hasLength(1)); + expect(() => cache.add(object), throwsAssertionError); + }); + + void expectObjectInCache( + SynchronousSkiaObjectCache cache, + TestSelfManagedObject object, + ) { + expect(cache.debugContains(object), isTrue); + expect(object._skiaObject, isNotNull); + } + + void expectObjectNotInCache( + SynchronousSkiaObjectCache cache, + TestSelfManagedObject object, + ) { + expect(cache.debugContains(object), isFalse); + expect(object._skiaObject, isNull); + } + + test('respects maximumSize', () { + final SynchronousSkiaObjectCache cache = SynchronousSkiaObjectCache(2); + final TestSelfManagedObject object1 = TestSelfManagedObject(); + final TestSelfManagedObject object2 = TestSelfManagedObject(); + final TestSelfManagedObject object3 = TestSelfManagedObject(); + final TestSelfManagedObject object4 = TestSelfManagedObject(); + + cache.add(object1); + expect(cache, hasLength(1)); + expectObjectInCache(cache, object1); + + cache.add(object2); + expect(cache, hasLength(2)); + expectObjectInCache(cache, object1); + expectObjectInCache(cache, object2); + + cache.add(object3); + expect(cache, hasLength(2)); + expectObjectNotInCache(cache, object1); + expectObjectInCache(cache, object2); + expectObjectInCache(cache, object3); + + cache.add(object4); + expect(cache, hasLength(2)); + expectObjectNotInCache(cache, object1); + expectObjectNotInCache(cache, object2); + expectObjectInCache(cache, object3); + expectObjectInCache(cache, object4); + }); + + test('uses RLU strategy', () { + final SynchronousSkiaObjectCache cache = SynchronousSkiaObjectCache(2); + final TestSelfManagedObject object1 = TestSelfManagedObject(); + final TestSelfManagedObject object2 = TestSelfManagedObject(); + final TestSelfManagedObject object3 = TestSelfManagedObject(); + final TestSelfManagedObject object4 = TestSelfManagedObject(); + + cache.add(object1); + expectObjectInCache(cache, object1); + cache.add(object2); + expectObjectInCache(cache, object2); + cache.add(object3); + expectObjectInCache(cache, object3); + expectObjectNotInCache(cache, object1); + + cache.markUsed(object2); + cache.add(object4); + expectObjectInCache(cache, object2); + expectObjectNotInCache(cache, object3); + expectObjectInCache(cache, object4); }); }); } -class TestOneShotSkiaObject extends OneShotSkiaObject { +/// A simple class that wraps a [SkiaObjectBox]. +/// +/// Can be [clone]d such that the clones share the same ref counted box. +class TestBoxWrapper implements StackTraceDebugger { + static int resurrectCount = 0; + + TestBoxWrapper() { + if (assertionsEnabled) { + _debugStackTrace = StackTrace.current; + } + box = SkiaObjectBox.resurrectable( + this, TestSkDeletable(), () { + resurrectCount += 1; + return TestSkDeletable(); + }); + } + + TestBoxWrapper.cloneOf(this.box) { + if (assertionsEnabled) { + _debugStackTrace = StackTrace.current; + } + box.ref(this); + } + + @override + StackTrace get debugStackTrace => _debugStackTrace; + late StackTrace _debugStackTrace; + + late SkiaObjectBox box; + + void dispose() { + box.unref(this); + } + + TestBoxWrapper clone() => TestBoxWrapper.cloneOf(box); +} + +class TestSkDeletable implements SkDeletable { static int deleteCount = 0; - TestOneShotSkiaObject() : super(SkPaint()); + @override + bool isDeleted() => _isDeleted; + bool _isDeleted = false; @override void delete() { - rawSkiaObject?.delete(); + expect(_isDeleted, isFalse, + reason: + 'CanvasKit does not allow deleting the same object more than once.'); + _isDeleted = true; deleteCount++; } + + @override + JsConstructor get constructor => TestJsConstructor('TestSkDeletable'); +} + +class TestJsConstructor implements JsConstructor { + TestJsConstructor(this.name); + + @override + final String name; } class TestSkiaObject extends ManagedSkiaObject { @@ -198,4 +406,46 @@ class TestSkiaObject extends ManagedSkiaObject { bool get isResurrectionExpensive => isExpensive; } -class MockRasterizer extends Mock implements Rasterizer {} +class FakeRasterizer implements Rasterizer { + int addPostFrameCallbackCount = 0; + + @override + void addPostFrameCallback(VoidCallback callback) { + addPostFrameCallbackCount++; + } + + @override + CompositorContext get context => throw UnimplementedError(); + + @override + void draw(LayerTree layerTree) { + throw UnimplementedError(); + } + + @override + void setSkiaResourceCacheMaxBytes(int bytes) { + throw UnimplementedError(); + } + + @override + void debugRunPostFrameCallbacks() { + throw UnimplementedError(); + } +} + +class TestSelfManagedObject extends SkiaObject { + TestSkDeletable? _skiaObject = TestSkDeletable(); + + @override + void delete() { + _skiaObject!.delete(); + } + + @override + void didDelete() { + _skiaObject = null; + } + + @override + TestSkDeletable get skiaObject => throw UnimplementedError(); +} diff --git a/lib/web_ui/test/canvaskit/surface_factory_test.dart b/lib/web_ui/test/canvaskit/surface_factory_test.dart new file mode 100644 index 0000000000000..77b03269ad03e --- /dev/null +++ b/lib/web_ui/test/canvaskit/surface_factory_test.dart @@ -0,0 +1,81 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import '../matchers.dart'; +import 'common.dart'; + +const MethodCodec codec = StandardMethodCodec(); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('$SurfaceFactory', () { + setUpCanvasKitTest(); + + test('cannot be created with size less than 2', () { + expect(() => SurfaceFactory(-1), throwsAssertionError); + expect(() => SurfaceFactory(0), throwsAssertionError); + expect(() => SurfaceFactory(1), throwsAssertionError); + expect(SurfaceFactory(2), isNotNull); + }); + + test('getSurface', () { + final SurfaceFactory factory = SurfaceFactory(3); + expect(factory.baseSurface, isNotNull); + expect(factory.backupSurface, isNotNull); + expect(factory.baseSurface, isNot(equals(factory.backupSurface))); + + expect(factory.debugSurfaceCount, equals(2)); + + // Get a surface from the factory, it should be unique. + final Surface newSurface = factory.getSurface(); + expect(newSurface, isNot(equals(factory.baseSurface))); + expect(newSurface, isNot(equals(factory.backupSurface))); + + expect(factory.debugSurfaceCount, equals(3)); + + // Get another surface from the factory. Now we are at maximum capacity, + // so it should return the backup surface. + final Surface anotherSurface = factory.getSurface(); + expect(anotherSurface, isNot(equals(factory.baseSurface))); + expect(anotherSurface, equals(factory.backupSurface)); + + expect(factory.debugSurfaceCount, equals(3)); + }); + + test('releaseSurface', () { + final SurfaceFactory factory = SurfaceFactory(3); + + // Create a new surface and immediately release it. + final Surface surface = factory.getSurface(); + factory.releaseSurface(surface); + + // If we create a new surface, it should be the same as the one we + // just created. + final Surface newSurface = factory.getSurface(); + expect(newSurface, equals(surface)); + }); + + test('isLive', () { + final SurfaceFactory factory = SurfaceFactory(3); + + expect(factory.isLive(factory.baseSurface), isTrue); + expect(factory.isLive(factory.backupSurface), isTrue); + + final Surface surface = factory.getSurface(); + expect(factory.isLive(surface), isTrue); + + factory.releaseSurface(surface); + expect(factory.isLive(surface), isFalse); + }); + + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/surface_test.dart b/lib/web_ui/test/canvaskit/surface_test.dart new file mode 100644 index 0000000000000..8c0fccb473190 --- /dev/null +++ b/lib/web_ui/test/canvaskit/surface_test.dart @@ -0,0 +1,183 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CanvasKit', () { + setUpCanvasKitTest(); + setUp(() { + window.debugOverrideDevicePixelRatio(1.0); + }); + + test('Surface allocates canvases efficiently', () { + final Surface surface = SurfaceFactory.instance.getSurface(); + final CkSurface originalSurface = + surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + final html.CanvasElement original = surface.htmlCanvas!; + + // Expect exact requested dimensions. + expect(original.width, 9); + expect(original.height, 19); + expect(original.style.width, '9px'); + expect(original.style.height, '19px'); + expect(originalSurface.width(), 9); + expect(originalSurface.height(), 19); + + // Shrinking reuses the existing canvas straight-up. + final CkSurface shrunkSurface = + surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; + final html.CanvasElement shrunk = surface.htmlCanvas!; + expect(shrunk, same(original)); + expect(shrunk.style.width, '9px'); + expect(shrunk.style.height, '19px'); + expect(shrunkSurface, isNot(same(original))); + expect(shrunkSurface.width(), 5); + expect(shrunkSurface.height(), 15); + + // The first increase will allocate a new canvas, but will overallocate + // by 40% to accommodate future increases. + final CkSurface firstIncreaseSurface = + surface.acquireFrame(const ui.Size(10, 20)).skiaSurface; + final html.CanvasElement firstIncrease = surface.htmlCanvas!; + expect(firstIncrease, isNot(same(original))); + expect(firstIncreaseSurface, isNot(same(shrunkSurface))); + + // Expect overallocated dimensions + expect(firstIncrease.width, 14); + expect(firstIncrease.height, 28); + expect(firstIncrease.style.width, '14px'); + expect(firstIncrease.style.height, '28px'); + expect(firstIncreaseSurface.width(), 10); + expect(firstIncreaseSurface.height(), 20); + + // Subsequent increases within 40% reuse the old canvas. + final CkSurface secondIncreaseSurface = + surface.acquireFrame(const ui.Size(11, 22)).skiaSurface; + final html.CanvasElement secondIncrease = surface.htmlCanvas!; + expect(secondIncrease, same(firstIncrease)); + expect(secondIncreaseSurface, isNot(same(firstIncreaseSurface))); + expect(secondIncreaseSurface.width(), 11); + expect(secondIncreaseSurface.height(), 22); + + // Increases beyond the 40% limit will cause a new allocation. + final CkSurface hugeSurface = surface.acquireFrame(const ui.Size(20, 40)).skiaSurface; + final html.CanvasElement huge = surface.htmlCanvas!; + expect(huge, isNot(same(secondIncrease))); + expect(hugeSurface, isNot(same(secondIncreaseSurface))); + + // Also over-allocated + expect(huge.width, 28); + expect(huge.height, 56); + expect(huge.style.width, '28px'); + expect(huge.style.height, '56px'); + expect(hugeSurface.width(), 20); + expect(hugeSurface.height(), 40); + + // Shrink again. Reuse the last allocated surface. + final CkSurface shrunkSurface2 = + surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; + final html.CanvasElement shrunk2 = surface.htmlCanvas!; + expect(shrunk2, same(huge)); + expect(shrunkSurface2, isNot(same(hugeSurface))); + expect(shrunkSurface2.width(), 5); + expect(shrunkSurface2.height(), 15); + // Skipping on Firefox for now since Firefox headless doesn't support WebGL + // This causes issues in the test since we create a Canvas-backed surface, + // which cannot be a different size from the canvas. + // TODO(hterkelsen): See if we can give a custom size for software + // surfaces. + }, skip: isFirefox || isIosSafari); + + test( + 'Surface creates new context when WebGL context is restored', + () async { + final Surface surface = SurfaceFactory.instance.getSurface(); + expect(surface.debugForceNewContext, isTrue); + final CkSurface before = + surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + expect(surface.debugForceNewContext, isFalse); + + // Pump a timer to flush any microtasks. + await Future.delayed(Duration.zero); + final CkSurface afterAcquireFrame = + surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + // Existing context is reused. + expect(afterAcquireFrame, same(before)); + + // Emulate WebGL context loss. + final html.CanvasElement canvas = + surface.htmlElement.children.single as html.CanvasElement; + final dynamic ctx = canvas.getContext('webgl2'); + expect(ctx, isNotNull); + final dynamic loseContextExtension = + ctx.getExtension('WEBGL_lose_context'); + loseContextExtension.loseContext(); + + // Pump a timer to allow the "lose context" event to propagate. + await Future.delayed(Duration.zero); + // We don't create a new GL context until the context is restored. + expect(surface.debugContextLost, isTrue); + expect(ctx.isContextLost(), isTrue); + + // Emulate WebGL context restoration. + loseContextExtension.restoreContext(); + + // Pump a timer to allow the "restore context" event to propagate. + await Future.delayed(Duration.zero); + expect(surface.debugForceNewContext, isTrue); + + final CkSurface afterContextLost = + surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + // A new context is created. + expect(afterContextLost, isNot(same(before))); + }, + // Firefox doesn't have the WEBGL_lose_context extension. + skip: isFirefox || isSafari, + ); + + // Regression test for https://github.com/flutter/flutter/issues/75286 + test('updates canvas logical size when device-pixel ratio changes', () { + final Surface surface = Surface(); + final CkSurface original = + surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; + + expect(original.width(), 10); + expect(original.height(), 16); + expect(surface.htmlCanvas!.style.width, '10px'); + expect(surface.htmlCanvas!.style.height, '16px'); + + // Increase device-pixel ratio: this makes CSS pixels bigger, so we need + // fewer of them to cover the browser window. + window.debugOverrideDevicePixelRatio(2.0); + final CkSurface highDpr = + surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; + expect(highDpr.width(), 10); + expect(highDpr.height(), 16); + expect(surface.htmlCanvas!.style.width, '5px'); + expect(surface.htmlCanvas!.style.height, '8px'); + + // Decrease device-pixel ratio: this makes CSS pixels smaller, so we need + // more of them to cover the browser window. + window.debugOverrideDevicePixelRatio(0.5); + final CkSurface lowDpr = + surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; + expect(lowDpr.width(), 10); + expect(lowDpr.height(), 16); + expect(surface.htmlCanvas!.style.width, '20px'); + expect(surface.htmlCanvas!.style.height, '32px'); + }); + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart b/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart new file mode 100644 index 0000000000000..78e667dcfba96 --- /dev/null +++ b/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); + +Future matchPictureGolden(String goldenFile, CkPicture picture, + {bool write = false}) async { + final EnginePlatformDispatcher dispatcher = + ui.window.platformDispatcher as EnginePlatformDispatcher; + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPicture(ui.Offset.zero, picture); + dispatcher.rasterizer!.draw(sb.build().layerTree); + await matchGoldenFile(goldenFile, region: region, write: write); +} + +void testMain() { + group('SweepGradient', () { + setUpCanvasKitTest(); + + test('is correctly rendered', () async { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(region); + + final CkGradientSweep gradient = CkGradientSweep( + const ui.Offset(250, 125), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0, + ], + ui.TileMode.clamp, + -(math.pi / 2), + math.pi * 2 - (math.pi / 2), + null); + + final CkPaint paint = CkPaint()..shader = gradient; + + canvas.drawRect(region, paint); + + await matchPictureGolden( + 'canvaskit_sweep_gradient.png', + recorder.endRecording(), + ); + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }, skip: isIosSafari || isFirefox); +} diff --git a/lib/web_ui/test/canvaskit/test_data.dart b/lib/web_ui/test/canvaskit/test_data.dart index 9ce6313d714ae..e13e62195bead 100644 --- a/lib/web_ui/test/canvaskit/test_data.dart +++ b/lib/web_ui/test/canvaskit/test_data.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 import 'dart:typed_data'; diff --git a/lib/web_ui/test/canvaskit/text_test.dart b/lib/web_ui/test/canvaskit/text_test.dart new file mode 100644 index 0000000000000..fabf17ed222b2 --- /dev/null +++ b/lib/web_ui/test/canvaskit/text_test.dart @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; + +import 'common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('CanvasKit text', () { + setUpCanvasKitTest(); + + test("doesn't crash when using shadows", () { + final ui.TextStyle textStyleWithShadows = ui.TextStyle( + fontSize: 16, + shadows: [ + const ui.Shadow( + color: ui.Color.fromARGB(255, 0, 0, 0), + blurRadius: 3.0, + offset: ui.Offset(3.0, 3.0), + ), + const ui.Shadow( + color: ui.Color.fromARGB(255, 0, 0, 0), + blurRadius: 3.0, + offset: ui.Offset(-3.0, 3.0), + ), + const ui.Shadow( + color: ui.Color.fromARGB(255, 0, 0, 0), + blurRadius: 3.0, + offset: ui.Offset(3.0, -3.0), + ), + const ui.Shadow( + color: ui.Color.fromARGB(255, 0, 0, 0), + blurRadius: 3.0, + offset: ui.Offset(-3.0, -3.0), + ), + ], + fontFamily: 'Roboto', + ); + + for (int i = 0; i < 10; i++) { + final ui.ParagraphBuilder builder = + ui.ParagraphBuilder(ui.ParagraphStyle(fontSize: 16)); + builder.pushStyle(textStyleWithShadows); + builder.addText('test'); + final ui.Paragraph paragraph = builder.build(); + expect(paragraph, isNotNull); + } + }); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 + }, skip: isIosSafari); +} diff --git a/lib/web_ui/test/canvaskit/vertices_test.dart b/lib/web_ui/test/canvaskit/vertices_test.dart index 0f0263a04eb7b..89f1d387e4e51 100644 --- a/lib/web_ui/test/canvaskit/vertices_test.dart +++ b/lib/web_ui/test/canvaskit/vertices_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; @@ -16,9 +15,7 @@ void main() { void testMain() { group('Vertices', () { - setUpAll(() async { - await ui.webOnlyInitializePlatform(); - }); + setUpCanvasKitTest(); test('can be constructed, drawn, and deleted', () { final CkVertices vertices = _testVertices(); @@ -26,37 +23,37 @@ void testMain() { expect(vertices.createDefault(), isNotNull); expect(vertices.resurrect(), isNotNull); - final recorder = CkPictureRecorder(); - final canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 100, 100)); + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 100, 100)); canvas.drawVertices( vertices, ui.BlendMode.srcOver, - ui.Paint(), + CkPaint(), ); vertices.delete(); }); - // TODO: https://github.com/flutter/flutter/issues/60040 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } -ui.Vertices _testVertices() { +CkVertices _testVertices() { return ui.Vertices( ui.VertexMode.triangles, - [ + const [ ui.Offset(0, 0), ui.Offset(10, 10), ui.Offset(0, 20), ], - textureCoordinates: [ + textureCoordinates: const [ ui.Offset(0, 0), ui.Offset(10, 10), ui.Offset(0, 20), ], - colors: [ + colors: const [ ui.Color.fromRGBO(255, 0, 0, 1.0), ui.Color.fromRGBO(0, 255, 0, 1.0), ui.Color.fromRGBO(0, 0, 255, 1.0), ], indices: [0, 1, 2], - ); + ) as CkVertices; } diff --git a/lib/web_ui/test/channel_buffers_test.dart b/lib/web_ui/test/channel_buffers_test.dart new file mode 100644 index 0000000000000..efede2f1f31ae --- /dev/null +++ b/lib/web_ui/test/channel_buffers_test.dart @@ -0,0 +1,383 @@ +// 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. + +// This is identical to +// ../../../testing/dart/channel_buffers_test.dart except for: +// +// * The imports are a bit different. +// * The main method has been renamed testMain. +// * A new main method here bootstraps the web tests. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +ByteData _makeByteData(String str) { + final Uint8List list = utf8.encode(str) as Uint8List; + final ByteBuffer buffer = list is Uint8List ? list.buffer : Uint8List.fromList(list).buffer; + return ByteData.view(buffer); +} + +void _resize(ui.ChannelBuffers buffers, String name, int newSize) { + buffers.handleMessage(_makeByteData('resize\r$name\r$newSize')); +} + +void testMain() { + test('push drain', () async { + const String channel = 'foo'; + final ByteData data = _makeByteData('bar'); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + bool called = false; + void callback(ByteData? responseData) { + called = true; + } + buffers.push(channel, data, callback); + await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + expect(drainedData, equals(data)); + assert(!called); + drainedCallback(drainedData); + assert(called); + return Future.value(); + }); + }); + + test('drain is sync', () async { + const String channel = 'foo'; + final ByteData data = _makeByteData('message'); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + void callback(ByteData? responseData) {} + buffers.push(channel, data, callback); + final List log = []; + final Completer completer = Completer(); + scheduleMicrotask(() { log.add('before drain, microtask'); }); + log.add('before drain'); + buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + log.add('callback'); + completer.complete(); + }); + log.add('after drain, before await'); + await completer.future; + log.add('after await'); + expect(log, [ + 'before drain', + 'callback', + 'after drain, before await', + 'before drain, microtask', + 'after await' + ]); + }); + + test('push drain zero', () async { + const String channel = 'foo'; + final ByteData data = _makeByteData('bar'); + final + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + void callback(ByteData? responseData) {} + _resize(buffers, channel, 0); + buffers.push(channel, data, callback); + bool didCall = false; + await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + didCall = true; + return Future.value(); + }); + expect(didCall, isFalse); + }); + + test('drain when empty', () async { + const String channel = 'foo'; + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + bool didCall = false; + await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + didCall = true; + return Future.value(); + }); + expect(didCall, isFalse); + }); + + test('overflow', () async { + const String channel = 'foo'; + final ByteData one = _makeByteData('one'); + final ByteData two = _makeByteData('two'); + final ByteData three = _makeByteData('three'); + final ByteData four = _makeByteData('four'); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + void callback(ByteData? responseData) {} + _resize(buffers, channel, 3); + buffers.push(channel, one, callback); + buffers.push(channel, two, callback); + buffers.push(channel, three, callback); + buffers.push(channel, four, callback); + int counter = 0; + await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + switch (counter) { + case 0: + expect(drainedData, equals(two)); + break; + case 1: + expect(drainedData, equals(three)); + break; + case 2: + expect(drainedData, equals(four)); + break; + } + counter += 1; + return Future.value(); + }); + expect(counter, equals(3)); + }); + + test('resize drop', () async { + const String channel = 'foo'; + final ByteData one = _makeByteData('one'); + final ByteData two = _makeByteData('two'); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + _resize(buffers, channel, 100); + void callback(ByteData? responseData) {} + buffers.push(channel, one, callback); + buffers.push(channel, two, callback); + _resize(buffers, channel, 1); + int counter = 0; + await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + switch (counter) { + case 0: + expect(drainedData, equals(two)); + } + counter += 1; + return Future.value(); + }); + expect(counter, equals(1)); + }); + + test('resize dropping calls callback', () async { + const String channel = 'foo'; + final ByteData one = _makeByteData('one'); + final ByteData two = _makeByteData('two'); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + bool didCallCallback = false; + void oneCallback(ByteData? responseData) { + expect(responseData, isNull); + didCallCallback = true; + } + void twoCallback(ByteData? responseData) { + throw TestFailure('wrong callback called'); + } + _resize(buffers, channel, 100); + buffers.push(channel, one, oneCallback); + buffers.push(channel, two, twoCallback); + expect(didCallCallback, isFalse); + _resize(buffers, channel, 1); + expect(didCallCallback, isTrue); + }); + + test('overflow calls callback', () async { + const String channel = 'foo'; + final ByteData one = _makeByteData('one'); + final ByteData two = _makeByteData('two'); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + bool didCallCallback = false; + void oneCallback(ByteData? responseData) { + expect(responseData, isNull); + didCallCallback = true; + } + void twoCallback(ByteData? responseData) { + throw TestFailure('wrong callback called'); + } + _resize(buffers, channel, 1); + buffers.push(channel, one, oneCallback); + buffers.push(channel, two, twoCallback); + expect(didCallCallback, isTrue); + }); + + test('handle garbage', () async { + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + expect(() => buffers.handleMessage(_makeByteData('asdfasdf')), + throwsException); + }); + + test('handle resize garbage', () async { + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + expect(() => buffers.handleMessage(_makeByteData('resize\rfoo\rbar')), + throwsException); + }); + + test('ChannelBuffers.setListener', () async { + final List log = []; + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + final ByteData one = _makeByteData('one'); + final ByteData two = _makeByteData('two'); + final ByteData three = _makeByteData('three'); + final ByteData four = _makeByteData('four'); + final ByteData five = _makeByteData('five'); + final ByteData six = _makeByteData('six'); + final ByteData seven = _makeByteData('seven'); + buffers.push('a', one, (ByteData? data) { }); + buffers.push('b', two, (ByteData? data) { }); + buffers.push('a', three, (ByteData? data) { }); + log.add('top'); + buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { + log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}'); + }); + log.add('-1'); + await null; + log.add('-2'); + buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { + log.add('a2: ${utf8.decode(data!.buffer.asUint8List())}'); + }); + log.add('-3'); + await null; + log.add('-4'); + buffers.setListener('b', (ByteData? data, ui.PlatformMessageResponseCallback callback) { + log.add('b: ${utf8.decode(data!.buffer.asUint8List())}'); + }); + log.add('-5'); + await null; // first microtask after setting listener drains the first message + await null; // second microtask ends the draining. + log.add('-6'); + buffers.push('b', four, (ByteData? data) { }); + buffers.push('a', five, (ByteData? data) { }); + log.add('-7'); + await null; + log.add('-8'); + buffers.clearListener('a'); + buffers.push('a', six, (ByteData? data) { }); + buffers.push('b', seven, (ByteData? data) { }); + await null; + log.add('-9'); + expect(log, [ + 'top', + '-1', + 'a1: three', + '-2', + '-3', + '-4', + '-5', + 'b: two', + '-6', + 'b: four', + 'a2: five', + '-7', + '-8', + 'b: seven', + '-9', + ]); + }); + + test('ChannelBuffers.clearListener', () async { + final List log = []; + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + final ByteData one = _makeByteData('one'); + final ByteData two = _makeByteData('two'); + final ByteData three = _makeByteData('three'); + final ByteData four = _makeByteData('four'); + buffers.handleMessage(_makeByteData('resize\ra\r10')); + buffers.push('a', one, (ByteData? data) { }); + buffers.push('a', two, (ByteData? data) { }); + buffers.push('a', three, (ByteData? data) { }); + log.add('-1'); + buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { + log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}'); + }); + await null; // handles one + log.add('-2'); + buffers.clearListener('a'); + await null; + log.add('-3'); + buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { + log.add('a2: ${utf8.decode(data!.buffer.asUint8List())}'); + }); + log.add('-4'); + await null; + buffers.push('a', four, (ByteData? data) { }); + log.add('-5'); + await null; + log.add('-6'); + await null; + log.add('-7'); + await null; + expect(log, [ + '-1', + 'a1: one', + '-2', + '-3', + '-4', + 'a2: two', + '-5', + 'a2: three', + '-6', + 'a2: four', + '-7', + ]); + }); + + test('ChannelBuffers.handleMessage for resize', () async { + final List log = []; + final ui.ChannelBuffers buffers = _TestChannelBuffers(log); + // Created as follows: + // print(StandardMethodCodec().encodeMethodCall(MethodCall('resize', ['abcdef', 12345])).buffer.asUint8List()); + // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. + buffers.handleMessage(ByteData.sublistView(Uint8List.fromList([255, 255, 255, 7, 6, 114, 101, 115, 105, 122, 101, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 3, 57, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 27)); + expect(log, const ['resize abcdef 12345']); + }); + + test('ChannelBuffers.handleMessage for overflow', () async { + final List log = []; + final ui.ChannelBuffers buffers = _TestChannelBuffers(log); + // Created as follows: + // print(StandardMethodCodec().encodeMethodCall(MethodCall('overflow', ['abcdef', false])).buffer.asUint8List()); + // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. + buffers.handleMessage(ByteData.sublistView(Uint8List.fromList([255, 255, 255, 7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 24)); + expect(log, const ['allowOverflow abcdef false']); + }); + + test('ChannelBuffers uses the right zones', () async { + final List log = []; + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); + final Zone zone1 = Zone.current.fork(); + final Zone zone2 = Zone.current.fork(); + zone1.run(() { + log.add('first zone run: ${Zone.current == zone1}'); + buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { + log.add('callback1: ${Zone.current == zone1}'); + callback(data); + }); + }); + zone2.run(() { + log.add('second zone run: ${Zone.current == zone2}'); + buffers.push('a', ByteData.sublistView(Uint8List.fromList([]), 0, 0), (ByteData? data) { + log.add('callback2: ${Zone.current == zone2}'); + }); + }); + await null; + expect(log, [ + 'first zone run: true', + 'second zone run: true', + 'callback1: true', + 'callback2: true', + ]); + }); +} + +class _TestChannelBuffers extends ui.ChannelBuffers { + _TestChannelBuffers(this.log); + + final List log; + + @override + void resize(String name, int newSize) { + log.add('resize $name $newSize'); + } + + @override + void allowOverflow(String name, bool allowed) { + log.add('allowOverflow $name $allowed'); + } +} diff --git a/lib/web_ui/test/clipboard_test.dart b/lib/web_ui/test/clipboard_test.dart index e0c526176e831..1e6174e233c5e 100644 --- a/lib/web_ui/test/clipboard_test.dart +++ b/lib/web_ui/test/clipboard_test.dart @@ -2,37 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:typed_data'; -import 'package:mockito/mockito.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart' as ui; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { +Future testMain() async { await ui.webOnlyInitializeTestDomRenderer(); group('message handler', () { const String testText = 'test text'; - final Future success = Future.value(true); - final Future failure = Future.value(false); - final Future pasteTest = Future.value(testText); - - ClipboardMessageHandler clipboardMessageHandler; - ClipboardAPICopyStrategy clipboardAPICopyStrategy = + late ClipboardMessageHandler clipboardMessageHandler; + MockClipboardAPICopyStrategy clipboardAPICopyStrategy = MockClipboardAPICopyStrategy(); - ClipboardAPIPasteStrategy clipboardAPIPasteStrategy = + MockClipboardAPIPasteStrategy clipboardAPIPasteStrategy = MockClipboardAPIPasteStrategy(); setUp(() { - clipboardMessageHandler = new ClipboardMessageHandler(); + clipboardMessageHandler = ClipboardMessageHandler(); clipboardAPICopyStrategy = MockClipboardAPICopyStrategy(); clipboardAPIPasteStrategy = MockClipboardAPIPasteStrategy(); clipboardMessageHandler.copyToClipboardStrategy = @@ -42,62 +36,73 @@ void testMain() async { }); test('set data successful', () async { - when(clipboardAPICopyStrategy.setData(testText)) - .thenAnswer((_) => success); + clipboardAPICopyStrategy.testResult = true; const MethodCodec codec = JSONMethodCodec(); - bool result = false; - ui.PlatformMessageResponseCallback callback = (ByteData data) { - result = codec.decodeEnvelope(data); - }; + final Completer completer = Completer(); + void callback(ByteData? data) { + completer.complete(codec.decodeEnvelope(data!) as bool); + } - await clipboardMessageHandler.setDataMethodCall( + clipboardMessageHandler.setDataMethodCall( const MethodCall('Clipboard.setData', { 'text': testText, }), callback); - await expectLater(result, true); + expect(await completer.future, isTrue); }); test('set data error', () async { - when(clipboardAPICopyStrategy.setData(testText)) - .thenAnswer((_) => failure); + clipboardAPICopyStrategy.testResult = false; const MethodCodec codec = JSONMethodCodec(); - ByteData result; - ui.PlatformMessageResponseCallback callback = (ByteData data) { - result = data; - }; + final Completer completer = Completer(); + void callback(ByteData? data) { + completer.complete(data!); + } - await clipboardMessageHandler.setDataMethodCall( + clipboardMessageHandler.setDataMethodCall( const MethodCall('Clipboard.setData', { 'text': testText, }), callback); - expect(() async { - codec.decodeEnvelope(result); - }, throwsA(TypeMatcher() - .having((e) => e.code, 'code', equals('copy_fail')))); + final ByteData result = await completer.future; + expect( + () =>codec.decodeEnvelope(result), + throwsA(const TypeMatcher() + .having((PlatformException e) => e.code, 'code', equals('copy_fail')))); }); test('get data successful', () async { - when(clipboardAPIPasteStrategy.getData()) - .thenAnswer((_) => pasteTest); + clipboardAPIPasteStrategy.testResult = testText; const MethodCodec codec = JSONMethodCodec(); - Map result; - ui.PlatformMessageResponseCallback callback = (ByteData data) { - result = codec.decodeEnvelope(data); - }; + final Completer> completer = Completer>(); + void callback(ByteData? data) { + completer.complete(codec.decodeEnvelope(data!) as Map); + } - await clipboardMessageHandler.getDataMethodCall(callback); + clipboardMessageHandler.getDataMethodCall(callback); - await expectLater(result['text'], testText); + final Map result = await completer.future; + expect(result['text'], testText); }); }); } -class MockClipboardAPICopyStrategy extends Mock - implements ClipboardAPICopyStrategy {} +class MockClipboardAPICopyStrategy implements ClipboardAPICopyStrategy { + bool testResult = true; -class MockClipboardAPIPasteStrategy extends Mock - implements ClipboardAPIPasteStrategy {} + @override + Future setData(String? text) { + return Future.value(testResult); + } +} + +class MockClipboardAPIPasteStrategy implements ClipboardAPIPasteStrategy { + String testResult = ''; + + @override + Future getData() { + return Future.value(testResult); + } +} diff --git a/lib/web_ui/test/color_test.dart b/lib/web_ui/test/color_test.dart index d48ea0bef83cb..c03ea0112ee74 100644 --- a/lib/web_ui/test/color_test.dart +++ b/lib/web_ui/test/color_test.dart @@ -2,11 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 -import 'package:ui/ui.dart'; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; +import 'package:ui/ui.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -33,29 +31,21 @@ void testMain() { }); test('color created with out of bounds value', () { - try { - const Color c = Color(0x100 << 24); - final Paint p = Paint(); - p.color = c; - } catch (e) { - expect(e != null, equals(true)); - } + const Color c = Color(0x100 << 24); + final Paint p = Paint(); + p.color = c; }); test('color created with wildly out of bounds value', () { - try { - const Color c = Color(1 << 1000000); - final Paint p = Paint(); - p.color = c; - } catch (e) { - expect(e != null, equals(true)); - } + const Color c = Color(1 << 1000000); + final Paint p = Paint(); + p.color = c; }); test('two colors are only == if they have the same runtime type', () { expect(const Color(123), equals(const Color(123))); expect(const Color(123), - equals(Color(123))); // ignore: prefer_const_constructors + equals(const Color(123))); expect(const Color(123), isNot(equals(const Color(321)))); expect(const Color(123), isNot(equals(const NotAColor(123)))); expect(const NotAColor(123), isNot(equals(const Color(123)))); @@ -147,14 +137,14 @@ void testMain() { // Regression test for https://github.com/flutter/flutter/issues/41257 // CupertinoDynamicColor was overriding base class and calling super(0). test('subclass of Color can override value', () { - final DynamicColorClass color = DynamicColorClass(0xF0E0D0C0); + const DynamicColorClass color = DynamicColorClass(0xF0E0D0C0); expect(color.value, 0xF0E0D0C0); // Call base class member, make sure it uses overridden value. expect(color.red, 0xE0); }); test('Paint converts Color subclasses to plain Color', () { - final DynamicColorClass color = DynamicColorClass(0xF0E0D0C0); + const DynamicColorClass color = DynamicColorClass(0xF0E0D0C0); final Paint paint = Paint()..color = color; expect(paint.color.runtimeType, Color); }); @@ -165,5 +155,6 @@ class DynamicColorClass extends Color { final int _newValue; + @override int get value => _newValue; } diff --git a/lib/web_ui/test/common.dart b/lib/web_ui/test/common.dart new file mode 100644 index 0000000000000..2909a6fecaca8 --- /dev/null +++ b/lib/web_ui/test/common.dart @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/src/engine.dart'; + +/// Whether the current browser is Safari. +bool get isSafari => browserEngine == BrowserEngine.webkit; + +/// Whether the current browser is Safari on iOS. +// TODO(yjbanov): https://github.com/flutter/flutter/issues/60040 +bool get isIosSafari => isSafari && operatingSystem == OperatingSystem.iOs; + +/// Whether the current browser is Firefox. +bool get isFirefox => browserEngine == BrowserEngine.firefox; diff --git a/lib/web_ui/test/dom_renderer_test.dart b/lib/web_ui/test/dom_renderer_test.dart index 514c54d4160bc..bc6fadb521501 100644 --- a/lib/web_ui/test/dom_renderer_test.dart +++ b/lib/web_ui/test/dom_renderer_test.dart @@ -1,10 +1,12 @@ // 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. +@JS() +library dom_renderer_test; // We need this to mess with the ShadowDOM. -// @dart = 2.6 import 'dart:html' as html; +import 'package:js/js.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; @@ -14,11 +16,19 @@ void main() { } void testMain() { + test('populates flt-renderer and flt-build-mode', () { + DomRenderer(); + expect(html.document.body!.attributes['flt-renderer'], + 'html (requested explicitly)'); + expect(html.document.body!.attributes['flt-build-mode'], 'debug'); + }); + test('creating elements works', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); expect(element, isNotNull); }); + test('can append children to parents', () { final DomRenderer renderer = DomRenderer(); final html.Element parent = renderer.createElement('div'); @@ -26,18 +36,21 @@ void testMain() { renderer.append(parent, child); expect(parent.children, hasLength(1)); }); + test('can set text on elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); renderer.setText(element, 'Hello World'); expect(element.text, 'Hello World'); }); + test('can set attributes on elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); renderer.setElementAttribute(element, 'id', 'foo'); expect(element.id, 'foo'); }); + test('can add classes to elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); @@ -45,6 +58,7 @@ void testMain() { renderer.addElementClass(element, 'bar'); expect(element.classes, ['foo', 'bar']); }); + test('can remove classes from elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); @@ -54,26 +68,30 @@ void testMain() { renderer.removeElementClass(element, 'foo'); expect(element.classes, ['bar']); }); + test('can set style properties on elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); - renderer.setElementStyle(element, 'color', 'red'); + DomRenderer.setElementStyle(element, 'color', 'red'); expect(element.style.color, 'red'); }); + test('can remove style properties from elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); - renderer.setElementStyle(element, 'color', 'blue'); + DomRenderer.setElementStyle(element, 'color', 'blue'); expect(element.style.color, 'blue'); - renderer.setElementStyle(element, 'color', null); + DomRenderer.setElementStyle(element, 'color', null); expect(element.style.color, ''); }); + test('elements can have children', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); renderer.createElement('div', parent: element); expect(element.children, hasLength(1)); }); + test('can detach elements', () { final DomRenderer renderer = DomRenderer(); final html.Element element = renderer.createElement('div'); @@ -85,8 +103,8 @@ void testMain() { test('innerHeight/innerWidth are equal to visualViewport height and width', () { if (html.window.visualViewport != null) { - expect(html.window.visualViewport.width, html.window.innerWidth); - expect(html.window.visualViewport.height, html.window.innerHeight); + expect(html.window.visualViewport!.width, html.window.innerWidth); + expect(html.window.visualViewport!.height, html.window.innerHeight); } }); @@ -94,21 +112,67 @@ void testMain() { final html.MetaElement existingMeta = html.MetaElement() ..name = 'viewport' ..content = 'foo=bar'; - html.document.head.append(existingMeta); - expect(existingMeta.isConnected, true); + html.document.head!.append(existingMeta); + expect(existingMeta.isConnected, isTrue); final DomRenderer renderer = DomRenderer(); renderer.reset(); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50828 - skip: (browserEngine == BrowserEngine.firefox || - browserEngine == BrowserEngine.edge)); + // TODO(ferhat): https://github.com/flutter/flutter/issues/46638 + // TODO(ferhat): https://github.com/flutter/flutter/issues/50828 + skip: browserEngine == BrowserEngine.firefox || + browserEngine == BrowserEngine.edge); test('accesibility placeholder is attached after creation', () { - DomRenderer(); + final DomRenderer renderer = DomRenderer(); + + expect( + renderer.glassPaneShadow?.querySelectorAll('flt-semantics-placeholder'), + isNotEmpty, + ); + }); - expect(html.document.getElementsByTagName('flt-semantics-placeholder'), - isNotEmpty); + test('renders a shadowRoot by default', () { + final DomRenderer renderer = DomRenderer(); + + final HostNode hostNode = renderer.glassPaneShadow!; + + expect(hostNode.node, isA()); + }); + + test('starts without shadowDom available too', () { + final dynamic oldAttachShadow = attachShadow; + expect(oldAttachShadow, isNotNull); + + attachShadow = null; // Break ShadowDOM + + final DomRenderer renderer = DomRenderer(); + + final HostNode hostNode = renderer.glassPaneShadow!; + + expect(hostNode.node, isA()); + expect( + (hostNode.node as html.Element).tagName, + equalsIgnoringCase('flt-element-host-node'), + ); + + attachShadow = oldAttachShadow; // Restore ShadowDOM + }); + + test('should add/remove global resource', () { + final DomRenderer renderer = DomRenderer(); + final html.DivElement resource = html.DivElement(); + renderer.addResource(resource); + final html.Element? resourceRoot = resource.parent; + expect(resourceRoot, isNotNull); + expect(resourceRoot!.childNodes.length, 1); + renderer.removeResource(resource); + expect(resourceRoot.childNodes.length, 0); }); } + +@JS('Element.prototype.attachShadow') +external dynamic get attachShadow; + +@JS('Element.prototype.attachShadow') +external set attachShadow(dynamic x); diff --git a/lib/web_ui/test/engine/frame_reference_test.dart b/lib/web_ui/test/engine/frame_reference_test.dart index 0658f8258ba7c..ee412c39dfa01 100644 --- a/lib/web_ui/test/engine/frame_reference_test.dart +++ b/lib/web_ui/test/engine/frame_reference_test.dart @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 -import 'package:ui/src/engine.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -14,25 +13,25 @@ void main() { void testMain() { group('CrossFrameCache', () { test('Reuse returns no object when cache empty', () { - final CrossFrameCache cache = CrossFrameCache(); + final CrossFrameCache cache = CrossFrameCache(); cache.commitFrame(); - TestItem requestedItem = cache.reuse('item1'); + final TestItem? requestedItem = cache.reuse('item1'); expect(requestedItem, null); }); test('Reuses object across frames', () { - final CrossFrameCache cache = CrossFrameCache(); + final CrossFrameCache cache = CrossFrameCache(); final TestItem testItem1 = TestItem('item1'); cache.cache(testItem1.label, testItem1); cache.commitFrame(); - TestItem requestedItem = cache.reuse('item1'); + TestItem? requestedItem = cache.reuse('item1'); expect(requestedItem, testItem1); requestedItem = cache.reuse('item1'); expect(requestedItem, null); }); test('Reuses objects that have same key across frames', () { - final CrossFrameCache cache = CrossFrameCache(); + final CrossFrameCache cache = CrossFrameCache(); final TestItem testItem1 = TestItem('sameLabel'); final TestItem testItem2 = TestItem('sameLabel'); final TestItem testItemX = TestItem('X'); @@ -40,7 +39,7 @@ void testMain() { cache.cache(testItemX.label, testItemX); cache.cache(testItem2.label, testItem2); cache.commitFrame(); - TestItem requestedItem = cache.reuse('sameLabel'); + TestItem? requestedItem = cache.reuse('sameLabel'); expect(requestedItem, testItem1); requestedItem = cache.reuse('sameLabel'); expect(requestedItem, testItem2); @@ -49,18 +48,18 @@ void testMain() { }); test('Values don\'t survive beyond next frame', () { - final CrossFrameCache cache = CrossFrameCache(); + final CrossFrameCache cache = CrossFrameCache(); final TestItem testItem1 = TestItem('item1'); cache.cache(testItem1.label, testItem1); cache.commitFrame(); cache.commitFrame(); - TestItem requestedItem = cache.reuse('item1'); + final TestItem? requestedItem = cache.reuse('item1'); expect(requestedItem, null); }); test('Values are evicted when not reused', () { - final Set _evictedItems = {}; - final CrossFrameCache cache = CrossFrameCache(); + final Set _evictedItems = {}; + final CrossFrameCache cache = CrossFrameCache(); final TestItem testItem1 = TestItem('item1'); final TestItem testItem2 = TestItem('item2'); cache.cache(testItem1.label, testItem1, (TestItem item) {_evictedItems.add(item);}); @@ -69,8 +68,8 @@ void testMain() { expect(_evictedItems.length, 0); cache.reuse('item2'); cache.commitFrame(); - expect(_evictedItems.contains(testItem1), true); - expect(_evictedItems.contains(testItem2), false); + expect(_evictedItems.contains(testItem1), isTrue); + expect(_evictedItems.contains(testItem2), isFalse); }); }); } diff --git a/lib/web_ui/test/engine/history_test.dart b/lib/web_ui/test/engine/history_test.dart index 4c11ed0033636..7f87324b4091d 100644 --- a/lib/web_ui/test/engine/history_test.dart +++ b/lib/web_ui/test/engine/history_test.dart @@ -2,25 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 @TestOn('!safari') -// TODO(nurhan): https://github.com/flutter/flutter/issues/51169 +// TODO(mdebbar): https://github.com/flutter/flutter/issues/51169 import 'dart:async'; import 'dart:html' as html; -import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine.dart' show window; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/navigation.dart'; +import 'package:ui/src/engine/services.dart'; +import 'package:ui/src/engine/test_embedding.dart'; import '../spy.dart'; -TestLocationStrategy get strategy => window.browserHistory.locationStrategy; -Future setStrategy(TestLocationStrategy newStrategy) async { - await window.browserHistory.setLocationStrategy(newStrategy); -} - Map _wrapOriginState(dynamic state) { return {'origin': true, 'state': state}; } @@ -37,34 +34,77 @@ const Map flutterState = {'flutter': true}; const MethodCodec codec = JSONMethodCodec(); -void emptyCallback(ByteData date) {} - void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { + test('createHistoryForExistingState', () { + TestUrlStrategy strategy; + BrowserHistory history; + + // No url strategy. + history = createHistoryForExistingState(null); + expect(history, isA()); + expect(history.urlStrategy, isNull); + + // Random history state. + strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry({'foo': 123}, null, '/'), + ); + history = createHistoryForExistingState(strategy); + expect(history, isA()); + expect(history.urlStrategy, strategy); + + // Multi-entry history state. + final Map state = { + 'serialCount': 1, + 'state': {'foo': 123}, + }; + strategy = TestUrlStrategy.fromEntry(TestHistoryEntry(state, null, '/')); + history = createHistoryForExistingState(strategy); + expect(history, isA()); + expect(history.urlStrategy, strategy); + + // Single-entry history "origin" state. + strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry({'origin': true}, null, '/'), + ); + history = createHistoryForExistingState(strategy); + expect(history, isA()); + expect(history.urlStrategy, strategy); + + // Single-entry history "flutter" state. + strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry({'flutter': true}, null, '/'), + ); + history = createHistoryForExistingState(strategy); + expect(history, isA()); + expect(history.urlStrategy, strategy); + }); + group('$SingleEntryBrowserHistory', () { final PlatformMessagesSpy spy = PlatformMessagesSpy(); setUp(() async { - await window.debugSwitchBrowserHistory(useSingle: true); spy.setUp(); }); tearDown(() async { spy.tearDown(); - await setStrategy(null); + await window.resetHistory(); }); test('basic setup works', () async { - await setStrategy(TestLocationStrategy.fromEntry( - TestHistoryEntry('initial state', null, '/initial'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry('initial state', null, '/initial'), + ); + await window.debugInitializeHistory(strategy, useSingle: true); // There should be two entries: origin and flutter. expect(strategy.history, hasLength(2)); - // The origin entry is setup but its path should remain unchanged. + // The origin entry is set up but its path should remain unchanged. final TestHistoryEntry originEntry = strategy.history[0]; expect(originEntry.state, _wrapOriginState('initial state')); expect(originEntry.url, '/initial'); @@ -78,11 +118,15 @@ void testMain() { // The flutter entry is the current entry. expect(strategy.currentEntry, flutterEntry); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); test('browser back button pops routes correctly', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry(null, null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry(null, null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: true); + // Initially, we should be on the flutter entry. expect(strategy.history, hasLength(2)); expect(strategy.currentEntry.state, flutterState); @@ -98,7 +142,7 @@ void testMain() { // No platform messages have been sent so far. expect(spy.messages, isEmpty); // Clicking back should take us to page1. - await strategy.back(); + await strategy.go(-1); // First, the framework should've received a `popRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -111,11 +155,14 @@ void testMain() { expect(strategy.currentEntry.state, flutterState); expect(strategy.currentEntry.url, '/home'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); test('multiple browser back clicks', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry(null, null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry(null, null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: true); await routeUpdated('/page1'); await routeUpdated('/page2'); @@ -127,7 +174,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page2'); // Back to page1. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `popRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -143,7 +190,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page1'); // Back to home. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `popRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -161,8 +208,8 @@ void testMain() { // The next browser back will exit the app. We store the strategy locally // because it will be remove from the browser history class once it exits // the app. - TestLocationStrategy originalStrategy = strategy; - await originalStrategy.back(); + final TestUrlStrategy originalStrategy = strategy; + await originalStrategy.go(-1); // 1. The engine sends a `popRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -176,12 +223,15 @@ void testMain() { // navigated past it. expect(originalStrategy.currentEntryIndex, -1); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); test('handle user-provided url', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry(null, null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry(null, null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: true); await strategy.simulateUserTypingUrl('/page3'); // This delay is necessary to wait for [BrowserHistory] because it @@ -202,7 +252,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page3'); // Back to home. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `popRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -217,11 +267,14 @@ void testMain() { expect(strategy.currentEntry.state, flutterState); expect(strategy.currentEntry.url, '/home'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); test('user types unknown url', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry(null, null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry(null, null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: true); await strategy.simulateUserTypingUrl('/unknown'); // This delay is necessary to wait for [BrowserHistory] because it @@ -240,7 +293,7 @@ void testMain() { expect(strategy.currentEntry.state, flutterState); expect(strategy.currentEntry.url, '/home'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); }); @@ -248,18 +301,19 @@ void testMain() { final PlatformMessagesSpy spy = PlatformMessagesSpy(); setUp(() async { - await window.debugSwitchBrowserHistory(useSingle: false); spy.setUp(); }); tearDown(() async { spy.tearDown(); - await setStrategy(null); + await window.resetHistory(); }); test('basic setup works', () async { - await setStrategy(TestLocationStrategy.fromEntry( - TestHistoryEntry('initial state', null, '/initial'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry('initial state', null, '/initial'), + ); + await window.debugInitializeHistory(strategy, useSingle: false); // There should be only one entry. expect(strategy.history, hasLength(1)); @@ -269,16 +323,20 @@ void testMain() { expect(taggedOriginEntry.state, _tagStateWithSerialCount('initial state', 0)); expect(taggedOriginEntry.url, '/initial'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); - test('browser back button push route infromation correctly', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/home'))); + test('browser back button push route information correctly', () async { + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry('initial state', null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: false); + // Initially, we should be on the flutter entry. expect(strategy.history, hasLength(1)); expect(strategy.currentEntry.state, _tagStateWithSerialCount('initial state', 0)); expect(strategy.currentEntry.url, '/home'); - await routeInfomrationUpdated('/page1', 'page1 state'); + await routeInformationUpdated('/page1', 'page1 state'); // Should have two history entries now. expect(strategy.history, hasLength(2)); expect(strategy.currentEntryIndex, 1); @@ -289,7 +347,7 @@ void testMain() { // No platform messages have been sent so far. expect(spy.messages, isEmpty); // Clicking back should take us to page1. - await strategy.back(); + await strategy.go(-1); // First, the framework should've received a `pushRouteInformation` // platform message. expect(spy.messages, hasLength(1)); @@ -306,14 +364,17 @@ void testMain() { expect(strategy.currentEntry.state, _tagStateWithSerialCount('initial state', 0)); expect(strategy.currentEntry.url, '/home'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); test('multiple browser back clicks', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry('initial state', null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: false); - await routeInfomrationUpdated('/page1', 'page1 state'); - await routeInfomrationUpdated('/page2', 'page2 state'); + await routeInformationUpdated('/page1', 'page1 state'); + await routeInformationUpdated('/page2', 'page2 state'); // Make sure we are on page2. expect(strategy.history, hasLength(3)); @@ -322,7 +383,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page2'); // Back to page1. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `pushRouteInformation` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -338,7 +399,7 @@ void testMain() { expect(strategy.currentEntry.state, _tagStateWithSerialCount('page1 state', 1)); expect(strategy.currentEntry.url, '/page1'); // Back to home. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `pushRouteInformation` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -354,12 +415,15 @@ void testMain() { expect(strategy.currentEntry.state, _tagStateWithSerialCount('initial state', 0)); expect(strategy.currentEntry.url, '/home'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); test('handle user-provided url', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry('initial state', null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: false); await strategy.simulateUserTypingUrl('/page3'); // This delay is necessary to wait for [BrowserHistory] because it @@ -381,7 +445,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page3'); // Back to home. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `pushRouteInformation` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -397,14 +461,17 @@ void testMain() { expect(strategy.currentEntry.state, _tagStateWithSerialCount('initial state', 0)); expect(strategy.currentEntry.url, '/home'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); test('forward button works', () async { - await setStrategy(TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/home'))); + final TestUrlStrategy strategy = TestUrlStrategy.fromEntry( + const TestHistoryEntry('initial state', null, '/home'), + ); + await window.debugInitializeHistory(strategy, useSingle: false); - await routeInfomrationUpdated('/page1', 'page1 state'); - await routeInfomrationUpdated('/page2', 'page2 state'); + await routeInformationUpdated('/page1', 'page1 state'); + await routeInformationUpdated('/page2', 'page2 state'); // Make sure we are on page2. expect(strategy.history, hasLength(3)); @@ -413,7 +480,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page2'); // Back to page1. - await strategy.back(); + await strategy.go(-1); // 1. The engine sends a `pushRouteInformation` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -430,7 +497,7 @@ void testMain() { expect(strategy.currentEntry.url, '/page1'); // Forward to page2 - await strategy.back(count: -1); + await strategy.go(1); // 1. The engine sends a `pushRouteInformation` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -446,42 +513,42 @@ void testMain() { expect(strategy.currentEntry.state, _tagStateWithSerialCount('page2 state', 2)); expect(strategy.currentEntry.url, '/page2'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50836 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50836 skip: browserEngine == BrowserEngine.edge); }); - group('$HashLocationStrategy', () { - TestPlatformLocation location; + group('$HashUrlStrategy', () { + late TestPlatformLocation location; setUp(() { location = TestPlatformLocation(); }); tearDown(() { - location = null; + location = TestPlatformLocation(); }); test('leading slash is optional', () { - final HashLocationStrategy strategy = HashLocationStrategy(location); + final HashUrlStrategy strategy = HashUrlStrategy(location); location.hash = '#/'; - expect(strategy.path, '/'); + expect(strategy.getPath(), '/'); location.hash = '#/foo'; - expect(strategy.path, '/foo'); + expect(strategy.getPath(), '/foo'); location.hash = '#foo'; - expect(strategy.path, 'foo'); + expect(strategy.getPath(), 'foo'); }); test('path should not be empty', () { - final HashLocationStrategy strategy = HashLocationStrategy(location); + final HashUrlStrategy strategy = HashUrlStrategy(location); location.hash = ''; - expect(strategy.path, '/'); + expect(strategy.getPath(), '/'); location.hash = '#'; - expect(strategy.path, '/'); + expect(strategy.getPath(), '/'); }); }); } @@ -499,7 +566,7 @@ Future routeUpdated(String routeName) { return completer.future; } -Future routeInfomrationUpdated(String location, dynamic state) { +Future routeInformationUpdated(String location, dynamic state) { final Completer completer = Completer(); window.sendPlatformMessage( 'flutter/navigation', @@ -516,7 +583,7 @@ Future systemNavigatorPop() { final Completer completer = Completer(); window.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(MethodCall('SystemNavigator.pop')), + codec.encodeMethodCall(const MethodCall('SystemNavigator.pop')), (_) => completer.complete(), ); return completer.future; @@ -524,36 +591,43 @@ Future systemNavigatorPop() { /// A mock implementation of [PlatformLocation] that doesn't access the browser. class TestPlatformLocation extends PlatformLocation { - String pathname; - String search; - String hash; + @override + String? hash; + + @override dynamic state; - void onPopState(html.EventListener fn) { - throw UnimplementedError(); - } + @override + String get pathname => throw UnimplementedError(); - void offPopState(html.EventListener fn) { - throw UnimplementedError(); - } + @override + String get search => throw UnimplementedError(); - void onHashChange(html.EventListener fn) { + @override + void addPopStateListener(html.EventListener fn) { throw UnimplementedError(); } - void offHashChange(html.EventListener fn) { + @override + void removePopStateListener(html.EventListener fn) { throw UnimplementedError(); } + @override void pushState(dynamic state, String title, String url) { throw UnimplementedError(); } + @override void replaceState(dynamic state, String title, String url) { throw UnimplementedError(); } - void back(int count) { + @override + void go(int count) { throw UnimplementedError(); } + + @override + String getBaseHref() => '/'; } diff --git a/lib/web_ui/test/engine/host_node_test.dart b/lib/web_ui/test/engine/host_node_test.dart new file mode 100644 index 0000000000000..2303650b683c6 --- /dev/null +++ b/lib/web_ui/test/engine/host_node_test.dart @@ -0,0 +1,94 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + final html.Element rootNode = html.document.createElement('div'); + html.document.body!.append(rootNode); + + group('ShadowDomHostNode', () { + final HostNode hostNode = ShadowDomHostNode(rootNode); + + test('Initializes and attaches a shadow root', () { + expect(hostNode.node, isA()); + expect((hostNode.node as html.ShadowRoot).host, rootNode); + expect(hostNode.node, rootNode.shadowRoot); + }); + + test('Attaches a stylesheet to the shadow root', () { + final html.Element firstChild = + (hostNode.node as html.ShadowRoot).children.first; + + expect(firstChild.tagName, equalsIgnoringCase('style')); + }); + + _runDomTests(hostNode); + }); + + group('ElementHostNode', () { + final HostNode hostNode = ElementHostNode(rootNode); + + test('Initializes and attaches a child element', () { + expect(hostNode.node, isA()); + expect((hostNode.node as html.Element).shadowRoot, isNull); + expect(hostNode.node.parent, rootNode); + }); + + _runDomTests(hostNode); + }); +} + +// The common test suite that all types of HostNode implementations need to pass. +void _runDomTests(HostNode hostNode) { + group('DOM operations', () { + final html.Element target = html.document.createElement('div')..id = 'yep'; + + setUp(() { + hostNode.nodes.addAll([ + html.document.createElement('div'), + target, + html.document.createElement('span'), + html.document.createElement('div'), + ]); + }); + + tearDown(() { + hostNode.nodes.clear(); + }); + + test('querySelector', () { + final html.Element? found = hostNode.querySelector('#yep'); + + expect(identical(found, target), isTrue); + }); + + test('.contains and .append', () { + final html.Element another = html.document.createElement('div') + ..id = 'another'; + + expect(hostNode.contains(target), isTrue); + expect(hostNode.contains(another), isFalse); + expect(hostNode.contains(null), isFalse); + + hostNode.append(another); + expect(hostNode.contains(another), isTrue); + }); + + test('querySelectorAll', () { + final List found = hostNode.querySelectorAll('div'); + + expect(found.length, 3); + expect(found[1], target); + }); + }); +} diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart index e8fbcbb05b404..ac3cfeb0b2cf8 100644 --- a/lib/web_ui/test/engine/image/html_image_codec_test.dart +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -2,20 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; +import 'package:ui/src/engine/html_image_codec.dart'; import 'package:ui/ui.dart' as ui; -import 'package:ui/src/engine.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { +Future testMain() async { await ui.webOnlyInitializeTestDomRenderer(); group('HtmCodec', () { test('supports raw images - RGBA8888', () async { @@ -63,8 +62,16 @@ void testMain() async { expect(frameInfo.image.width, 100); expect(frameInfo.image.toString(), '[100×100]'); }); + test('dispose image image', () async { + final HtmlCodec codec = HtmlCodec('sample_image1.png'); + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + expect(frameInfo.image, isNotNull); + expect(frameInfo.image.debugDisposed, isFalse); + frameInfo.image.dispose(); + expect(frameInfo.image.debugDisposed, isTrue); + }); test('provides image loading progress', () async { - StringBuffer buffer = new StringBuffer(); + final StringBuffer buffer = StringBuffer(); final HtmlCodec codec = HtmlCodec('sample_image1.png', chunkCallback: (int loaded, int total) { buffer.write('$loaded/$total,'); @@ -72,23 +79,41 @@ void testMain() async { await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); + + /// Regression test for Firefox/ie11 + /// https://github.com/flutter/flutter/issues/66412 + test('Returns nonzero natural width/height', () async { + final HtmlCodec codec = HtmlCodec( + 'data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9I' + 'jAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dG' + 'l0bGU+QWJzdHJhY3QgaWNvbjwvdGl0bGU+PHBhdGggZD0iTTEyIDBjOS42MDEgMCAx' + 'MiAyLjM5OSAxMiAxMiAwIDkuNjAxLTIuMzk5IDEyLTEyIDEyLTkuNjAxIDAtMTItMi' + '4zOTktMTItMTJDMCAyLjM5OSAyLjM5OSAwIDEyIDB6bS0xLjk2OSAxOC41NjRjMi41' + 'MjQuMDAzIDQuNjA0LTIuMDcgNC42MDktNC41OTUgMC0yLjUyMS0yLjA3NC00LjU5NS' + '00LjU5NS00LjU5NVM1LjQ1IDExLjQ0OSA1LjQ1IDEzLjk2OWMwIDIuNTE2IDIuMDY1' + 'IDQuNTg4IDQuNTgxIDQuNTk1em04LjM0NC0uMTg5VjUuNjI1SDUuNjI1djIuMjQ3aD' + 'EwLjQ5OHYxMC41MDNoMi4yNTJ6bS04LjM0NC02Ljc0OGEyLjM0MyAyLjM0MyAwIDEx' + 'LS4wMDIgNC42ODYgMi4zNDMgMi4zNDMgMCAwMS4wMDItNC42ODZ6Ii8+PC9zdmc+'); + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + expect(frameInfo.image.width, isNot(0)); + }); }); group('ImageCodecUrl', () { test('loads sample image from web', () async { final Uri uri = Uri.base.resolve('sample_image1.png'); - final HtmlCodec codec = await ui.webOnlyInstantiateImageCodecFromUrl(uri); + final HtmlCodec codec = await ui.webOnlyInstantiateImageCodecFromUrl(uri) as HtmlCodec; final ui.FrameInfo frameInfo = await codec.getNextFrame(); expect(frameInfo.image, isNotNull); expect(frameInfo.image.width, 100); }); test('provides image loading progress from web', () async { final Uri uri = Uri.base.resolve('sample_image1.png'); - StringBuffer buffer = new StringBuffer(); + final StringBuffer buffer = StringBuffer(); final HtmlCodec codec = await ui.webOnlyInstantiateImageCodecFromUrl(uri, chunkCallback: (int loaded, int total) { buffer.write('$loaded/$total,'); - }); + }) as HtmlCodec; await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); diff --git a/lib/web_ui/test/engine/image_to_byte_data_test.dart b/lib/web_ui/test/engine/image_to_byte_data_test.dart index 14140a4af16f7..a6d7bea6ffa3c 100644 --- a/lib/web_ui/test/engine/image_to_byte_data_test.dart +++ b/lib/web_ui/test/engine/image_to_byte_data_test.dart @@ -2,20 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:typed_data'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { +Future testMain() async { setUp(() async { await webOnlyInitializePlatform(); webOnlyFontCollection.debugRegisterTestFonts(); @@ -23,21 +21,21 @@ void testMain() async { }); test('Picture.toImage().toByteData()', () async { - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = - recorder.beginRecording(Rect.fromLTRB(0, 0, 2, 2)); - canvas.drawColor(Color(0xFFCCDD00), BlendMode.srcOver); + recorder.beginRecording(const Rect.fromLTRB(0, 0, 2, 2)); + canvas.drawColor(const Color(0xFFCCDD00), BlendMode.srcOver); final Picture testPicture = recorder.endRecording(); final Image testImage = await testPicture.toImage(2, 2); final ByteData bytes = - await testImage.toByteData(format: ImageByteFormat.rawRgba); + (await testImage.toByteData(format: ImageByteFormat.rawRgba))!; expect( bytes.buffer.asUint32List(), [0xFF00DDCC, 0xFF00DDCC, 0xFF00DDCC, 0xFF00DDCC], ); final ByteData pngBytes = - await testImage.toByteData(format: ImageByteFormat.png); + (await testImage.toByteData(format: ImageByteFormat.png))!; // PNG-encoding is browser-specific, but the header is standard. We only // test the header. diff --git a/lib/web_ui/test/engine/navigation_test.dart b/lib/web_ui/test/engine/navigation_test.dart index 44d3bf2939e95..ad45995073707 100644 --- a/lib/web_ui/test/engine/navigation_test.dart +++ b/lib/web_ui/test/engine/navigation_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:typed_data'; @@ -10,8 +9,6 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart' as engine; -engine.TestLocationStrategy _strategy; - const engine.MethodCodec codec = engine.JSONMethodCodec(); void emptyCallback(ByteData date) {} @@ -21,12 +18,16 @@ void main() { } void testMain() { - setUp(() { - engine.window.locationStrategy = _strategy = engine.TestLocationStrategy(); + engine.TestUrlStrategy? _strategy; + + setUp(() async { + _strategy = engine.TestUrlStrategy(); + await engine.window.debugInitializeHistory(_strategy, useSingle: true); }); - tearDown(() { - engine.window.locationStrategy = _strategy = null; + tearDown(() async { + _strategy = null; + await engine.window.resetHistory(); }); test('Tracks pushed, replaced and popped routes', () async { @@ -40,6 +41,6 @@ void testMain() { (_) => completer.complete(), ); await completer.future; - expect(_strategy.path, '/foo'); + expect(_strategy!.getPath(), '/foo'); }); } diff --git a/lib/web_ui/test/engine/path_metrics_test.dart b/lib/web_ui/test/engine/path_metrics_test.dart index a8938f913d99f..924ee64527818 100644 --- a/lib/web_ui/test/engine/path_metrics_test.dart +++ b/lib/web_ui/test/engine/path_metrics_test.dart @@ -19,12 +19,12 @@ void main() { void testMain() { group('PathMetric length', () { test('empty path', () { - Path path = Path(); + final Path path = Path(); expect(path.computeMetrics().isEmpty, isTrue); }); test('simple line', () { - Path path = Path(); + final Path path = Path(); path.moveTo(100.0, 50.0); path.lineTo(200.0, 100.0); expect(path.computeMetrics().isEmpty, isFalse); @@ -34,7 +34,7 @@ void testMain() { }); test('2 lines', () { - Path path = Path(); + final Path path = Path(); path.moveTo(100.0, 50.0); path.lineTo(200.0, 50.0); path.lineTo(100.0, 200.0); @@ -45,7 +45,7 @@ void testMain() { }); test('2 lines forceClosed', () { - Path path = Path(); + final Path path = Path(); path.moveTo(100.0, 50.0); path.lineTo(200.0, 50.0); path.lineTo(100.0, 200.0); @@ -57,7 +57,7 @@ void testMain() { }); test('2 subpaths', () { - Path path = Path(); + final Path path = Path(); path.moveTo(100.0, 50.0); path.lineTo(200.0, 100.0); path.moveTo(200.0, 100.0); @@ -69,7 +69,7 @@ void testMain() { }); test('quadratic curve', () { - Path path = Path(); + final Path path = Path(); path.moveTo(20, 100); path.quadraticBezierTo(80, 10, 140, 110); final List contourLengths = computeLengths(path.computeMetrics()); @@ -78,7 +78,7 @@ void testMain() { }); test('cubic curve', () { - Path path = Path(); + final Path path = Path(); path.moveTo(20, 100); path.cubicTo(80, 10, 120, 90, 140, 40); final List contourLengths = computeLengths(path.computeMetrics()); @@ -87,24 +87,24 @@ void testMain() { }); test('addRect', () { - Path path = Path(); - path.addRect(Rect.fromLTRB(20, 30, 220, 130)); + final Path path = Path(); + path.addRect(const Rect.fromLTRB(20, 30, 220, 130)); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 600.0)); }); test('addRRect with zero radius', () { - Path path = Path(); - path.addRRect(RRect.fromLTRBR(20, 30, 220, 130, Radius.circular(0))); + final Path path = Path(); + path.addRRect(RRect.fromLTRBR(20, 30, 220, 130, const Radius.circular(0))); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 600.0)); }); test('addRRect with elliptical radius', () { - Path path = Path(); - path.addRRect(RRect.fromLTRBR(20, 30, 220, 130, Radius.elliptical(8, 4))); + final Path path = Path(); + path.addRRect(RRect.fromLTRBR(20, 30, 220, 130, const Radius.elliptical(8, 4))); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 590.408)); @@ -117,15 +117,15 @@ void testMain() { const double cy = 100; const double startAngle = 0.0; const double endAngle = 90.0; - double startRad = startAngle * math.pi / 180.0; - double endRad = endAngle * math.pi / 180.0; + const double startRad = startAngle * math.pi / 180.0; + const double endRad = endAngle * math.pi / 180.0; final double startX = cx + (rx * math.cos(startRad)); final double startY = cy + (ry * math.sin(startRad)); final double endX = cx + (rx * math.cos(endRad)); final double endY = cy + (ry * math.sin(endRad)); - final bool clockwise = endAngle > startAngle; + const bool clockwise = endAngle > startAngle; final bool largeArc = (endAngle - startAngle).abs() > 180.0; final Path path = Path() ..moveTo(startX, startY) @@ -146,15 +146,15 @@ void testMain() { const double cy = 100; const double startAngle = 0.0; const double endAngle = 180.0; - double startRad = startAngle * math.pi / 180.0; - double endRad = endAngle * math.pi / 180.0; + const double startRad = startAngle * math.pi / 180.0; + const double endRad = endAngle * math.pi / 180.0; final double startX = cx + (rx * math.cos(startRad)); final double startY = cy + (ry * math.sin(startRad)); final double endX = cx + (rx * math.cos(endRad)); final double endY = cy + (ry * math.sin(endRad)); - final bool clockwise = endAngle > startAngle; + const bool clockwise = endAngle > startAngle; final bool largeArc = (endAngle - startAngle).abs() > 180.0; final Path path = Path() ..moveTo(startX, startY) @@ -175,15 +175,15 @@ void testMain() { const double cy = 100; const double startAngle = 0.0; const double endAngle = 270.0; - double startRad = startAngle * math.pi / 180.0; - double endRad = endAngle * math.pi / 180.0; + const double startRad = startAngle * math.pi / 180.0; + const double endRad = endAngle * math.pi / 180.0; final double startX = cx + (rx * math.cos(startRad)); final double startY = cy + (ry * math.sin(startRad)); final double endX = cx + (rx * math.cos(endRad)); final double endY = cy + (ry * math.sin(endRad)); - final bool clockwise = endAngle > startAngle; + const bool clockwise = endAngle > startAngle; final bool largeArc = (endAngle - startAngle).abs() > 180.0; final Path path = Path() ..moveTo(startX, startY) @@ -204,15 +204,15 @@ void testMain() { const double cy = 100; const double startAngle = 0.0; const double endAngle = 270.0; - double startRad = startAngle * math.pi / 180.0; - double endRad = endAngle * math.pi / 180.0; + const double startRad = startAngle * math.pi / 180.0; + const double endRad = endAngle * math.pi / 180.0; final double startX = cx + (rx * math.cos(startRad)); final double startY = cy + (ry * math.sin(startRad)); final double endX = cx + (rx * math.cos(endRad)); final double endY = cy + (ry * math.sin(endRad)); - final bool clockwise = endAngle > startAngle; + const bool clockwise = endAngle > startAngle; final bool largeArc = (endAngle - startAngle).abs() > 180.0; final Path path = Path() ..moveTo(startX, startY) @@ -230,7 +230,7 @@ void testMain() { List computeLengths(PathMetrics pathMetrics) { final List lengths = []; - for (PathMetric metric in pathMetrics) { + for (final PathMetric metric in pathMetrics) { lengths.add(metric.length); } return lengths; diff --git a/lib/web_ui/test/engine/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher_test.dart new file mode 100644 index 0000000000000..eb2bcdd9ddb6f --- /dev/null +++ b/lib/web_ui/test/engine/platform_dispatcher_test.dart @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('PlatformDispatcher', () { + test('responds to flutter/skia Skia.setResourceCacheMaxBytes', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/skia', + codec.encodeMethodCall(const MethodCall( + 'Skia.setResourceCacheMaxBytes', + 512 * 1000 * 1000, + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + [true], + ); + }); + + test('responds to flutter/platform HapticFeedback.vibrate', () async { + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'HapticFeedback.vibrate', + )), + completer.complete, + ); + + final ByteData? response = await completer.future; + expect(response, isNotNull); + expect( + codec.decodeEnvelope(response!), + true, + ); + }); + + test('responds correctly to flutter/platform Clipboard.getData failure', + () async { + // Patch browser so that clipboard api is not available. + final dynamic originalClipboard = + js_util.getProperty(html.window.navigator, 'clipboard'); + js_util.setProperty(html.window.navigator, 'clipboard', null); + const MethodCodec codec = JSONMethodCodec(); + final Completer completer = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/platform', + codec.encodeMethodCall(const MethodCall( + 'Clipboard.getData', + )), + completer.complete, + ); + final ByteData? response = await completer.future; + if (response != null) { + expect( + () => codec.decodeEnvelope(response), + throwsA(isA()), + ); + } + js_util.setProperty( + html.window.navigator, 'clipboard', originalClipboard); + }); + }); +} diff --git a/lib/web_ui/test/engine/platform_views/content_manager_test.dart b/lib/web_ui/test/engine/platform_views/content_manager_test.dart new file mode 100644 index 0000000000000..2025e5a6ac461 --- /dev/null +++ b/lib/web_ui/test/engine/platform_views/content_manager_test.dart @@ -0,0 +1,147 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +import '../../matchers.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('PlatformViewManager', () { + const String viewType = 'forTest'; + const int viewId = 6; + + late PlatformViewManager contentManager; + + setUp(() { + contentManager = PlatformViewManager(); + }); + + group('knowsViewType', () { + test('recognizes viewTypes after registering them', () async { + expect(contentManager.knowsViewType(viewType), isFalse); + + contentManager.registerFactory(viewType, (int id) => html.DivElement()); + + expect(contentManager.knowsViewType(viewType), isTrue); + }); + }); + + group('knowsViewId', () { + test('recognizes viewIds after *rendering* them', () async { + expect(contentManager.knowsViewId(viewId), isFalse); + + contentManager.registerFactory(viewType, (int id) => html.DivElement()); + + expect(contentManager.knowsViewId(viewId), isFalse); + + contentManager.renderContent(viewType, viewId, null); + + expect(contentManager.knowsViewId(viewId), isTrue); + }); + + test('forgets viewIds after clearing them', () { + contentManager.registerFactory(viewType, (int id) => html.DivElement()); + contentManager.renderContent(viewType, viewId, null); + + expect(contentManager.knowsViewId(viewId), isTrue); + + contentManager.clearPlatformView(viewId); + + expect(contentManager.knowsViewId(viewId), isFalse); + }); + }); + + group('registerFactory', () { + test('does NOT re-register factories', () async { + contentManager.registerFactory( + viewType, (int id) => html.DivElement()..id = 'pass'); + // this should be rejected + contentManager.registerFactory( + viewType, (int id) => html.SpanElement()..id = 'fail'); + + final html.Element contents = + contentManager.renderContent(viewType, viewId, null); + + expect(contents.querySelector('#pass'), isNotNull); + expect(contents.querySelector('#fail'), isNull, + reason: 'Factories cannot be overridden once registered'); + }); + }); + + group('renderContent', () { + const String unregisteredViewType = 'unregisteredForTest'; + const String anotherViewType = 'anotherViewType'; + + setUp(() { + contentManager.registerFactory(viewType, (int id) { + return html.DivElement()..setAttribute('data-viewId', '$id'); + }); + + contentManager.registerFactory(anotherViewType, (int id) { + return html.DivElement() + ..setAttribute('data-viewId', '$id') + ..style.height = 'auto' + ..style.width = '55%'; + }); + }); + + test('refuse to render views for unregistered factories', () async { + try { + contentManager.renderContent(unregisteredViewType, viewId, null); + fail('renderContent should have thrown an Assertion error!'); + } catch (e) { + expect(e, isAssertionError); + expect((e as AssertionError).message, contains(unregisteredViewType)); + } + }); + + test('rendered markup contains required attributes', () async { + final html.Element content = + contentManager.renderContent(viewType, viewId, null); + expect(content.getAttribute('slot'), contains('$viewId')); + + final html.Element userContent = content.querySelector('div')!; + expect(userContent.style.height, '100%'); + expect(userContent.style.width, '100%'); + }); + + test('slot property has the same value as createPlatformViewSlot', () async { + final html.Element content = + contentManager.renderContent(viewType, viewId, null); + final html.Element slot = createPlatformViewSlot(viewId); + final html.Element innerSlot = slot.querySelector('slot')!; + + expect(content.getAttribute('slot'), innerSlot.getAttribute('name'), + reason: + 'The slot attribute of the rendered content must match the name attribute of the SLOT of a given viewId'); + }); + + test('do not modify style.height / style.width if passed by the user (anotherViewType)', + () async { + final html.Element content = + contentManager.renderContent(anotherViewType, viewId, null); + final html.Element userContent = content.querySelector('div')!; + expect(userContent.style.height, 'auto'); + expect(userContent.style.width, '55%'); + }); + + test('returns cached instances of already-rendered content', () async { + final html.Element firstRender = + contentManager.renderContent(viewType, viewId, null); + final html.Element anotherRender = + contentManager.renderContent(viewType, viewId, null); + + expect(firstRender, same(anotherRender)); + }); + }); + }); +} diff --git a/lib/web_ui/test/engine/platform_views/message_handler_test.dart b/lib/web_ui/test/engine/platform_views/message_handler_test.dart new file mode 100644 index 0000000000000..54d14bc300833 --- /dev/null +++ b/lib/web_ui/test/engine/platform_views/message_handler_test.dart @@ -0,0 +1,180 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +const MethodCodec codec = StandardMethodCodec(); + +void testMain() { + group('PlatformViewMessageHandler', () { + group('handlePlatformViewCall', () { + const String viewType = 'forTest'; + const int viewId = 6; + late PlatformViewManager contentManager; + late Completer completer; + late Completer contentCompleter; + + setUp(() { + contentManager = PlatformViewManager(); + completer = Completer(); + contentCompleter = Completer(); + }); + + group('"create" message', () { + test('unregistered viewType, fails with descriptive exception', + () async { + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: contentManager, + ); + final ByteData? message = _getCreateMessage(viewType, viewId); + + messageHandler.handlePlatformViewCall(message, completer.complete); + + final ByteData? response = await completer.future; + try { + codec.decodeEnvelope(response!); + } on PlatformException catch (e) { + expect(e.code, 'unregistered_view_type'); + expect(e.details, contains(viewType)); + } + }); + + test('duplicate viewId, fails with descriptive exception', () async { + contentManager.registerFactory( + viewType, (int id) => html.DivElement()); + contentManager.renderContent(viewType, viewId, null); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: contentManager, + ); + final ByteData? message = _getCreateMessage(viewType, viewId); + + messageHandler.handlePlatformViewCall(message, completer.complete); + + final ByteData? response = await completer.future; + try { + codec.decodeEnvelope(response!); + } on PlatformException catch (e) { + expect(e.code, 'recreating_view'); + expect(e.details, contains('$viewId')); + } + }); + + test('returns a successEnvelope when the view is created normally', + () async { + contentManager.registerFactory( + viewType, (int id) => html.DivElement()..id = 'success'); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: contentManager, + ); + final ByteData? message = _getCreateMessage(viewType, viewId); + + messageHandler.handlePlatformViewCall(message, completer.complete); + + final ByteData? response = await completer.future; + expect(codec.decodeEnvelope(response!), isNull, + reason: + 'The response should be a success envelope, with null in it.'); + }); + + test('calls a contentHandler with the result of creating a view', + () async { + contentManager.registerFactory( + viewType, (int id) => html.DivElement()..id = 'success'); + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: contentManager, + contentHandler: contentCompleter.complete, + ); + final ByteData? message = _getCreateMessage(viewType, viewId); + + messageHandler.handlePlatformViewCall(message, completer.complete); + + final html.Element contents = await contentCompleter.future; + final ByteData? response = await completer.future; + + expect(contents.querySelector('div#success'), isNotNull, + reason: + 'The element created by the factory should be present in the created view.'); + expect(codec.decodeEnvelope(response!), isNull, + reason: + 'The response should be a success envelope, with null in it.'); + }); + }); + + group('"dispose" message', () { + late Completer viewIdCompleter; + + setUp(() { + viewIdCompleter = Completer(); + }); + + test('never fails, even for unknown viewIds', () async { + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: contentManager, + ); + final ByteData? message = _getDisposeMessage(viewId); + + messageHandler.handlePlatformViewCall(message, completer.complete); + + final ByteData? response = await completer.future; + expect(codec.decodeEnvelope(response!), isNull, + reason: + 'The response should be a success envelope, with null in it.'); + }); + + test('never fails, even for unknown viewIds', () async { + final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( + contentManager: _FakePlatformViewManager(viewIdCompleter.complete), + ); + final ByteData? message = _getDisposeMessage(viewId); + + messageHandler.handlePlatformViewCall(message, completer.complete); + + final int disposedViewId = await viewIdCompleter.future; + expect(disposedViewId, viewId, + reason: + 'The viewId to dispose should be passed to the contentManager'); + }); + }); + }); + }); +} + +class _FakePlatformViewManager extends PlatformViewManager { + _FakePlatformViewManager(void Function(int) clearFunction) + : _clearPlatformView = clearFunction; + + void Function(int) _clearPlatformView; + + @override + void clearPlatformView(int viewId) { + return _clearPlatformView(viewId); + } +} + +ByteData? _getCreateMessage(String viewType, int viewId) { + return codec.encodeMethodCall(MethodCall( + 'create', + { + 'id': viewId, + 'viewType': viewType, + }, + )); +} + +ByteData? _getDisposeMessage(int viewId) { + return codec.encodeMethodCall(MethodCall( + 'dispose', + viewId, + )); +} diff --git a/lib/web_ui/test/engine/platform_views/slots_test.dart b/lib/web_ui/test/engine/platform_views/slots_test.dart new file mode 100644 index 0000000000000..f799e063c337b --- /dev/null +++ b/lib/web_ui/test/engine/platform_views/slots_test.dart @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('PlatformViewManager', () { + const int viewId = 6; + + group('createPlatformViewSlot', () { + test( + 'can render slot, even for views that might have never been rendered before', + () async { + final html.Element slot = createPlatformViewSlot(viewId); + expect(slot, isNotNull); + expect(slot.querySelector('slot'), isNotNull); + }); + + test('rendered markup contains required attributes', () async { + final html.Element slot = createPlatformViewSlot(viewId); + expect(slot.style.pointerEvents, 'auto', + reason: + 'Should re-enable pointer events for the contents of the view.'); + final html.Element innerSlot = slot.querySelector('slot')!; + expect(innerSlot.getAttribute('name'), contains('$viewId'), + reason: + 'The name attribute of the inner SLOT tag must refer to the viewId.'); + }); + }); + }); +} diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index 091cde33fc52f..8840f0c5feb9b 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'dart:js_util' as js_util; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine.dart' show domRenderer, window; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/dom_renderer.dart'; +import 'package:ui/src/engine/pointer_binding.dart'; import 'package:ui/ui.dart' as ui; const int _kNoButtonChange = -1; @@ -25,7 +27,7 @@ void _testEach( String description, _ContextTestBody body, ) { - for (T context in contexts) { + for (final T context in contexts) { if (context.isSupported) { test('${context.name} $description', () { body(context); @@ -35,22 +37,22 @@ void _testEach( } /// Some methods in this class are skipped for iOS-Safari. -/// TODO: https://github.com/flutter/flutter/issues/60033 -bool get isIosSafari => (browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs); +// TODO(mdebbar): https://github.com/flutter/flutter/issues/60033 +bool get isIosSafari => browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs; void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { - html.Element glassPane = domRenderer.glassPaneElement; + final html.Element glassPane = domRenderer.glassPaneElement!; + double dpi = 1.0; setUp(() { - // Touching domRenderer creates PointerBinding.instance. - domRenderer; - + ensureDomRendererInitialized(); ui.window.onPointerDataPacket = null; + dpi = window.devicePixelRatio; }); test('_PointerEventContext generates expected events', () { @@ -60,7 +62,7 @@ void testMain() { html.PointerEvent expectCorrectType(html.Event e) { expect(e.runtimeType, equals(html.PointerEvent)); - return e; + return e as html.PointerEvent; } List expectCorrectTypes(List events) { @@ -88,7 +90,7 @@ void testMain() { expect(event.client.x, equals(110)); expect(event.client.y, equals(111)); - events = expectCorrectTypes(context.multiTouchDown(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 100, clientX: 120, clientY: 121), _TouchDetails(pointer: 101, clientX: 122, clientY: 123), ])); @@ -141,7 +143,7 @@ void testMain() { expect(event.client.x, equals(214)); expect(event.client.y, equals(215)); - events = expectCorrectTypes(context.multiTouchMove(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchMove(const <_TouchDetails>[ _TouchDetails(pointer: 102, clientX: 220, clientY: 221), _TouchDetails(pointer: 103, clientX: 222, clientY: 223), ])); @@ -176,7 +178,7 @@ void testMain() { expect(event.client.x, equals(310)); expect(event.client.y, equals(311)); - events = expectCorrectTypes(context.multiTouchUp(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchUp(const <_TouchDetails>[ _TouchDetails(pointer: 104, clientX: 320, clientY: 321), _TouchDetails(pointer: 105, clientX: 322, clientY: 323), ])); @@ -202,7 +204,7 @@ void testMain() { expect(event.client.x, equals(400)); expect(event.client.y, equals(401)); - events = expectCorrectTypes(context.multiTouchCancel(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchCancel(const <_TouchDetails>[ _TouchDetails(pointer: 106, clientX: 500, clientY: 501), _TouchDetails(pointer: 107, clientX: 502, clientY: 503), ])); @@ -228,7 +230,7 @@ void testMain() { html.TouchEvent expectCorrectType(html.Event e) { expect(e.runtimeType, equals(html.TouchEvent)); - return e; + return e as html.TouchEvent; } List expectCorrectTypes(List events) { @@ -241,80 +243,80 @@ void testMain() { event = expectCorrectType(context.primaryDown(clientX: 100, clientY: 101)); expect(event.type, equals('touchstart')); - expect(event.changedTouches.length, equals(1)); - expect(event.changedTouches[0].identifier, equals(1)); - expect(event.changedTouches[0].client.x, equals(100)); - expect(event.changedTouches[0].client.y, equals(101)); + expect(event.changedTouches!.length, equals(1)); + expect(event.changedTouches![0].identifier, equals(1)); + expect(event.changedTouches![0].client.x, equals(100)); + expect(event.changedTouches![0].client.y, equals(101)); - events = expectCorrectTypes(context.multiTouchDown(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 100, clientX: 120, clientY: 121), _TouchDetails(pointer: 101, clientX: 122, clientY: 123), ])); expect(events.length, equals(1)); expect(events[0].type, equals('touchstart')); - expect(events[0].changedTouches.length, equals(2)); - expect(events[0].changedTouches[0].identifier, equals(100)); - expect(events[0].changedTouches[0].client.x, equals(120)); - expect(events[0].changedTouches[0].client.y, equals(121)); - expect(events[0].changedTouches[1].identifier, equals(101)); - expect(events[0].changedTouches[1].client.x, equals(122)); - expect(events[0].changedTouches[1].client.y, equals(123)); + expect(events[0].changedTouches!.length, equals(2)); + expect(events[0].changedTouches![0].identifier, equals(100)); + expect(events[0].changedTouches![0].client.x, equals(120)); + expect(events[0].changedTouches![0].client.y, equals(121)); + expect(events[0].changedTouches![1].identifier, equals(101)); + expect(events[0].changedTouches![1].client.x, equals(122)); + expect(events[0].changedTouches![1].client.y, equals(123)); event = expectCorrectType(context.primaryMove(clientX: 200, clientY: 201)); expect(event.type, equals('touchmove')); - expect(event.changedTouches.length, equals(1)); - expect(event.changedTouches[0].identifier, equals(1)); - expect(event.changedTouches[0].client.x, equals(200)); - expect(event.changedTouches[0].client.y, equals(201)); + expect(event.changedTouches!.length, equals(1)); + expect(event.changedTouches![0].identifier, equals(1)); + expect(event.changedTouches![0].client.x, equals(200)); + expect(event.changedTouches![0].client.y, equals(201)); - events = expectCorrectTypes(context.multiTouchMove(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchMove(const <_TouchDetails>[ _TouchDetails(pointer: 102, clientX: 220, clientY: 221), _TouchDetails(pointer: 103, clientX: 222, clientY: 223), ])); expect(events.length, equals(1)); expect(events[0].type, equals('touchmove')); - expect(events[0].changedTouches.length, equals(2)); - expect(events[0].changedTouches[0].identifier, equals(102)); - expect(events[0].changedTouches[0].client.x, equals(220)); - expect(events[0].changedTouches[0].client.y, equals(221)); - expect(events[0].changedTouches[1].identifier, equals(103)); - expect(events[0].changedTouches[1].client.x, equals(222)); - expect(events[0].changedTouches[1].client.y, equals(223)); + expect(events[0].changedTouches!.length, equals(2)); + expect(events[0].changedTouches![0].identifier, equals(102)); + expect(events[0].changedTouches![0].client.x, equals(220)); + expect(events[0].changedTouches![0].client.y, equals(221)); + expect(events[0].changedTouches![1].identifier, equals(103)); + expect(events[0].changedTouches![1].client.x, equals(222)); + expect(events[0].changedTouches![1].client.y, equals(223)); event = expectCorrectType(context.primaryUp(clientX: 300, clientY: 301)); expect(event.type, equals('touchend')); - expect(event.changedTouches.length, equals(1)); - expect(event.changedTouches[0].identifier, equals(1)); - expect(event.changedTouches[0].client.x, equals(300)); - expect(event.changedTouches[0].client.y, equals(301)); + expect(event.changedTouches!.length, equals(1)); + expect(event.changedTouches![0].identifier, equals(1)); + expect(event.changedTouches![0].client.x, equals(300)); + expect(event.changedTouches![0].client.y, equals(301)); - events = expectCorrectTypes(context.multiTouchUp(<_TouchDetails>[ + events = expectCorrectTypes(context.multiTouchUp(const <_TouchDetails>[ _TouchDetails(pointer: 104, clientX: 320, clientY: 321), _TouchDetails(pointer: 105, clientX: 322, clientY: 323), ])); expect(events.length, equals(1)); expect(events[0].type, equals('touchend')); - expect(events[0].changedTouches.length, equals(2)); - expect(events[0].changedTouches[0].identifier, equals(104)); - expect(events[0].changedTouches[0].client.x, equals(320)); - expect(events[0].changedTouches[0].client.y, equals(321)); - expect(events[0].changedTouches[1].identifier, equals(105)); - expect(events[0].changedTouches[1].client.x, equals(322)); - expect(events[0].changedTouches[1].client.y, equals(323)); - - events = expectCorrectTypes(context.multiTouchCancel(<_TouchDetails>[ + expect(events[0].changedTouches!.length, equals(2)); + expect(events[0].changedTouches![0].identifier, equals(104)); + expect(events[0].changedTouches![0].client.x, equals(320)); + expect(events[0].changedTouches![0].client.y, equals(321)); + expect(events[0].changedTouches![1].identifier, equals(105)); + expect(events[0].changedTouches![1].client.x, equals(322)); + expect(events[0].changedTouches![1].client.y, equals(323)); + + events = expectCorrectTypes(context.multiTouchCancel(const <_TouchDetails>[ _TouchDetails(pointer: 104, clientX: 320, clientY: 321), _TouchDetails(pointer: 105, clientX: 322, clientY: 323), ])); expect(events.length, equals(1)); expect(events[0].type, equals('touchcancel')); - expect(events[0].changedTouches.length, equals(2)); - expect(events[0].changedTouches[0].identifier, equals(104)); - expect(events[0].changedTouches[0].client.x, equals(320)); - expect(events[0].changedTouches[0].client.y, equals(321)); - expect(events[0].changedTouches[1].identifier, equals(105)); - expect(events[0].changedTouches[1].client.x, equals(322)); - expect(events[0].changedTouches[1].client.y, equals(323)); + expect(events[0].changedTouches!.length, equals(2)); + expect(events[0].changedTouches![0].identifier, equals(104)); + expect(events[0].changedTouches![0].client.x, equals(320)); + expect(events[0].changedTouches![0].client.y, equals(321)); + expect(events[0].changedTouches![1].identifier, equals(105)); + expect(events[0].changedTouches![1].client.x, equals(322)); + expect(events[0].changedTouches![1].client.y, equals(323)); }); test('_MouseEventContext generates expected events', () { @@ -324,7 +326,7 @@ void testMain() { html.MouseEvent expectCorrectType(html.Event e) { expect(e.runtimeType, equals(html.MouseEvent)); - return e; + return e as html.MouseEvent; } final _MouseEventContext context = _MouseEventContext(); @@ -402,15 +404,15 @@ void testMain() { // ALL ADAPTERS _testEach<_BasicEventContext>( - [ + <_BasicEventContext>[ _PointerEventContext(), _MouseEventContext(), _TouchEventContext(), ], 'can receive pointer events on the glass pane', (_BasicEventContext context) { - PointerBinding.instance.debugOverrideDetector(context); - ui.PointerDataPacket receivedPacket; + PointerBinding.instance!.debugOverrideDetector(context); + ui.PointerDataPacket? receivedPacket; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { receivedPacket = packet; }; @@ -418,20 +420,20 @@ void testMain() { glassPane.dispatchEvent(context.primaryDown()); expect(receivedPacket, isNotNull); - expect(receivedPacket.data[0].buttons, equals(1)); + expect(receivedPacket!.data[0].buttons, equals(1)); }, ); _testEach<_BasicEventContext>( - [ + <_BasicEventContext>[ _PointerEventContext(), _MouseEventContext(), _TouchEventContext(), ], 'does create an add event if got a pointerdown', (_BasicEventContext context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -447,14 +449,14 @@ void testMain() { ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _MouseEventContext(), ], 'correctly detects events on the semantics placeholder', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -472,8 +474,8 @@ void testMain() { expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].physicalX, equals(10.0)); - expect(packets[0].data[1].physicalY, equals(10.0)); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); packets.clear(); // Drag on the semantics placeholder. @@ -484,8 +486,8 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(12.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].physicalX, equals(12.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); packets.clear(); // Keep dragging. @@ -495,8 +497,8 @@ void testMain() { )); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(15.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].physicalX, equals(15.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); packets.clear(); // Release the pointer on the semantics placeholder. @@ -507,11 +509,11 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(100.0)); - expect(packets[0].data[0].physicalY, equals(200.0)); + expect(packets[0].data[0].physicalX, equals(100.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(200.0 * dpi)); expect(packets[0].data[1].change, equals(ui.PointerChange.up)); - expect(packets[0].data[1].physicalX, equals(100.0)); - expect(packets[0].data[1].physicalY, equals(200.0)); + expect(packets[0].data[1].physicalX, equals(100.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(200.0 * dpi)); packets.clear(); semanticsPlaceholder.remove(); @@ -521,14 +523,14 @@ void testMain() { // BUTTONED ADAPTERS _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'creates an add event if the first pointer activity is a hover', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -539,20 +541,20 @@ void testMain() { expect(packets.single.data, hasLength(2)); expect(packets.single.data[0].change, equals(ui.PointerChange.add)); - expect(packets.single.data[0].synthesized, equals(true)); + expect(packets.single.data[0].synthesized, isTrue); expect(packets.single.data[1].change, equals(ui.PointerChange.hover)); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'sends a pointermove event instead of the second pointerdown in a row', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -565,7 +567,7 @@ void testMain() { // An add will be synthesized. expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); packets.clear(); @@ -582,30 +584,28 @@ void testMain() { ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _MouseEventContext(), ], 'does synthesize add or hover or move for scroll', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; - glassPane.dispatchEvent(html.WheelEvent( - 'wheel', - button: 1, + glassPane.dispatchEvent(context.wheel( + buttons: 0, clientX: 10, clientY: 10, deltaX: 10, deltaY: 10, )); - glassPane.dispatchEvent(html.WheelEvent( - 'wheel', - button: 1, + glassPane.dispatchEvent(context.wheel( + buttons: 0, clientX: 20, clientY: 50, deltaX: 10, @@ -619,9 +619,8 @@ void testMain() { clientY: 50.0, )); - glassPane.dispatchEvent(html.WheelEvent( - 'wheel', - button: 1, + glassPane.dispatchEvent(context.wheel( + buttons: 1, clientX: 30, clientY: 60, deltaX: 10, @@ -634,9 +633,9 @@ void testMain() { expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, equals(true)); - expect(packets[0].data[0].physicalX, equals(10.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); @@ -644,9 +643,9 @@ void testMain() { expect( packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); expect(packets[0].data[1].pointerIdentifier, equals(0)); - expect(packets[0].data[1].synthesized, equals(false)); - expect(packets[0].data[1].physicalX, equals(10.0)); - expect(packets[0].data[1].physicalY, equals(10.0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0.0)); expect(packets[0].data[1].physicalDeltaY, equals(0.0)); @@ -654,19 +653,19 @@ void testMain() { expect(packets[1].data, hasLength(2)); expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); expect(packets[1].data[0].pointerIdentifier, equals(0)); - expect(packets[1].data[0].synthesized, equals(true)); - expect(packets[1].data[0].physicalX, equals(20.0)); - expect(packets[1].data[0].physicalY, equals(50.0)); - expect(packets[1].data[0].physicalDeltaX, equals(10.0)); - expect(packets[1].data[0].physicalDeltaY, equals(40.0)); + expect(packets[1].data[0].synthesized, isTrue); + expect(packets[1].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[1].data[0].physicalY, equals(50.0 * dpi)); + expect(packets[1].data[0].physicalDeltaX, equals(10.0 * dpi)); + expect(packets[1].data[0].physicalDeltaY, equals(40.0 * dpi)); expect(packets[1].data[1].change, equals(ui.PointerChange.hover)); expect( packets[1].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); expect(packets[1].data[1].pointerIdentifier, equals(0)); - expect(packets[1].data[1].synthesized, equals(false)); - expect(packets[1].data[1].physicalX, equals(20.0)); - expect(packets[1].data[1].physicalY, equals(50.0)); + expect(packets[1].data[1].synthesized, isFalse); + expect(packets[1].data[1].physicalX, equals(20.0 * dpi)); + expect(packets[1].data[1].physicalY, equals(50.0 * dpi)); expect(packets[1].data[1].physicalDeltaX, equals(0.0)); expect(packets[1].data[1].physicalDeltaY, equals(0.0)); @@ -675,9 +674,9 @@ void testMain() { expect(packets[2].data[0].change, equals(ui.PointerChange.down)); expect(packets[2].data[0].signalKind, equals(ui.PointerSignalKind.none)); expect(packets[2].data[0].pointerIdentifier, equals(1)); - expect(packets[2].data[0].synthesized, equals(false)); - expect(packets[2].data[0].physicalX, equals(20.0)); - expect(packets[2].data[0].physicalY, equals(50.0)); + expect(packets[2].data[0].synthesized, isFalse); + expect(packets[2].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[2].data[0].physicalY, equals(50.0 * dpi)); expect(packets[2].data[0].physicalDeltaX, equals(0.0)); expect(packets[2].data[0].physicalDeltaY, equals(0.0)); @@ -685,33 +684,33 @@ void testMain() { expect(packets[3].data, hasLength(2)); expect(packets[3].data[0].change, equals(ui.PointerChange.move)); expect(packets[3].data[0].pointerIdentifier, equals(1)); - expect(packets[3].data[0].synthesized, equals(true)); - expect(packets[3].data[0].physicalX, equals(30.0)); - expect(packets[3].data[0].physicalY, equals(60.0)); - expect(packets[3].data[0].physicalDeltaX, equals(10.0)); - expect(packets[3].data[0].physicalDeltaY, equals(10.0)); + expect(packets[3].data[0].synthesized, isTrue); + expect(packets[3].data[0].physicalX, equals(30.0 * dpi)); + expect(packets[3].data[0].physicalY, equals(60.0 * dpi)); + expect(packets[3].data[0].physicalDeltaX, equals(10.0 * dpi)); + expect(packets[3].data[0].physicalDeltaY, equals(10.0 * dpi)); expect(packets[3].data[1].change, equals(ui.PointerChange.hover)); expect( packets[3].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); expect(packets[3].data[1].pointerIdentifier, equals(1)); - expect(packets[3].data[1].synthesized, equals(false)); - expect(packets[3].data[1].physicalX, equals(30.0)); - expect(packets[3].data[1].physicalY, equals(60.0)); + expect(packets[3].data[1].synthesized, isFalse); + expect(packets[3].data[1].physicalX, equals(30.0 * dpi)); + expect(packets[3].data[1].physicalY, equals(60.0 * dpi)); expect(packets[3].data[1].physicalDeltaX, equals(0.0)); expect(packets[3].data[1].physicalDeltaY, equals(0.0)); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _MouseEventContext() ], 'does calculate delta and pointer identifier correctly', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -724,17 +723,17 @@ void testMain() { expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, equals(true)); - expect(packets[0].data[0].physicalX, equals(10.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); expect(packets[0].data[1].pointerIdentifier, equals(0)); - expect(packets[0].data[1].synthesized, equals(false)); - expect(packets[0].data[1].physicalX, equals(10.0)); - expect(packets[0].data[1].physicalY, equals(10.0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0.0)); expect(packets[0].data[1].physicalDeltaY, equals(0.0)); packets.clear(); @@ -747,11 +746,11 @@ void testMain() { expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20.0)); - expect(packets[0].data[0].physicalY, equals(20.0)); - expect(packets[0].data[0].physicalDeltaX, equals(10.0)); - expect(packets[0].data[0].physicalDeltaY, equals(10.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); packets.clear(); glassPane.dispatchEvent(context.primaryDown( @@ -762,9 +761,9 @@ void testMain() { expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.down)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20.0)); - expect(packets[0].data[0].physicalY, equals(20.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); packets.clear(); @@ -777,11 +776,11 @@ void testMain() { expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(40.0)); - expect(packets[0].data[0].physicalY, equals(30.0)); - expect(packets[0].data[0].physicalDeltaX, equals(20.0)); - expect(packets[0].data[0].physicalDeltaY, equals(10.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); packets.clear(); glassPane.dispatchEvent(context.primaryUp( @@ -792,9 +791,9 @@ void testMain() { expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(40.0)); - expect(packets[0].data[0].physicalY, equals(30.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); packets.clear(); @@ -807,11 +806,11 @@ void testMain() { expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); - expect(packets[0].data[0].physicalDeltaX, equals(-20.0)); - expect(packets[0].data[0].physicalDeltaY, equals(-20.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(-20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(-20.0 * dpi)); packets.clear(); glassPane.dispatchEvent(context.primaryDown( @@ -822,9 +821,9 @@ void testMain() { expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.down)); expect(packets[0].data[0].pointerIdentifier, equals(2)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); packets.clear(); @@ -832,14 +831,14 @@ void testMain() { ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _MouseEventContext(), ], 'correctly converts buttons of down, move and up events', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -854,14 +853,14 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); - expect(packets[0].data[0].physicalX, equals(10)); - expect(packets[0].data[0].physicalY, equals(11)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10 * dpi)); + expect(packets[0].data[0].physicalY, equals(11 * dpi)); expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].synthesized, equals(false)); - expect(packets[0].data[1].physicalX, equals(10)); - expect(packets[0].data[1].physicalY, equals(11)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10 * dpi)); + expect(packets[0].data[1].physicalY, equals(11 * dpi)); expect(packets[0].data[1].buttons, equals(0)); packets.clear(); @@ -874,9 +873,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(10)); - expect(packets[0].data[0].physicalY, equals(11)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(10 * dpi)); + expect(packets[0].data[0].physicalY, equals(11 * dpi)); expect(packets[0].data[0].buttons, equals(1)); packets.clear(); @@ -889,9 +888,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20)); - expect(packets[0].data[0].physicalY, equals(21)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); expect(packets[0].data[0].buttons, equals(1)); packets.clear(); @@ -903,9 +902,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20)); - expect(packets[0].data[0].physicalY, equals(21)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); @@ -919,9 +918,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20)); - expect(packets[0].data[0].physicalY, equals(21)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); expect(packets[0].data[0].buttons, equals(2)); packets.clear(); @@ -934,9 +933,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(30)); - expect(packets[0].data[0].physicalY, equals(31)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(30 * dpi)); + expect(packets[0].data[0].physicalY, equals(31 * dpi)); expect(packets[0].data[0].buttons, equals(2)); packets.clear(); @@ -948,9 +947,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(30)); - expect(packets[0].data[0].physicalY, equals(31)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(30 * dpi)); + expect(packets[0].data[0].physicalY, equals(31 * dpi)); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); @@ -964,9 +963,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(30)); - expect(packets[0].data[0].physicalY, equals(31)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(30 * dpi)); + expect(packets[0].data[0].physicalY, equals(31 * dpi)); expect(packets[0].data[0].buttons, equals(4)); packets.clear(); @@ -979,9 +978,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(40)); - expect(packets[0].data[0].physicalY, equals(41)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40 * dpi)); + expect(packets[0].data[0].physicalY, equals(41 * dpi)); expect(packets[0].data[0].buttons, equals(4)); packets.clear(); @@ -993,23 +992,23 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(40)); - expect(packets[0].data[0].physicalY, equals(41)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40 * dpi)); + expect(packets[0].data[0].physicalY, equals(41 * dpi)); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles button changes during a down sequence', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1022,10 +1021,10 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(1)); packets.clear(); @@ -1037,7 +1036,7 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(5)); packets.clear(); @@ -1049,7 +1048,7 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(4)); packets.clear(); @@ -1060,24 +1059,24 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _MouseEventContext(), ], 'synthesizes a pointerup event when pointermove comes before the up', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen when the user pops up the context menu by right // clicking, then dismisses it with a left click. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1092,14 +1091,14 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); - expect(packets[0].data[0].physicalX, equals(10)); - expect(packets[0].data[0].physicalY, equals(11)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10 * dpi)); + expect(packets[0].data[0].physicalY, equals(11 * dpi)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); - expect(packets[0].data[1].physicalX, equals(10)); - expect(packets[0].data[1].physicalY, equals(11)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10 * dpi)); + expect(packets[0].data[1].physicalY, equals(11 * dpi)); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1112,9 +1111,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20)); - expect(packets[0].data[0].physicalY, equals(21)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); expect(packets[0].data[0].buttons, equals(2)); packets.clear(); @@ -1127,9 +1126,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20)); - expect(packets[0].data[0].physicalY, equals(21)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); expect(packets[0].data[0].buttons, equals(2)); packets.clear(); @@ -1141,29 +1140,29 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(20)); - expect(packets[0].data[0].physicalY, equals(21)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles uncontinuous button changes during a down sequence', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen with the following gesture sequence: // // - Pops up the context menu by right clicking, but holds RMB; // - Clicks LMB; // - Releases RMB. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1176,10 +1175,10 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1193,7 +1192,7 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(3)); packets.clear(); @@ -1205,7 +1204,7 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(2)); packets.clear(); @@ -1216,25 +1215,76 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }, + ); + + _testEach<_ButtonedEventMixin>( + <_ButtonedEventMixin>[ + _PointerEventContext(), + ], + 'correctly handles missing right mouse button up when followed by move', + (_ButtonedEventMixin context) { + PointerBinding.instance!.debugOverrideDetector(context); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking; + // - Clicks LMB to close context menu. + // - Moves mouse. + + final List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + // Press RMB popping up the context menu, then release by LMB down and up. + // Browser won't send up event in that case. + glassPane.dispatchEvent(context.mouseDown( + button: 2, + buttons: 2, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); + + // User now hovers. + glassPane.dispatchEvent(context.mouseMove( + button: _kNoButtonChange, + buttons: 0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'handles RMB click when the browser sends it as a move', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // When the user clicks the RMB and moves the mouse quickly (before the // context menu shows up), the browser sends a move event before down. // The move event will have "button:-1, buttons:2". - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1249,29 +1299,29 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles hover after RMB click', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen with the following gesture sequence: // // - Pops up the context menu by right clicking, but holds RMB; // - Move the pointer to hover. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1286,10 +1336,10 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1301,22 +1351,28 @@ void testMain() { clientY: 20.0, )); expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); + expect(packets[0].data, hasLength(3)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[0].buttons, equals(2)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[2].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[2].synthesized, isFalse); + expect(packets[0].data[2].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles LMB click after RMB click', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen with the following gesture sequence: // // - Pops up the context menu by right clicking, but holds RMB; @@ -1328,7 +1384,7 @@ void testMain() { // `pointermove`/`mousemove` events. Then when the LMB click comes in, it // could be in a different location without any `*move` events in between. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1343,10 +1399,10 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1360,7 +1416,7 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(3)); packets.clear(); @@ -1372,26 +1428,26 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles two consecutive RMB clicks with no up in between', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen with the following gesture sequence: // // - Pops up the context menu by right clicking, but holds RMB; // - Clicks RMB again in a different location; - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1406,15 +1462,15 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); // Press RMB again. In Chrome, when RMB is clicked again while the // context menu is still active, it sends a pointerdown/mousedown event - // with "buttons:0". + // with "buttons:0". We convert this to pointer up, pointer down. glassPane.dispatchEvent(context.mouseDown( button: 2, buttons: 0, @@ -1422,10 +1478,18 @@ void testMain() { clientY: 20.0, )); expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); + expect(packets[0].data, hasLength(3)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[0].buttons, equals(2)); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[2].change, equals(ui.PointerChange.down)); + expect(packets[0].data[2].synthesized, isFalse); + expect(packets[0].data[2].buttons, equals(2)); packets.clear(); // Release RMB. @@ -1437,20 +1501,20 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles two consecutive RMB clicks with up in between', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen with the following gesture sequence: // // - Pops up the context menu by right clicking, but doesn't hold RMB; @@ -1458,7 +1522,7 @@ void testMain() { // // This seems to be happening sometimes when using RMB on the Mac trackpad. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1473,9 +1537,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1488,7 +1552,7 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); @@ -1504,10 +1568,10 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[0].buttons, equals(0)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1520,20 +1584,20 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, equals(false)); + expect(packets[0].data[0].synthesized, isFalse); expect(packets[0].data[0].buttons, equals(0)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ _PointerEventContext(), _MouseEventContext(), ], 'correctly handles two consecutive RMB clicks in two different locations', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen with the following gesture sequence: // // - Pops up the context menu by right clicking; @@ -1544,7 +1608,7 @@ void testMain() { // cases, the browser actually sends an `up` event for the RMB click even // when the context menu is shown. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1559,9 +1623,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); @@ -1587,27 +1651,111 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[0].buttons, equals(0)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, equals(false)); + expect(packets[0].data[1].synthesized, isFalse); expect(packets[0].data[1].buttons, equals(2)); packets.clear(); }, ); _testEach<_ButtonedEventMixin>( - [ + <_ButtonedEventMixin>[ + _PointerEventContext(), + _MouseEventContext(), + ], + 'handles overlapping left/right down and up events', + (_ButtonedEventMixin context) { + PointerBinding.instance!.debugOverrideDetector(context); + // This can happen with the following gesture sequence: + // + // LMB: down-------------------up + // RMB: down------------------up + // Flutter: down-------move-------move-------up + + final List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + // Press and hold LMB. + glassPane.dispatchEvent(context.mouseDown( + button: 0, + buttons: 1, + clientX: 5.0, + clientY: 100.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(1)); + expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(100.0 * dpi)); + packets.clear(); + + // Press and hold RMB. The pointer is already down, so we only send a move + // to update the position of the pointer. + glassPane.dispatchEvent(context.mouseDown( + button: 2, + buttons: 3, + clientX: 20.0, + clientY: 100.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].buttons, equals(3)); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); + packets.clear(); + + // Release LMB. The pointer is still down (RMB), so we only send a move to + // update the position of the pointer. + glassPane.dispatchEvent(context.mouseUp( + button: 0, + buttons: 2, + clientX: 30.0, + clientY: 100.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].buttons, equals(2)); + expect(packets[0].data[0].physicalX, equals(30.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); + packets.clear(); + + // Release RMB. There's no more buttons down, so we send an up event. + glassPane.dispatchEvent(context.mouseUp( + button: 2, + buttons: 0, + clientX: 30.0, + clientY: 100.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }, + ); + + _testEach<_ButtonedEventMixin>( + <_ButtonedEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _MouseEventContext(), ], 'correctly detects up event outside of glasspane', (_ButtonedEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); + PointerBinding.instance!.debugOverrideDetector(context); // This can happen when the up event occurs while the mouse is outside the // browser window. - List packets = []; + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; @@ -1639,8 +1787,9 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(900.0)); - expect(packets[0].data[0].physicalY, equals(1900.0)); + expect(packets[0].data[0].physicalX, + equals(900.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(1900.0 * dpi)); packets.clear(); // Release outside the glasspane. @@ -1651,11 +1800,11 @@ void testMain() { expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(1000.0)); - expect(packets[0].data[0].physicalY, equals(2000.0)); + expect(packets[0].data[0].physicalX, equals(1000.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(2000.0 * dpi)); expect(packets[0].data[1].change, equals(ui.PointerChange.up)); - expect(packets[0].data[1].physicalX, equals(1000.0)); - expect(packets[0].data[1].physicalY, equals(2000.0)); + expect(packets[0].data[1].physicalX, equals(1000.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(2000.0 * dpi)); packets.clear(); }, ); @@ -1663,21 +1812,21 @@ void testMain() { // MULTIPOINTER ADAPTERS _testEach<_MultiPointerEventMixin>( - [ + <_MultiPointerEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _TouchEventContext(), ], 'treats each pointer separately', (_MultiPointerEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; List data; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; // Two pointers down - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 2, clientX: 100, clientY: 101), _TouchDetails(pointer: 3, clientX: 200, clientY: 201), ]).forEach(glassPane.dispatchEvent); @@ -1695,36 +1844,36 @@ void testMain() { data = _allPointerData(packets); expect(data, hasLength(4)); expect(data[0].change, equals(ui.PointerChange.add)); - expect(data[0].synthesized, equals(true)); + expect(data[0].synthesized, isTrue); expect(data[0].device, equals(2)); - expect(data[0].physicalX, equals(100)); - expect(data[0].physicalY, equals(101)); + expect(data[0].physicalX, equals(100 * dpi)); + expect(data[0].physicalY, equals(101 * dpi)); expect(data[1].change, equals(ui.PointerChange.down)); expect(data[1].device, equals(2)); expect(data[1].buttons, equals(1)); - expect(data[1].physicalX, equals(100)); - expect(data[1].physicalY, equals(101)); + expect(data[1].physicalX, equals(100 * dpi)); + expect(data[1].physicalY, equals(101 * dpi)); expect(data[1].physicalDeltaX, equals(0)); expect(data[1].physicalDeltaY, equals(0)); expect(data[2].change, equals(ui.PointerChange.add)); - expect(data[2].synthesized, equals(true)); + expect(data[2].synthesized, isTrue); expect(data[2].device, equals(3)); - expect(data[2].physicalX, equals(200)); - expect(data[2].physicalY, equals(201)); + expect(data[2].physicalX, equals(200 * dpi)); + expect(data[2].physicalY, equals(201 * dpi)); expect(data[3].change, equals(ui.PointerChange.down)); expect(data[3].device, equals(3)); expect(data[3].buttons, equals(1)); - expect(data[3].physicalX, equals(200)); - expect(data[3].physicalY, equals(201)); + expect(data[3].physicalX, equals(200 * dpi)); + expect(data[3].physicalY, equals(201 * dpi)); expect(data[3].physicalDeltaX, equals(0)); expect(data[3].physicalDeltaY, equals(0)); packets.clear(); // Two pointers move - context.multiTouchMove(<_TouchDetails>[ + context.multiTouchMove(const <_TouchDetails>[ _TouchDetails(pointer: 3, clientX: 300, clientY: 302), _TouchDetails(pointer: 2, clientX: 400, clientY: 402), ]).forEach(glassPane.dispatchEvent); @@ -1744,22 +1893,22 @@ void testMain() { expect(data[0].change, equals(ui.PointerChange.move)); expect(data[0].device, equals(3)); expect(data[0].buttons, equals(1)); - expect(data[0].physicalX, equals(300)); - expect(data[0].physicalY, equals(302)); - expect(data[0].physicalDeltaX, equals(100)); - expect(data[0].physicalDeltaY, equals(101)); + expect(data[0].physicalX, equals(300 * dpi)); + expect(data[0].physicalY, equals(302 * dpi)); + expect(data[0].physicalDeltaX, equals(100 * dpi)); + expect(data[0].physicalDeltaY, equals(101 * dpi)); expect(data[1].change, equals(ui.PointerChange.move)); expect(data[1].device, equals(2)); expect(data[1].buttons, equals(1)); - expect(data[1].physicalX, equals(400)); - expect(data[1].physicalY, equals(402)); - expect(data[1].physicalDeltaX, equals(300)); - expect(data[1].physicalDeltaY, equals(301)); + expect(data[1].physicalX, equals(400 * dpi)); + expect(data[1].physicalY, equals(402 * dpi)); + expect(data[1].physicalDeltaX, equals(300 * dpi)); + expect(data[1].physicalDeltaY, equals(301 * dpi)); packets.clear(); // One pointer up - context.multiTouchUp(<_TouchDetails>[ + context.multiTouchUp(const <_TouchDetails>[ _TouchDetails(pointer: 3, clientX: 300, clientY: 302), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); @@ -1767,22 +1916,22 @@ void testMain() { expect(packets[0].data[0].change, equals(ui.PointerChange.up)); expect(packets[0].data[0].device, equals(3)); expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(300)); - expect(packets[0].data[0].physicalY, equals(302)); + expect(packets[0].data[0].physicalX, equals(300 * dpi)); + expect(packets[0].data[0].physicalY, equals(302 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0)); expect(packets[0].data[0].physicalDeltaY, equals(0)); expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); expect(packets[0].data[1].device, equals(3)); expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[1].physicalX, equals(300)); - expect(packets[0].data[1].physicalY, equals(302)); + expect(packets[0].data[1].physicalX, equals(300 * dpi)); + expect(packets[0].data[1].physicalY, equals(302 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0)); expect(packets[0].data[1].physicalDeltaY, equals(0)); packets.clear(); // Another pointer up - context.multiTouchUp(<_TouchDetails>[ + context.multiTouchUp(const <_TouchDetails>[ _TouchDetails(pointer: 2, clientX: 400, clientY: 402), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); @@ -1790,22 +1939,22 @@ void testMain() { expect(packets[0].data[0].change, equals(ui.PointerChange.up)); expect(packets[0].data[0].device, equals(2)); expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(400)); - expect(packets[0].data[0].physicalY, equals(402)); + expect(packets[0].data[0].physicalX, equals(400 * dpi)); + expect(packets[0].data[0].physicalY, equals(402 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0)); expect(packets[0].data[0].physicalDeltaY, equals(0)); expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); expect(packets[0].data[1].device, equals(2)); expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[1].physicalX, equals(400)); - expect(packets[0].data[1].physicalY, equals(402)); + expect(packets[0].data[1].physicalX, equals(400 * dpi)); + expect(packets[0].data[1].physicalY, equals(402 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0)); expect(packets[0].data[1].physicalDeltaY, equals(0)); packets.clear(); // Again two pointers down (reuse pointer ID) - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 3, clientX: 500, clientY: 501), _TouchDetails(pointer: 2, clientX: 600, clientY: 601), ]).forEach(glassPane.dispatchEvent); @@ -1823,30 +1972,30 @@ void testMain() { data = _allPointerData(packets); expect(data, hasLength(4)); expect(data[0].change, equals(ui.PointerChange.add)); - expect(data[0].synthesized, equals(true)); + expect(data[0].synthesized, isTrue); expect(data[0].device, equals(3)); - expect(data[0].physicalX, equals(500)); - expect(data[0].physicalY, equals(501)); + expect(data[0].physicalX, equals(500 * dpi)); + expect(data[0].physicalY, equals(501 * dpi)); expect(data[1].change, equals(ui.PointerChange.down)); expect(data[1].device, equals(3)); expect(data[1].buttons, equals(1)); - expect(data[1].physicalX, equals(500)); - expect(data[1].physicalY, equals(501)); + expect(data[1].physicalX, equals(500 * dpi)); + expect(data[1].physicalY, equals(501 * dpi)); expect(data[1].physicalDeltaX, equals(0)); expect(data[1].physicalDeltaY, equals(0)); expect(data[2].change, equals(ui.PointerChange.add)); - expect(data[2].synthesized, equals(true)); + expect(data[2].synthesized, isTrue); expect(data[2].device, equals(2)); - expect(data[2].physicalX, equals(600)); - expect(data[2].physicalY, equals(601)); + expect(data[2].physicalX, equals(600 * dpi)); + expect(data[2].physicalY, equals(601 * dpi)); expect(data[3].change, equals(ui.PointerChange.down)); expect(data[3].device, equals(2)); expect(data[3].buttons, equals(1)); - expect(data[3].physicalX, equals(600)); - expect(data[3].physicalY, equals(601)); + expect(data[3].physicalX, equals(600 * dpi)); + expect(data[3].physicalY, equals(601 * dpi)); expect(data[3].physicalDeltaX, equals(0)); expect(data[3].physicalDeltaY, equals(0)); packets.clear(); @@ -1854,27 +2003,27 @@ void testMain() { ); _testEach<_MultiPointerEventMixin>( - [ + <_MultiPointerEventMixin>[ if (!isIosSafari) _PointerEventContext(), if (!isIosSafari) _TouchEventContext(), ], 'correctly parses cancel event', (_MultiPointerEventMixin context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; // Two pointers down - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 2, clientX: 100, clientY: 101), _TouchDetails(pointer: 3, clientX: 200, clientY: 201), ]).forEach(glassPane.dispatchEvent); packets.clear(); // Down event is tested in other tests. // One pointer cancel - context.multiTouchCancel(<_TouchDetails>[ + context.multiTouchCancel(const <_TouchDetails>[ _TouchDetails(pointer: 3, clientX: 300, clientY: 302), ]).forEach(glassPane.dispatchEvent); expect(packets.length, 1); @@ -1882,16 +2031,16 @@ void testMain() { expect(packets[0].data[0].change, equals(ui.PointerChange.cancel)); expect(packets[0].data[0].device, equals(3)); expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(200)); - expect(packets[0].data[0].physicalY, equals(201)); + expect(packets[0].data[0].physicalX, equals(200 * dpi)); + expect(packets[0].data[0].physicalY, equals(201 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0)); expect(packets[0].data[0].physicalDeltaY, equals(0)); expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); expect(packets[0].data[1].device, equals(3)); expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[1].physicalX, equals(200)); - expect(packets[0].data[1].physicalY, equals(201)); + expect(packets[0].data[1].physicalX, equals(200 * dpi)); + expect(packets[0].data[1].physicalY, equals(201 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0)); expect(packets[0].data[1].physicalDeltaY, equals(0)); packets.clear(); @@ -1901,38 +2050,38 @@ void testMain() { // POINTER ADAPTER _testEach<_PointerEventContext>( - [ + <_PointerEventContext>[ if (!isIosSafari) _PointerEventContext(), ], 'does not synthesize pointer up if from different device', (_PointerEventContext context) { - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 1, clientX: 100, clientY: 101), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); // An add will be synthesized. expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[0].device, equals(1)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); expect(packets[0].data[1].device, equals(1)); packets.clear(); - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 2, clientX: 200, clientY: 202), ]).forEach(glassPane.dispatchEvent); // An add will be synthesized. expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, equals(true)); + expect(packets[0].data[0].synthesized, isTrue); expect(packets[0].data[0].device, equals(2)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); expect(packets[0].data[1].device, equals(2)); @@ -1940,97 +2089,151 @@ void testMain() { }, ); + _testEach<_PointerEventContext>( + <_PointerEventContext>[ + _PointerEventContext(), + ], + 'handles random pointer id on up events', + (_PointerEventContext context) { + PointerBinding.instance!.debugOverrideDetector(context); + // This happens with pens that are simulated with mouse events + // (e.g. Wacom). It sends events with the pointer type "mouse", and + // assigns a random pointer ID to each event. + // + // For more info, see: https://github.com/flutter/flutter/issues/75559 + + final List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + glassPane.dispatchEvent(context.mouseDown( + pointerId: 12, + button: 0, + buttons: 1, + clientX: 10.0, + clientY: 10.0, + )); + + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(2)); + + expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[0].synthesized, isTrue); + expect(packets.single.data[1].change, equals(ui.PointerChange.down)); + packets.clear(); + + expect( + () { + glassPane.dispatchEvent(context.mouseUp( + pointerId: 41, + button: 0, + buttons: 0, + clientX: 10.0, + clientY: 10.0, + )); + }, + returnsNormally, + ); + + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(1)); + + expect(packets.single.data[0].change, equals(ui.PointerChange.up)); + }, + ); + // TOUCH ADAPTER - _testEach( - [ + _testEach<_TouchEventContext>( + <_TouchEventContext>[ if (!isIosSafari) _TouchEventContext(), ], 'does calculate delta and pointer identifier correctly', (_TouchEventContext context) { // Mouse and Pointer are in another test since these tests can involve hovering - PointerBinding.instance.debugOverrideDetector(context); - List packets = []; + PointerBinding.instance!.debugOverrideDetector(context); + final List packets = []; ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { packets.add(packet); }; - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 1, clientX: 20, clientY: 20), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(true)); - expect(packets[0].data[0].physicalX, equals(20.0)); - expect(packets[0].data[0].physicalY, equals(20.0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); expect(packets[0].data[1].pointerIdentifier, equals(1)); - expect(packets[0].data[1].synthesized, equals(false)); - expect(packets[0].data[1].physicalX, equals(20.0)); - expect(packets[0].data[1].physicalY, equals(20.0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(20.0 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0.0)); expect(packets[0].data[1].physicalDeltaY, equals(0.0)); packets.clear(); - context.multiTouchMove(<_TouchDetails>[ + context.multiTouchMove(const <_TouchDetails>[ _TouchDetails(pointer: 1, clientX: 40, clientY: 30), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(40.0)); - expect(packets[0].data[0].physicalY, equals(30.0)); - expect(packets[0].data[0].physicalDeltaX, equals(20.0)); - expect(packets[0].data[0].physicalDeltaY, equals(10.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); packets.clear(); - context.multiTouchUp(<_TouchDetails>[ + context.multiTouchUp(const <_TouchDetails>[ _TouchDetails(pointer: 1, clientX: 40, clientY: 30), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, equals(false)); - expect(packets[0].data[0].physicalX, equals(40.0)); - expect(packets[0].data[0].physicalY, equals(30.0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); expect(packets[0].data[1].pointerIdentifier, equals(1)); - expect(packets[0].data[1].synthesized, equals(true)); - expect(packets[0].data[1].physicalX, equals(40.0)); - expect(packets[0].data[1].physicalY, equals(30.0)); + expect(packets[0].data[1].synthesized, isTrue); + expect(packets[0].data[1].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(30.0 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0.0)); expect(packets[0].data[1].physicalDeltaY, equals(0.0)); packets.clear(); - context.multiTouchDown(<_TouchDetails>[ + context.multiTouchDown(const <_TouchDetails>[ _TouchDetails(pointer: 2, clientX: 20, clientY: 10), ]).forEach(glassPane.dispatchEvent); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); expect(packets[0].data[0].pointerIdentifier, equals(2)); - expect(packets[0].data[0].synthesized, equals(true)); - expect(packets[0].data[0].physicalX, equals(20.0)); - expect(packets[0].data[0].physicalY, equals(10.0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); expect(packets[0].data[0].physicalDeltaX, equals(0.0)); expect(packets[0].data[0].physicalDeltaY, equals(0.0)); expect(packets[0].data[1].change, equals(ui.PointerChange.down)); expect(packets[0].data[1].pointerIdentifier, equals(2)); - expect(packets[0].data[1].synthesized, equals(false)); - expect(packets[0].data[1].physicalX, equals(20.0)); - expect(packets[0].data[1].physicalY, equals(10.0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); expect(packets[0].data[1].physicalDeltaX, equals(0.0)); expect(packets[0].data[1].physicalDeltaY, equals(0.0)); packets.clear(); @@ -2065,19 +2268,19 @@ abstract class _BasicEventContext implements PointerSupportDetector { mixin _ButtonedEventMixin on _BasicEventContext { // Generate an event that is a mouse down with the specific buttons. html.Event mouseDown( - {double clientX, double clientY, int button, int buttons}); + {double? clientX, double? clientY, int? button, int? buttons}); // Generate an event that is a mouse drag with the specific buttons, or button // changes during the drag. // // If there is no button change, assign `button` with _kNoButtonChange. html.Event mouseMove( - {double clientX, double clientY, int button, int buttons}); + {double? clientX, double? clientY, required int button, required int buttons}); // Generate an event that releases all mouse buttons. - html.Event mouseUp({double clientX, double clientY, int button}); + html.Event mouseUp({double? clientX, double? clientY, int? button, int? buttons}); - html.Event hover({double clientX, double clientY}) { + html.Event hover({double? clientX, double? clientY}) { return mouseMove( buttons: 0, button: _kNoButtonChange, @@ -2087,7 +2290,7 @@ mixin _ButtonedEventMixin on _BasicEventContext { } @override - html.Event primaryDown({double clientX, double clientY}) { + html.Event primaryDown({double? clientX, double? clientY}) { return mouseDown( buttons: 1, button: 0, @@ -2097,7 +2300,7 @@ mixin _ButtonedEventMixin on _BasicEventContext { } @override - html.Event primaryMove({double clientX, double clientY}) { + html.Event primaryMove({double? clientX, double? clientY}) { return mouseMove( buttons: 1, button: _kNoButtonChange, @@ -2107,21 +2310,45 @@ mixin _ButtonedEventMixin on _BasicEventContext { } @override - html.Event primaryUp({double clientX, double clientY}) { + html.Event primaryUp({double? clientX, double? clientY}) { return mouseUp( button: 0, clientX: clientX, clientY: clientY, ); } + + html.Event wheel({ + required int? buttons, + required double? clientX, + required double? clientY, + required double? deltaX, + required double? deltaY, + }) { + final Function jsWheelEvent = js_util.getProperty(html.window, 'WheelEvent') as Function; + final List eventArgs = [ + 'wheel', + { + 'buttons': buttons, + 'clientX': clientX, + 'clientY': clientY, + 'deltaX': deltaX, + 'deltaY': deltaY, + } + ]; + return js_util.callConstructor( + jsWheelEvent, + js_util.jsify(eventArgs) as List, + ) as html.Event; + } } class _TouchDetails { const _TouchDetails({this.pointer, this.clientX, this.clientY}); - final int pointer; - final double clientX; - final double clientY; + final int? pointer; + final double? clientX; + final double? clientY; } mixin _MultiPointerEventMixin on _BasicEventContext { @@ -2131,7 +2358,7 @@ mixin _MultiPointerEventMixin on _BasicEventContext { List multiTouchCancel(List<_TouchDetails> touches); @override - html.Event primaryDown({double clientX, double clientY}) { + html.Event primaryDown({double? clientX, double? clientY}) { return multiTouchDown(<_TouchDetails>[ _TouchDetails( pointer: 1, @@ -2142,7 +2369,7 @@ mixin _MultiPointerEventMixin on _BasicEventContext { } @override - html.Event primaryMove({double clientX, double clientY}) { + html.Event primaryMove({double? clientX, double? clientY}) { return multiTouchMove(<_TouchDetails>[ _TouchDetails( pointer: 1, @@ -2153,7 +2380,7 @@ mixin _MultiPointerEventMixin on _BasicEventContext { } @override - html.Event primaryUp({double clientX, double clientY}) { + html.Event primaryUp({double? clientX, double? clientY}) { return multiTouchUp(<_TouchDetails>[ _TouchDetails( pointer: 1, @@ -2169,9 +2396,8 @@ mixin _MultiPointerEventMixin on _BasicEventContext { class _TouchEventContext extends _BasicEventContext with _MultiPointerEventMixin implements PointerSupportDetector { - _TouchEventContext() { + _TouchEventContext() : _target = html.document.createElement('div'); - } @override String get name => 'TouchAdapter'; @@ -2191,9 +2417,9 @@ class _TouchEventContext extends _BasicEventContext html.EventTarget _target; html.Touch _createTouch({ - int identifier, - double clientX, - double clientY, + int? identifier, + double? clientX, + double? clientY, }) { return html.Touch({ 'identifier': identifier, @@ -2265,8 +2491,12 @@ class _MouseEventContext extends _BasicEventContext bool get hasMouseEvents => true; @override - html.Event mouseDown( - {double clientX, double clientY, int button, int buttons}) { + html.Event mouseDown({ + double? clientX, + double? clientY, + int? button, + int? buttons, + }) { return _createMouseEvent( 'mousedown', buttons: buttons, @@ -2277,8 +2507,12 @@ class _MouseEventContext extends _BasicEventContext } @override - html.Event mouseMove( - {double clientX, double clientY, int button, int buttons}) { + html.Event mouseMove({ + double? clientX, + double? clientY, + required int button, + required int buttons, + }) { final bool hasButtonChange = button != _kNoButtonChange; final bool changeIsButtonDown = hasButtonChange && (buttons & convertButtonToButtons(button)) != 0; @@ -2296,10 +2530,15 @@ class _MouseEventContext extends _BasicEventContext } @override - html.Event mouseUp({double clientX, double clientY, int button}) { + html.Event mouseUp({ + double? clientX, + double? clientY, + int? button, + int? buttons, + }) { return _createMouseEvent( 'mouseup', - buttons: 0, + buttons: buttons, button: button, clientX: clientX, clientY: clientY, @@ -2308,13 +2547,13 @@ class _MouseEventContext extends _BasicEventContext html.MouseEvent _createMouseEvent( String type, { - int buttons, - int button, - double clientX, - double clientY, + int? buttons, + int? button, + double? clientX, + double? clientY, }) { final Function jsMouseEvent = - js_util.getProperty(html.window, 'MouseEvent'); + js_util.getProperty(html.window, 'MouseEvent') as Function; final List eventArgs = [ type, { @@ -2324,7 +2563,10 @@ class _MouseEventContext extends _BasicEventContext 'clientY': clientY, } ]; - return js_util.callConstructor(jsMouseEvent, js_util.jsify(eventArgs)); + return js_util.callConstructor( + jsMouseEvent, + js_util.jsify(eventArgs) as List, + ) as html.MouseEvent; } } @@ -2365,10 +2607,15 @@ class _PointerEventContext extends _BasicEventContext } @override - html.Event mouseDown( - {double clientX, double clientY, int button, int buttons}) { + html.Event mouseDown({ + double? clientX, + double? clientY, + int? button, + int? buttons, + int? pointerId = 1, + }) { return _downWithFullDetails( - pointer: 1, + pointer: pointerId, buttons: buttons, button: button, clientX: clientX, @@ -2377,13 +2624,14 @@ class _PointerEventContext extends _BasicEventContext ); } - html.Event _downWithFullDetails( - {double clientX, - double clientY, - int button, - int buttons, - int pointer, - String pointerType}) { + html.Event _downWithFullDetails({ + double? clientX, + double? clientY, + int? button, + int? buttons, + int? pointer, + String? pointerType, + }) { return html.PointerEvent('pointerdown', { 'pointerId': pointer, 'button': button, @@ -2409,10 +2657,15 @@ class _PointerEventContext extends _BasicEventContext } @override - html.Event mouseMove( - {double clientX, double clientY, int button, int buttons}) { + html.Event mouseMove({ + double? clientX, + double? clientY, + required int button, + required int buttons, + int pointerId = 1, + }) { return _moveWithFullDetails( - pointer: 1, + pointer: pointerId, buttons: buttons, button: button, clientX: clientX, @@ -2421,13 +2674,14 @@ class _PointerEventContext extends _BasicEventContext ); } - html.Event _moveWithFullDetails( - {double clientX, - double clientY, - int button, - int buttons, - int pointer, - String pointerType}) { + html.Event _moveWithFullDetails({ + double? clientX, + double? clientY, + int? button, + int? buttons, + int? pointer, + String? pointerType, + }) { return html.PointerEvent('pointermove', { 'pointerId': pointer, 'button': button, @@ -2452,26 +2706,35 @@ class _PointerEventContext extends _BasicEventContext } @override - html.Event mouseUp({double clientX, double clientY, int button}) { + html.Event mouseUp({ + double? clientX, + double? clientY, + int? button, + int? buttons, + int? pointerId = 1, + }) { return _upWithFullDetails( - pointer: 1, + pointer: pointerId, button: button, + buttons: buttons, clientX: clientX, clientY: clientY, pointerType: 'mouse', ); } - html.Event _upWithFullDetails( - {double clientX, - double clientY, - int button, - int pointer, - String pointerType}) { + html.Event _upWithFullDetails({ + double? clientX, + double? clientY, + int? button, + int? buttons, + int? pointer, + String? pointerType, + }) { return html.PointerEvent('pointerup', { 'pointerId': pointer, 'button': button, - 'buttons': 0, + 'buttons': buttons, 'clientX': clientX, 'clientY': clientY, 'pointerType': pointerType, diff --git a/lib/web_ui/test/engine/profiler_test.dart b/lib/web_ui/test/engine/profiler_test.dart index 49f11242c860d..4bce4c906c31a 100644 --- a/lib/web_ui/test/engine/profiler_test.dart +++ b/lib/web_ui/test/engine/profiler_test.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; +import 'dart:js' as js; import 'dart:js_util' as js_util; import 'package:test/bootstrap/browser.dart'; @@ -11,11 +11,23 @@ import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; +import '../spy.dart'; + void main() { internalBootstrapBrowserTest(() => testMain); } void testMain() { + group('$Profiler', () { + _profilerTests(); + }); + + group('$Instrumentation', () { + _instrumentationTests(); + }); +} + +void _profilerTests() { setUp(() { Profiler.isBenchmarkMode = true; Profiler.ensureInitialized(); @@ -60,7 +72,7 @@ void testMain() { }); expect( () => Profiler.instance.benchmark('foo', 123), - throwsA(isA()), + throwsA(isA()), ); expect(data, isEmpty); @@ -73,6 +85,52 @@ void testMain() { }); } +void _instrumentationTests() { + setUp(() { + Instrumentation.enabled = false; + }); + + tearDown(() { + Instrumentation.enabled = false; + }); + + test('when disabled throws instead of initializing', () { + expect(() => Instrumentation.instance, throwsStateError); + }); + + test('when disabled throws instead of incrementing counter', () { + Instrumentation.enabled = true; + final Instrumentation instrumentation = Instrumentation.instance; + Instrumentation.enabled = false; + expect(() => instrumentation.incrementCounter('test'), throwsStateError); + }); + + test('when enabled increments counter', () { + final ZoneSpy spy = ZoneSpy(); + spy.run(() { + Instrumentation.enabled = true; + final Instrumentation instrumentation = Instrumentation.instance; + expect(instrumentation.debugPrintTimer, isNull); + instrumentation.incrementCounter('foo'); + expect(instrumentation.debugPrintTimer, isNotNull); + instrumentation.incrementCounter('foo'); + instrumentation.incrementCounter('bar'); + expect(spy.printLog, isEmpty); + + expect(instrumentation.debugPrintTimer, isNotNull); + spy.fakeAsync.elapse(const Duration(seconds: 2)); + expect(instrumentation.debugPrintTimer, isNull); + expect(spy.printLog, hasLength(1)); + expect( + spy.printLog.single, + 'Engine counters:\n' + ' bar: 1\n' + ' foo: 2\n', + ); + }); + }); +} + class BenchmarkDatapoint { BenchmarkDatapoint(this.name, this.value); @@ -102,5 +160,11 @@ class BenchmarkDatapoint { } void jsOnBenchmark(dynamic listener) { - js_util.setProperty(html.window, '_flutter_internal_on_benchmark', listener); + js_util.setProperty( + html.window, + '_flutter_internal_on_benchmark', + listener is Function + ? js.allowInterop(listener) + : listener, + ); } diff --git a/lib/web_ui/test/engine/recording_canvas_test.dart b/lib/web_ui/test/engine/recording_canvas_test.dart index 455d59f91ada3..6097a7dd1f56c 100644 --- a/lib/web_ui/test/engine/recording_canvas_test.dart +++ b/lib/web_ui/test/engine/recording_canvas_test.dart @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; import '../mock_engine_canvas.dart'; @@ -15,9 +14,9 @@ void main() { } void testMain() { - RecordingCanvas underTest; - MockEngineCanvas mockCanvas; - final Rect screenRect = Rect.largest; + late RecordingCanvas underTest; + late MockEngineCanvas mockCanvas; + const Rect screenRect = Rect.largest; setUp(() { underTest = RecordingCanvas(screenRect); @@ -25,7 +24,7 @@ void testMain() { }); group('drawDRRect', () { - final RRect rrect = RRect.fromLTRBR(10, 10, 50, 50, Radius.circular(3)); + final RRect rrect = RRect.fromLTRBR(10, 10, 50, 50, const Radius.circular(3)); final SurfacePaint somePaint = SurfacePaint() ..color = const Color(0xFFFF0000); @@ -73,13 +72,13 @@ void testMain() { // This comes from github issue #40728 final RRect outer = RRect.fromRectAndCorners( const Rect.fromLTWH(0, 0, 88, 48), - topLeft: Radius.circular(6), - bottomLeft: Radius.circular(6)); + topLeft: const Radius.circular(6), + bottomLeft: const Radius.circular(6)); final RRect inner = outer.deflate(1); // If these assertions fail, check [_measureBorderRadius] in recording_canvas.dart - expect(inner.brRadius, equals(Radius.circular(-1))); - expect(inner.trRadius, equals(Radius.circular(-1))); + expect(inner.brRadius, equals(const Radius.circular(-1))); + expect(inner.trRadius, equals(const Radius.circular(-1))); underTest.drawDRRect(outer, inner, somePaint); underTest.endRecording(); @@ -113,63 +112,63 @@ void testMain() { 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()); + underTest.drawRect(const Rect.fromLTWH(0.0, 20.0, 10.0, 10.0), SurfacePaint()); // Outside above - underTest.drawRect(Rect.fromLTWH(20.0, 0.0, 10.0, 10.0), Paint()); + underTest.drawRect(const Rect.fromLTWH(20.0, 0.0, 10.0, 10.0), SurfacePaint()); // Visible - underTest.drawRect(Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), Paint()); + underTest.drawRect(const Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), SurfacePaint()); // Inside the layer clip rect but zero-size - underTest.drawRect(Rect.fromLTRB(20.0, 20.0, 30.0, 20.0), Paint()); + underTest.drawRect(const Rect.fromLTRB(20.0, 20.0, 30.0, 20.0), SurfacePaint()); // Inside the layer clip but clipped out by a canvas clip underTest.save(); - underTest.clipRect(Rect.fromLTWH(0, 0, 10, 10), ClipOp.intersect); - underTest.drawRect(Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), Paint()); + underTest.clipRect(const Rect.fromLTWH(0, 0, 10, 10), ClipOp.intersect); + underTest.drawRect(const Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), SurfacePaint()); underTest.restore(); // Outside to the right - underTest.drawRect(Rect.fromLTWH(40.0, 20.0, 10.0, 10.0), Paint()); + underTest.drawRect(const Rect.fromLTWH(40.0, 20.0, 10.0, 10.0), SurfacePaint()); // Outside below - underTest.drawRect(Rect.fromLTWH(20.0, 40.0, 10.0, 10.0), Paint()); + underTest.drawRect(const Rect.fromLTWH(20.0, 40.0, 10.0, 10.0), SurfacePaint()); underTest.endRecording(); expect(underTest.debugPaintCommands, hasLength(10)); - final PaintDrawRect outsideLeft = underTest.debugPaintCommands[0]; - expect(outsideLeft.isClippedOut, false); + final PaintDrawRect outsideLeft = underTest.debugPaintCommands[0] as PaintDrawRect; + expect(outsideLeft.isClippedOut, isFalse); 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 outsideAbove = underTest.debugPaintCommands[1] as PaintDrawRect; + expect(outsideAbove.isClippedOut, isFalse); - final PaintDrawRect visible = underTest.debugPaintCommands[2]; - expect(visible.isClippedOut, false); + final PaintDrawRect visible = underTest.debugPaintCommands[2] as PaintDrawRect; + expect(visible.isClippedOut, isFalse); - final PaintDrawRect zeroSize = underTest.debugPaintCommands[3]; - expect(zeroSize.isClippedOut, true); + final PaintDrawRect zeroSize = underTest.debugPaintCommands[3] as PaintDrawRect; + expect(zeroSize.isClippedOut, isTrue); expect(underTest.debugPaintCommands[4], isA()); - final PaintClipRect clip = underTest.debugPaintCommands[5]; - expect(clip.isClippedOut, false); + final PaintClipRect clip = underTest.debugPaintCommands[5] as PaintClipRect; + expect(clip.isClippedOut, isFalse); - final PaintDrawRect clippedOut = underTest.debugPaintCommands[6]; - expect(clippedOut.isClippedOut, true); + final PaintDrawRect clippedOut = underTest.debugPaintCommands[6] as PaintDrawRect; + expect(clippedOut.isClippedOut, isTrue); expect(underTest.debugPaintCommands[7], isA()); - final PaintDrawRect outsideRight = underTest.debugPaintCommands[8]; - expect(outsideRight.isClippedOut, false); + final PaintDrawRect outsideRight = underTest.debugPaintCommands[8] as PaintDrawRect; + expect(outsideRight.isClippedOut, isFalse); - final PaintDrawRect outsideBelow = underTest.debugPaintCommands[9]; - expect(outsideBelow.isClippedOut, false); + final PaintDrawRect outsideBelow = underTest.debugPaintCommands[9] as PaintDrawRect; + expect(outsideBelow.isClippedOut, isFalse); // Give it the entire screen so everything paints. underTest.apply(mockCanvas, screenRect); @@ -188,7 +187,7 @@ void testMain() { // Clip out a middle region that only contains 'drawRect' mockCanvas.methodCallLog.clear(); - underTest.apply(mockCanvas, Rect.fromLTRB(15, 15, 35, 35)); + underTest.apply(mockCanvas, const Rect.fromLTRB(15, 15, 35, 35)); expect(mockCanvas.methodCallLog, hasLength(4)); expect(mockCanvas.methodCallLog[0].methodName, 'drawRect'); expect(mockCanvas.methodCallLog[1].methodName, 'save'); @@ -198,7 +197,7 @@ void testMain() { // Regression test for https://github.com/flutter/flutter/issues/61697. test('Allows restore calls after recording has ended', () { - final RecordingCanvas rc = RecordingCanvas(Rect.fromLTRB(0, 0, 200, 400)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); rc.endRecording(); // Should not throw exception on restore. expect(() => rc.restore(), returnsNormally); @@ -206,7 +205,7 @@ void testMain() { // Regression test for https://github.com/flutter/flutter/issues/61697. test('Allows restore calls even if recording is not ended', () { - final RecordingCanvas rc = RecordingCanvas(Rect.fromLTRB(0, 0, 200, 400)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); // Should not throw exception on restore. expect(() => rc.restore(), returnsNormally); }); @@ -216,10 +215,10 @@ void testMain() { void _expectDrawDRRectCall( MockEngineCanvas mock, Map expectedArguments) { expect(mock.methodCallLog.length, equals(2)); - MockCanvasCall mockCall = mock.methodCallLog[0]; + final MockCanvasCall mockCall = mock.methodCallLog[0]; expect(mockCall.methodName, equals('drawPath')); - Map argMap = mockCall.arguments as Map; - Map argContents = {}; + final Map argMap = mockCall.arguments as Map; + final Map argContents = {}; argMap.forEach((String key, dynamic value) { argContents[key] = value is SurfacePath ? value.toString() : value; }); diff --git a/lib/web_ui/test/engine/semantics/accessibility_test.dart b/lib/web_ui/test/engine/semantics/accessibility_test.dart index 6eb3366668752..a66483b4b8f78 100644 --- a/lib/web_ui/test/engine/semantics/accessibility_test.dart +++ b/lib/web_ui/test/engine/semantics/accessibility_test.dart @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async' show Future; import 'dart:html'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/semantics.dart'; +import 'package:ui/src/engine/services.dart'; -const MessageCodec codec = StandardMessageCodec(); +const StandardMessageCodec codec = StandardMessageCodec(); const String testMessage = 'This is an tooltip.'; const Map testInput = { 'data': {'message': testMessage} @@ -21,7 +21,7 @@ void main() { } void testMain() { - AccessibilityAnnouncements accessibilityAnnouncements; + late AccessibilityAnnouncements accessibilityAnnouncements; group('$AccessibilityAnnouncements', () { setUp(() { @@ -45,7 +45,7 @@ void testMain() { isNotNull, ); final LabelElement input = - document.getElementById('accessibility-element'); + document.getElementById('accessibility-element')! as LabelElement; expect(input.getAttribute('aria-live'), equals('polite')); expect(input.text, testMessage); diff --git a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart index 56fb691d117a1..1b38d0c9d2e66 100644 --- a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/pointer_binding.dart'; +import 'package:ui/src/engine/semantics.dart'; + +const PointerSupportDetector _defaultSupportDetector = PointerSupportDetector(); void main() { internalBootstrapBrowserTest(() => testMain); @@ -15,53 +18,48 @@ void main() { void testMain() { group('$DesktopSemanticsEnabler', () { - DesktopSemanticsEnabler desktopSemanticsEnabler; - html.Element _placeholder; + late DesktopSemanticsEnabler desktopSemanticsEnabler; + late html.Element? _placeholder; setUp(() { + EngineSemanticsOwner.instance.semanticsEnabled = false; desktopSemanticsEnabler = DesktopSemanticsEnabler(); + _placeholder = desktopSemanticsEnabler.prepareAccessibilityPlaceholder(); + html.document.body!.append(_placeholder!); }); tearDown(() { - if (_placeholder != null) { - _placeholder.remove(); - } - if (desktopSemanticsEnabler?.semanticsActivationTimer != null) { - desktopSemanticsEnabler.semanticsActivationTimer.cancel(); - desktopSemanticsEnabler.semanticsActivationTimer = null; - } + expect(_placeholder, isNotNull, + reason: 'Expected the test to create a placeholder'); + _placeholder!.remove(); + EngineSemanticsOwner.instance.semanticsEnabled = false; }); - test('prepare accesibility placeholder', () async { - _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); + test('prepare accessibility placeholder', () async { + expect(_placeholder!.getAttribute('role'), 'button'); + expect(_placeholder!.getAttribute('aria-live'), 'polite'); + expect(_placeholder!.getAttribute('tabindex'), '0'); - expect(_placeholder.getAttribute('role'), 'button'); - expect(_placeholder.getAttribute('aria-live'), 'true'); - expect(_placeholder.getAttribute('tabindex'), '0'); - - html.document.body.append(_placeholder); + html.document.body!.append(_placeholder!); expect(html.document.getElementsByTagName('flt-semantics-placeholder'), isNotEmpty); - expect(_placeholder.getBoundingClientRect().height, 1); - expect(_placeholder.getBoundingClientRect().width, 1); - expect(_placeholder.getBoundingClientRect().top, -1); - expect(_placeholder.getBoundingClientRect().left, -1); - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - skip: browserEngine == BrowserEngine.webkit); + expect(_placeholder!.getBoundingClientRect().height, 1); + expect(_placeholder!.getBoundingClientRect().width, 1); + expect(_placeholder!.getBoundingClientRect().top, -1); + expect(_placeholder!.getBoundingClientRect().left, -1); + }); test('Not relevant events should be forwarded to the framework', () async { - // Prework. Attach the placeholder to dom. - _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); - html.document.body.append(_placeholder); + // Attach the placeholder to dom. + html.document.body!.append(_placeholder!); html.Event event = html.MouseEvent('mousemove'); bool shouldForwardToFramework = desktopSemanticsEnabler.tryEnableSemantics(event); - expect(shouldForwardToFramework, true); + expect(shouldForwardToFramework, isTrue); // Pointer events are not defined in webkit. if (browserEngine != BrowserEngine.webkit) { @@ -69,97 +67,106 @@ void testMain() { shouldForwardToFramework = desktopSemanticsEnabler.tryEnableSemantics(event); - expect(shouldForwardToFramework, true); + expect(shouldForwardToFramework, isTrue); } - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); - - test( - 'Relevants events targeting placeholder should not be forwarded to the framework', - () async { - // Prework. Attach the placeholder to dom. - _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); - html.document.body.append(_placeholder); - - html.Event event = html.MouseEvent('mousedown'); - _placeholder.dispatchEvent(event); - - bool shouldForwardToFramework = - desktopSemanticsEnabler.tryEnableSemantics(event); - - expect(shouldForwardToFramework, false); }); test( - 'After max number of relevant events, events should be forwarded to the framework', + 'Relevant events targeting placeholder should not be forwarded to the framework', () async { - // Prework. Attach the placeholder to dom. - _placeholder = desktopSemanticsEnabler.prepareAccesibilityPlaceholder(); - html.document.body.append(_placeholder); + final html.Event event = html.MouseEvent('mousedown'); + _placeholder!.dispatchEvent(event); - html.Event event = html.MouseEvent('mousedown'); - _placeholder.dispatchEvent(event); - - bool shouldForwardToFramework = + final bool shouldForwardToFramework = desktopSemanticsEnabler.tryEnableSemantics(event); - expect(shouldForwardToFramework, false); - - // Send max number of events; - for (int i = 1; i <= kMaxSemanticsActivationAttempts; i++) { - event = html.MouseEvent('mousedown'); - _placeholder.dispatchEvent(event); - - shouldForwardToFramework = - desktopSemanticsEnabler.tryEnableSemantics(event); - } - - expect(shouldForwardToFramework, true); + expect(shouldForwardToFramework, isFalse); }); - }); - - group('$MobileSemanticsEnabler', () { - MobileSemanticsEnabler mobileSemanticsEnabler; - html.Element _placeholder; - setUp(() { - mobileSemanticsEnabler = MobileSemanticsEnabler(); - }); + test('disposes of the placeholder', () { + html.document.body!.append(_placeholder!); - tearDown(() { - if (_placeholder != null) { - _placeholder.remove(); - } + expect(_placeholder!.isConnected, isTrue); + desktopSemanticsEnabler.dispose(); + expect(_placeholder!.isConnected, isFalse); }); - - test('prepare accesibility placeholder', () async { - _placeholder = mobileSemanticsEnabler.prepareAccesibilityPlaceholder(); - - expect(_placeholder.getAttribute('role'), 'button'); - - html.document.body.append(_placeholder); - - // Placeholder should cover all the screen on a mobile device. - final num bodyHeight = html.window.innerHeight; - final num bodyWidht = html.window.innerWidth; - - expect(_placeholder.getBoundingClientRect().height, bodyHeight); - expect(_placeholder.getBoundingClientRect().width, bodyWidht); - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - skip: browserEngine == BrowserEngine.webkit); - - test('Not relevant events should be forwarded to the framework', () async { - final html.Event event = html.TouchEvent('touchcancel'); - bool shouldForwardToFramework = - mobileSemanticsEnabler.tryEnableSemantics(event); - - expect(shouldForwardToFramework, true); + }, skip: isMobile); + + group( + '$MobileSemanticsEnabler', + () { + late MobileSemanticsEnabler mobileSemanticsEnabler; + html.Element? _placeholder; + + setUp(() { + EngineSemanticsOwner.instance.semanticsEnabled = false; + mobileSemanticsEnabler = MobileSemanticsEnabler(); + _placeholder = mobileSemanticsEnabler.prepareAccessibilityPlaceholder(); + html.document.body!.append(_placeholder!); + }); + + tearDown(() { + _placeholder!.remove(); + EngineSemanticsOwner.instance.semanticsEnabled = false; + }); + + test('prepare accessibility placeholder', () async { + expect(_placeholder!.getAttribute('role'), 'button'); + + // Placeholder should cover all the screen on a mobile device. + final num bodyHeight = html.window.innerHeight!; + final num bodyWidth = html.window.innerWidth!; + + expect(_placeholder!.getBoundingClientRect().height, bodyHeight); + expect(_placeholder!.getBoundingClientRect().width, bodyWidth); + }); + + test('Non-relevant events should be forwarded to the framework', + () async { + html.Event event; + if (_defaultSupportDetector.hasPointerEvents) { + event = html.PointerEvent('pointermove'); + } else if (_defaultSupportDetector.hasTouchEvents) { + event = html.TouchEvent('touchcancel'); + } else { + event = html.MouseEvent('mousemove'); + } + + final bool shouldForwardToFramework = + mobileSemanticsEnabler.tryEnableSemantics(event); + + expect(shouldForwardToFramework, isTrue); + }); + + test('Enables semantics when receiving a relevant event', () { + expect(mobileSemanticsEnabler.semanticsActivationTimer, isNull); + + // Send a click off center + _placeholder!.dispatchEvent(html.MouseEvent( + 'click', + clientX: 0, + clientY: 0, + )); + expect(mobileSemanticsEnabler.semanticsActivationTimer, isNull); + + // Send a click at center + final html.Rectangle activatingElementRect = + _placeholder!.getBoundingClientRect(); + final int midX = (activatingElementRect.left + + (activatingElementRect.right - activatingElementRect.left) / 2) + .toInt(); + final int midY = (activatingElementRect.top + + (activatingElementRect.bottom - activatingElementRect.top) / 2) + .toInt(); + _placeholder!.dispatchEvent(html.MouseEvent( + 'click', + clientX: midX, + clientY: midY, + )); + expect(mobileSemanticsEnabler.semanticsActivationTimer, isNotNull); + }); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine != BrowserEngine.blink); - }); + // We can run `MobileSemanticsEnabler` tests in mobile browsers and in desktop Chrome. + skip: isDesktop && browserEngine != BrowserEngine.blink, + ); } diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index d3b0e52eb0180..3af88c138d637 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -2,23 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 -@TestOn('chrome') -// TODO(nurhan): https://github.com/flutter/flutter/issues/50590 +@TestOn('chrome || safari || firefox') import 'dart:async'; import 'dart:html' as html; import 'dart:typed_data'; -import 'package:mockito/mockito.dart'; import 'package:quiver/testing/async.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine.dart' show domRenderer; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/semantics.dart'; +import 'package:ui/src/engine/vector_math.dart'; import 'package:ui/ui.dart' as ui; -import '../../matchers.dart'; +import 'semantics_tester.dart'; DateTime _testTime = DateTime(2018, 12, 17); @@ -77,30 +77,58 @@ void _testEngineSemanticsOwner() { }); test('semantics is off by default', () { - expect(semantics().semanticsEnabled, false); + expect(semantics().semanticsEnabled, isFalse); }); test('default mode is "unknown"', () { expect(semantics().mode, AccessibilityMode.unknown); }); - test('auto-enables semantics', () async { + test('placeholder enables semantics', () async { domRenderer.reset(); // triggers `autoEnableOnTap` to be called - expect(semantics().semanticsEnabled, false); + expect(semantics().semanticsEnabled, isFalse); // Synthesize a click on the placeholder. final html.Element placeholder = - html.document.querySelectorAll('flt-semantics-placeholder').single; + appHostNode.querySelector('flt-semantics-placeholder')!; + + expect(placeholder.isConnected, isTrue); + final html.Rectangle rect = placeholder.getBoundingClientRect(); placeholder.dispatchEvent(html.MouseEvent( 'click', clientX: (rect.left + (rect.right - rect.left) / 2).floor(), clientY: (rect.top + (rect.bottom - rect.top) / 2).floor(), )); - while (!semantics().semanticsEnabled) { - await Future.delayed(const Duration(milliseconds: 50)); + + // On mobile semantics is enabled asynchronously. + if (isMobile) { + while (placeholder.isConnected!) { + await Future.delayed(const Duration(milliseconds: 50)); + } } - expect(semantics().semanticsEnabled, true); + expect(semantics().semanticsEnabled, isTrue); + expect(placeholder.isConnected, isFalse); + }); + + test('auto-enables semantics', () async { + domRenderer.reset(); // triggers `autoEnableOnTap` to be called + expect(semantics().semanticsEnabled, isFalse); + + final html.Element placeholder = + appHostNode.querySelector('flt-semantics-placeholder')!; + + expect(placeholder.isConnected, isTrue); + + // Sending a semantics update should auto-enable engine semantics. + final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); + updateNode(builder, id: 0); + semantics().updateSemantics(builder.build()); + + expect(semantics().semanticsEnabled, isTrue); + + // The placeholder should be removed + expect(placeholder.isConnected, isFalse); }); void renderLabel(String label) { @@ -133,15 +161,15 @@ void _testEngineSemanticsOwner() { // Create renderLabel('Hello'); - final Map tree = semantics().debugSemanticsTree; + final Map tree = semantics().debugSemanticsTree!; expect(tree.length, 2); - expect(tree[0].id, 0); - expect(tree[0].element.tagName.toLowerCase(), 'flt-semantics'); - expect(tree[1].id, 1); - expect(tree[1].label, 'Hello'); + expect(tree[0]!.id, 0); + expect(tree[0]!.element.tagName.toLowerCase(), 'flt-semantics'); + expect(tree[1]!.id, 1); + expect(tree[1]!.label, 'Hello'); expectSemanticsTree(''' - + Hello @@ -153,7 +181,7 @@ void _testEngineSemanticsOwner() { renderLabel('World'); expectSemanticsTree(''' - + World @@ -165,16 +193,14 @@ void _testEngineSemanticsOwner() { renderLabel(''); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); + }); test('clears semantics tree when disabled', () { expect(semantics().debugSemanticsTree, isEmpty); @@ -187,7 +213,7 @@ void _testEngineSemanticsOwner() { test('accepts standalone browser gestures', () { semantics().semanticsEnabled = true; - expect(semantics().shouldAcceptBrowserGesture('click'), true); + expect(semantics().shouldAcceptBrowserGesture('click'), isTrue); semantics().semanticsEnabled = false; }); @@ -196,13 +222,13 @@ void _testEngineSemanticsOwner() { semantics() ..debugOverrideTimestampFunction(fakeAsync.getClock(_testTime).now) ..semanticsEnabled = true; - expect(semantics().shouldAcceptBrowserGesture('click'), true); + expect(semantics().shouldAcceptBrowserGesture('click'), isTrue); semantics().receiveGlobalEvent(html.Event('pointermove')); - expect(semantics().shouldAcceptBrowserGesture('click'), false); + expect(semantics().shouldAcceptBrowserGesture('click'), isFalse); // After 1 second of inactivity a browser gestures counts as standalone. fakeAsync.elapse(const Duration(seconds: 1)); - expect(semantics().shouldAcceptBrowserGesture('click'), true); + expect(semantics().shouldAcceptBrowserGesture('click'), isTrue); semantics().semanticsEnabled = false; }); }); @@ -214,21 +240,48 @@ void _testEngineSemanticsOwner() { semantics().receiveGlobalEvent(pointerEvent); // Verify the interactions. - verify(mockSemanticsEnabler.shouldEnableSemantics(pointerEvent)); + expect( + mockSemanticsEnabler.shouldEnableSemanticsEvents, + [pointerEvent], + ); }); - test('Forward events to framewors if shouldEnableSemantics', () { + test('forwards events to framework if shouldEnableSemantics returns true', () { final MockSemanticsEnabler mockSemanticsEnabler = MockSemanticsEnabler(); semantics().semanticsHelper.semanticsEnabler = mockSemanticsEnabler; final html.Event pointerEvent = html.Event('pointermove'); - when(mockSemanticsEnabler.shouldEnableSemantics(pointerEvent)) - .thenReturn(true); - + mockSemanticsEnabler.shouldEnableSemanticsReturnValue = true; expect(semantics().receiveGlobalEvent(pointerEvent), isTrue); }); } -class MockSemanticsEnabler extends Mock implements SemanticsEnabler {} +class MockSemanticsEnabler implements SemanticsEnabler { + @override + void dispose() { + } + + @override + bool get isWaitingToEnableSemantics => throw UnimplementedError(); + + @override + html.Element prepareAccessibilityPlaceholder() { + throw UnimplementedError(); + } + + bool shouldEnableSemanticsReturnValue = false; + final List shouldEnableSemanticsEvents = []; + + @override + bool shouldEnableSemantics(html.Event event) { + shouldEnableSemanticsEvents.add(event); + return shouldEnableSemanticsReturnValue; + } + + @override + bool tryEnableSemantics(html.Event event) { + throw UnimplementedError(); + } +} void _testHeader() { test('renders heading role for headers', () { @@ -249,15 +302,13 @@ void _testHeader() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + Header of the page '''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); + }); } void _testLongestIncreasingSubsequence() { @@ -327,26 +378,34 @@ void _testContainer() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); final html.Element parentElement = - html.document.querySelector('flt-semantics'); + appHostNode.querySelector('flt-semantics')!; final html.Element container = - html.document.querySelector('flt-semantics-container'); - + appHostNode.querySelector('flt-semantics-container')!; + + if (isMacOrIOS) { + expect(parentElement.style.top, '0px'); + expect(parentElement.style.left, '0px'); + expect(container.style.top, '0px'); + expect(container.style.left, '0px'); + } else { + expect(parentElement.style.top, ''); + expect(parentElement.style.left, ''); + expect(container.style.top, ''); + expect(container.style.left, ''); + } expect(parentElement.style.transform, ''); expect(parentElement.style.transformOrigin, ''); expect(container.style.transform, ''); expect(container.style.transformOrigin, ''); - semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); + }); test('container node compensates for rect offset', () async { semantics() @@ -367,26 +426,81 @@ void _testContainer() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); final html.Element parentElement = - html.document.querySelector('flt-semantics'); + appHostNode.querySelector('flt-semantics')!; final html.Element container = - html.document.querySelector('flt-semantics-container'); + appHostNode.querySelector('flt-semantics-container')!; expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 10, 10)'); expect(parentElement.style.transformOrigin, '0px 0px 0px'); - expect(container.style.transform, 'translate(-10px, -10px)'); - expect(container.style.transformOrigin, '0px 0px 0px'); + expect(container.style.top, '-10px'); + expect(container.style.left, '-10px'); + semantics().semanticsEnabled = false; + }); + + test('0 offsets are not removed for voiceover', () async { + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; + + final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); + updateNode( + builder, + id: 0, + actions: 0, + flags: 0, + transform: Matrix4.identity().toFloat64(), + rect: const ui.Rect.fromLTRB(0, 0, 20, 20), + childrenInHitTestOrder: Int32List.fromList([1]), + childrenInTraversalOrder: Int32List.fromList([1]), + ); + + semantics().updateSemantics(builder.build()); + if (browserEngine == BrowserEngine.edge) { + expectSemanticsTree(''' + + + + +'''); + } else { + expectSemanticsTree(''' + + + + +'''); + } + + final html.Element parentElement = + appHostNode.querySelector('flt-semantics')!; + final html.Element container = + appHostNode.querySelector('flt-semantics-container')!; + + if (isMacOrIOS) { + expect(parentElement.style.top, '0px'); + expect(parentElement.style.left, '0px'); + expect(container.style.top, '0px'); + expect(container.style.left, '0px'); + } else { + expect(parentElement.style.top, ''); + expect(parentElement.style.left, ''); + expect(container.style.top, ''); + expect(container.style.left, ''); + } + expect(parentElement.style.transform, ''); + expect(parentElement.style.transformOrigin, ''); + expect(container.style.transform, ''); + expect(container.style.transformOrigin, ''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); + }); } void _testVerticalScrolling() { @@ -407,15 +521,11 @@ void _testVerticalScrolling() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + }); test('scrollable node with children has a container node', () async { semantics() @@ -436,25 +546,21 @@ void _testVerticalScrolling() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); - final html.Element scrollable = findScrollable(); + final html.Element? scrollable = findScrollable(); expect(scrollable, isNotNull); // When there's less content than the available size the neutral scrollTop // is 0. - expect(scrollable.scrollTop, 0); + expect(scrollable!.scrollTop, 0); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + }); test('scrollable node dispatches scroll events', () async { final StreamController idLogController = StreamController(); @@ -470,7 +576,7 @@ void _testVerticalScrolling() { final Zone testZone = Zone.current; ui.window.onSemanticsAction = - (int id, ui.SemanticsAction action, ByteData args) { + (int id, ui.SemanticsAction action, ByteData? args) { idLogController.add(id); actionLogController.add(action); testZone.run(() { @@ -495,7 +601,7 @@ void _testVerticalScrolling() { childrenInTraversalOrder: Int32List.fromList([1, 2, 3]), ); - for (int id = 1; id <= 3; id++) { + for (int id = 1; id <= 5; id++) { updateNode( builder, id: id, @@ -508,7 +614,7 @@ void _testVerticalScrolling() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + @@ -516,33 +622,37 @@ void _testVerticalScrolling() { '''); - final html.Element scrollable = findScrollable(); + final html.Element? scrollable = findScrollable(); expect(scrollable, isNotNull); // When there's more content than the available size the neutral scrollTop - // is greater than 0 with a maximum of 10. - expect(scrollable.scrollTop, 10); + // is greater than 0 with a maximum of 10 or 9. + int browserMaxScrollDiff = 0; + // The max scroll value varies between `9` and `10` for Safari desktop + // browsers. + if (browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.macOs) { + browserMaxScrollDiff = 1; + } + + expect(scrollable!.scrollTop >= (10 - browserMaxScrollDiff), isTrue); scrollable.scrollTop = 20; expect(scrollable.scrollTop, 20); expect(await idLog.first, 0); expect(await actionLog.first, ui.SemanticsAction.scrollUp); // Engine semantics returns scroll top back to neutral. - expect(scrollable.scrollTop, 10); + expect(scrollable.scrollTop >= (10 - browserMaxScrollDiff), isTrue); scrollable.scrollTop = 5; - expect(scrollable.scrollTop, 5); + expect(scrollable.scrollTop >= (5 - browserMaxScrollDiff), isTrue); expect(await idLog.first, 0); expect(await actionLog.first, ui.SemanticsAction.scrollDown); // Engine semantics returns scroll top back to neutral. - expect(scrollable.scrollTop, 10); + expect(scrollable.scrollTop >= (10 - browserMaxScrollDiff), isTrue); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + }); } void _testHorizontalScrolling() { @@ -563,15 +673,11 @@ void _testHorizontalScrolling() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + }); test('scrollable node with children has a container node', () async { semantics() @@ -592,25 +698,21 @@ void _testHorizontalScrolling() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); - final html.Element scrollable = findScrollable(); + final html.Element? scrollable = findScrollable(); expect(scrollable, isNotNull); // When there's less content than the available size the neutral // scrollLeft is 0. - expect(scrollable.scrollLeft, 0); + expect(scrollable!.scrollLeft, 0); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + }); test('scrollable node dispatches scroll events', () async { final SemanticsActionLogger logger = SemanticsActionLogger(); @@ -645,7 +747,7 @@ void _testHorizontalScrolling() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + @@ -653,33 +755,36 @@ void _testHorizontalScrolling() { '''); - final html.Element scrollable = findScrollable(); + final html.Element? scrollable = findScrollable(); expect(scrollable, isNotNull); // When there's more content than the available size the neutral scrollTop // is greater than 0 with a maximum of 10. - expect(scrollable.scrollLeft, 10); + int browserMaxScrollDiff = 0; + // The max scroll value varies between `9` and `10` for Safari desktop + // browsers. + if (browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.macOs) { + browserMaxScrollDiff = 1; + } + expect(scrollable!.scrollLeft >= (10 - browserMaxScrollDiff), isTrue); scrollable.scrollLeft = 20; expect(scrollable.scrollLeft, 20); expect(await logger.idLog.first, 0); expect(await logger.actionLog.first, ui.SemanticsAction.scrollLeft); // Engine semantics returns scroll position back to neutral. - expect(scrollable.scrollLeft, 10); + expect(scrollable.scrollLeft >= (10 - browserMaxScrollDiff), isTrue); scrollable.scrollLeft = 5; - expect(scrollable.scrollLeft, 5); + expect(scrollable.scrollLeft >= (5 - browserMaxScrollDiff), isTrue); expect(await logger.idLog.first, 0); expect(await logger.actionLog.first, ui.SemanticsAction.scrollRight); // Engine semantics returns scroll top back to neutral. - expect(scrollable.scrollLeft, 10); + expect(scrollable.scrollLeft >= (10 - browserMaxScrollDiff), isTrue); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + }); } void _testIncrementables() { @@ -701,14 +806,12 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); + }); test('increments', () async { final SemanticsActionLogger logger = SemanticsActionLogger(); @@ -730,12 +833,11 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); - final html.InputElement input = - html.document.querySelectorAll('input').single; + final html.InputElement input = appHostNode.querySelector('input')! as html.InputElement; input.value = '2'; input.dispatchEvent(html.Event('change')); @@ -743,9 +845,7 @@ void _testIncrementables() { expect(await logger.actionLog.first, ui.SemanticsAction.increase); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); + }); test('decrements', () async { final SemanticsActionLogger logger = SemanticsActionLogger(); @@ -767,12 +867,11 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); - final html.InputElement input = - html.document.querySelectorAll('input').single; + final html.InputElement input = appHostNode.querySelector('input')! as html.InputElement; input.value = '0'; input.dispatchEvent(html.Event('change')); @@ -781,7 +880,7 @@ void _testIncrementables() { semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a node that can both increment and decrement', () async { @@ -806,13 +905,13 @@ void _testIncrementables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); } @@ -835,13 +934,13 @@ void _testTextField() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); // TODO(yjbanov): this test will need to be adjusted for Safari when we add @@ -865,23 +964,22 @@ void _testTextField() { semantics().updateSemantics(builder.build()); - final html.Element textField = html.document - .querySelectorAll('input[data-semantics-role="text-field"]') - .single; + final html.Element textField = + appHostNode.querySelector('input[data-semantics-role="text-field"]')!; - expect(html.document.activeElement, isNot(textField)); + expect(appHostNode.activeElement, isNot(textField)); textField.focus(); - expect(html.document.activeElement, textField); + expect(appHostNode.activeElement, textField); expect(await logger.idLog.first, 0); expect(await logger.actionLog.first, ui.SemanticsAction.tap); semantics().semanticsEnabled = false; - }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: (browserEngine != BrowserEngine.blink)); + }, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50590 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 + skip: browserEngine != BrowserEngine.blink); } void _testCheckables() { @@ -906,12 +1004,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a switched on disabled switch element', () async { @@ -934,12 +1032,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a switched off switch element', () async { @@ -962,12 +1060,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a checked checkbox', () async { @@ -991,12 +1089,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a checked disabled checkbox', () async { @@ -1019,12 +1117,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders an unchecked checkbox', () async { @@ -1047,12 +1145,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a checked radio button', () async { @@ -1077,12 +1175,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a checked disabled radio button', () async { @@ -1106,12 +1204,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders an unchecked checkbox', () async { @@ -1135,12 +1233,12 @@ void _testCheckables() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); } @@ -1150,27 +1248,26 @@ void _testTappable() { ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; - final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); - updateNode( - builder, + final SemanticsTester tester = SemanticsTester(semantics()); + tester.updateNode( id: 0, - actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | - ui.SemanticsFlag.hasEnabledState.index | - ui.SemanticsFlag.isEnabled.index | - ui.SemanticsFlag.isButton.index, - transform: Matrix4.identity().toFloat64(), + hasTap: true, + hasEnabledState: true, + isEnabled: true, + isButton: true, rect: const ui.Rect.fromLTRB(0, 0, 100, 50), ); + tester.apply(); - semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); + expect(tester.getSemanticsObject(0).element.tabIndex, 0); + semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders a disabled tappable widget', () async { @@ -1192,15 +1289,13 @@ void _testTappable() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.webkit || - browserEngine == BrowserEngine.edge); + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 + skip: browserEngine == BrowserEngine.edge); } void _testImage() { @@ -1222,12 +1317,12 @@ void _testImage() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders an image with a child node and with a label', () async { @@ -1250,7 +1345,7 @@ void _testImage() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + @@ -1260,7 +1355,7 @@ void _testImage() { semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders an image with no child nodes without a label', () async { @@ -1280,11 +1375,11 @@ void _testImage() { semantics().updateSemantics(builder.build()); expectSemanticsTree( - ''''''); + ''''''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('renders an image with a child node and without a label', () async { @@ -1306,7 +1401,7 @@ void _testImage() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + @@ -1316,7 +1411,7 @@ void _testImage() { semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); } @@ -1339,12 +1434,12 @@ void _testLiveRegion() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' -This is a snackbar +This is a snackbar '''); semantics().semanticsEnabled = false; }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine == BrowserEngine.edge); test('does not render a live region if there is no label', () async { @@ -1364,62 +1459,16 @@ void _testLiveRegion() { semantics().updateSemantics(builder.build()); expectSemanticsTree(''' - + '''); semantics().semanticsEnabled = false; - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50754 - skip: browserEngine == BrowserEngine.edge); -} - -void expectSemanticsTree(String semanticsHtml) { - expect( - canonicalizeHtml(html.document.querySelector('flt-semantics').outerHtml), - canonicalizeHtml(semanticsHtml), - ); -} - -html.Element findScrollable() { - return html.document.querySelectorAll('flt-semantics').firstWhere( - (html.Element element) => - element.style.overflow == 'hidden' || - element.style.overflowY == 'scroll' || - element.style.overflowX == 'scroll', - orElse: () => null, - ); -} - -class SemanticsActionLogger { - StreamController idLogController; - StreamController actionLogController; - Stream idLog; - Stream actionLog; - - SemanticsActionLogger() { - idLogController = StreamController(); - actionLogController = StreamController(); - idLog = idLogController.stream.asBroadcastStream(); - actionLog = actionLogController.stream.asBroadcastStream(); - - // The browser kicks us out of the test zone when the browser event happens. - // We memorize the test zone so we can call expect when the callback is - // fired. - final Zone testZone = Zone.current; - - ui.window.onSemanticsAction = - (int id, ui.SemanticsAction action, ByteData args) { - idLogController.add(id); - actionLogController.add(action); - testZone.run(() { - expect(args, null); - }); - }; - } + }); } /// A facade in front of [ui.SemanticsUpdateBuilder.updateNode] that /// supplies default values for semantics attributes. +// TODO(yjbanov): move this to TestSemanticsBuilder void updateNode( ui.SemanticsUpdateBuilder builder, { int id = 0, @@ -1444,10 +1493,10 @@ void updateNode( String increasedValue = '', String decreasedValue = '', ui.TextDirection textDirection = ui.TextDirection.ltr, - Float64List transform, - Int32List childrenInTraversalOrder, - Int32List childrenInHitTestOrder, - Int32List additionalActions, + Float64List? transform, + Int32List? childrenInTraversalOrder, + Int32List? childrenInHitTestOrder, + Int32List? additionalActions, }) { transform ??= Float64List.fromList(Matrix4.identity().storage); childrenInTraversalOrder ??= Int32List(0); diff --git a/lib/web_ui/test/engine/semantics/semantics_tester.dart b/lib/web_ui/test/engine/semantics/semantics_tester.dart new file mode 100644 index 0000000000000..b812c8de983bf --- /dev/null +++ b/lib/web_ui/test/engine/semantics/semantics_tester.dart @@ -0,0 +1,401 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart' show domRenderer, toMatrix32; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/host_node.dart'; +import 'package:ui/src/engine/semantics.dart'; +import 'package:ui/src/engine/util.dart'; +import 'package:ui/src/engine/vector_math.dart'; +import 'package:ui/ui.dart' as ui; + +import '../../matchers.dart'; + +/// Gets the DOM host where the Flutter app is being rendered. +/// +/// This function returns the correct host for the flutter app under testing, +/// so we don't have to hardcode html.document across the test. (The host of a +/// normal flutter app used to be html.document, but now that the app is wrapped +/// in a Shadow DOM, that's not the case anymore.) +HostNode get appHostNode => domRenderer.glassPaneShadow!; + +/// CSS style applied to the root of the semantics tree. +// TODO(yjbanov): this should be handled internally by [expectSemanticsTree]. +// No need for every test to inject it. +final String rootSemanticStyle = browserEngine != BrowserEngine.edge + ? 'filter: opacity(0%); color: rgba(0, 0, 0, 0)' + : 'color: rgba(0, 0, 0, 0); filter: opacity(0%)'; + +/// A convenience wrapper of the semantics API for building and inspecting the +/// semantics tree in unit tests. +class SemanticsTester { + SemanticsTester(this.owner); + + final EngineSemanticsOwner owner; + final List _nodeUpdates = []; + + /// Updates one semantics node. + /// + /// Provides reasonable defaults for the missing attributes, and conveniences + /// for specifying flags, such as [isTextField]. + SemanticsNodeUpdate updateNode({ + required int id, + + // Flags + int flags = 0, + bool? hasCheckedState, + bool? isChecked, + bool? isSelected, + bool? isButton, + bool? isLink, + bool? isTextField, + bool? isReadOnly, + bool? isFocusable, + bool? isFocused, + bool? hasEnabledState, + bool? isEnabled, + bool? isInMutuallyExclusiveGroup, + bool? isHeader, + bool? isObscured, + bool? scopesRoute, + bool? namesRoute, + bool? isHidden, + bool? isImage, + bool? isLiveRegion, + bool? hasToggledState, + bool? isToggled, + bool? hasImplicitScrolling, + bool? isMultiline, + bool? isSlider, + bool? isKeyboardKey, + + // Actions + int actions = 0, + bool? hasTap, + bool? hasLongPress, + bool? hasScrollLeft, + bool? hasScrollRight, + bool? hasScrollUp, + bool? hasScrollDown, + bool? hasIncrease, + bool? hasDecrease, + bool? hasShowOnScreen, + bool? hasMoveCursorForwardByCharacter, + bool? hasMoveCursorBackwardByCharacter, + bool? hasSetSelection, + bool? hasCopy, + bool? hasCut, + bool? hasPaste, + bool? hasDidGainAccessibilityFocus, + bool? hasDidLoseAccessibilityFocus, + bool? hasCustomAction, + bool? hasDismiss, + bool? hasMoveCursorForwardByWord, + bool? hasMoveCursorBackwardByWord, + bool? hasSetText, + + // Other attributes + int? maxValueLength, + int? currentValueLength, + int? textSelectionBase, + int? textSelectionExtent, + int? platformViewId, + int? scrollChildren, + int? scrollIndex, + double? scrollPosition, + double? scrollExtentMax, + double? scrollExtentMin, + double? elevation, + double? thickness, + ui.Rect? rect, + String? label, + String? hint, + String? value, + String? increasedValue, + String? decreasedValue, + ui.TextDirection? textDirection, + Float64List? transform, + Int32List? additionalActions, + List? children, + }) { + // Flags + if (hasCheckedState == true) { + flags |= ui.SemanticsFlag.hasCheckedState.index; + } + if (isChecked == true) { + flags |= ui.SemanticsFlag.isChecked.index; + } + if (isSelected == true) { + flags |= ui.SemanticsFlag.isSelected.index; + } + if (isButton == true) { + flags |= ui.SemanticsFlag.isButton.index; + } + if (isLink == true) { + flags |= ui.SemanticsFlag.isLink.index; + } + if (isTextField == true) { + flags |= ui.SemanticsFlag.isTextField.index; + } + if (isReadOnly == true) { + flags |= ui.SemanticsFlag.isReadOnly.index; + } + if (isFocusable == true) { + flags |= ui.SemanticsFlag.isFocusable.index; + } + if (isFocused == true) { + flags |= ui.SemanticsFlag.isFocused.index; + } + if (hasEnabledState == true) { + flags |= ui.SemanticsFlag.hasEnabledState.index; + } + if (isEnabled == true) { + flags |= ui.SemanticsFlag.isEnabled.index; + } + if (isInMutuallyExclusiveGroup == true) { + flags |= ui.SemanticsFlag.isInMutuallyExclusiveGroup.index; + } + if (isHeader == true) { + flags |= ui.SemanticsFlag.isHeader.index; + } + if (isObscured == true) { + flags |= ui.SemanticsFlag.isObscured.index; + } + if (scopesRoute == true) { + flags |= ui.SemanticsFlag.scopesRoute.index; + } + if (namesRoute == true) { + flags |= ui.SemanticsFlag.namesRoute.index; + } + if (isHidden == true) { + flags |= ui.SemanticsFlag.isHidden.index; + } + if (isImage == true) { + flags |= ui.SemanticsFlag.isImage.index; + } + if (isLiveRegion == true) { + flags |= ui.SemanticsFlag.isLiveRegion.index; + } + if (hasToggledState == true) { + flags |= ui.SemanticsFlag.hasToggledState.index; + } + if (isToggled == true) { + flags |= ui.SemanticsFlag.isToggled.index; + } + if (hasImplicitScrolling == true) { + flags |= ui.SemanticsFlag.hasImplicitScrolling.index; + } + if (isMultiline == true) { + flags |= ui.SemanticsFlag.isMultiline.index; + } + if (isSlider == true) { + flags |= ui.SemanticsFlag.isSlider.index; + } + if (isKeyboardKey == true) { + flags |= ui.SemanticsFlag.isKeyboardKey.index; + } + + // Actions + if (hasTap == true) { + actions |= ui.SemanticsAction.tap.index; + } + if (hasLongPress == true) { + actions |= ui.SemanticsAction.longPress.index; + } + if (hasScrollLeft == true) { + actions |= ui.SemanticsAction.scrollLeft.index; + } + if (hasScrollRight == true) { + actions |= ui.SemanticsAction.scrollRight.index; + } + if (hasScrollUp == true) { + actions |= ui.SemanticsAction.scrollUp.index; + } + if (hasScrollDown == true) { + actions |= ui.SemanticsAction.scrollDown.index; + } + if (hasIncrease == true) { + actions |= ui.SemanticsAction.increase.index; + } + if (hasDecrease == true) { + actions |= ui.SemanticsAction.decrease.index; + } + if (hasShowOnScreen == true) { + actions |= ui.SemanticsAction.showOnScreen.index; + } + if (hasMoveCursorForwardByCharacter == true) { + actions |= ui.SemanticsAction.moveCursorForwardByCharacter.index; + } + if (hasMoveCursorBackwardByCharacter == true) { + actions |= ui.SemanticsAction.moveCursorBackwardByCharacter.index; + } + if (hasSetSelection == true) { + actions |= ui.SemanticsAction.setSelection.index; + } + if (hasCopy == true) { + actions |= ui.SemanticsAction.copy.index; + } + if (hasCut == true) { + actions |= ui.SemanticsAction.cut.index; + } + if (hasPaste == true) { + actions |= ui.SemanticsAction.paste.index; + } + if (hasDidGainAccessibilityFocus == true) { + actions |= ui.SemanticsAction.didGainAccessibilityFocus.index; + } + if (hasDidLoseAccessibilityFocus == true) { + actions |= ui.SemanticsAction.didLoseAccessibilityFocus.index; + } + if (hasCustomAction == true) { + actions |= ui.SemanticsAction.customAction.index; + } + if (hasDismiss == true) { + actions |= ui.SemanticsAction.dismiss.index; + } + if (hasMoveCursorForwardByWord == true) { + actions |= ui.SemanticsAction.moveCursorForwardByWord.index; + } + if (hasMoveCursorBackwardByWord == true) { + actions |= ui.SemanticsAction.moveCursorBackwardByWord.index; + } + if (hasSetText == true) { + actions |= ui.SemanticsAction.setText.index; + } + + // Other attributes + ui.Rect childRect(SemanticsNodeUpdate child) { + return transformRect(Matrix4.fromFloat32List(child.transform), child.rect); + } + + // If a rect is not provided, generate one than covers all children. + ui.Rect effectiveRect = rect ?? ui.Rect.zero; + if (children != null && children.isNotEmpty) { + effectiveRect = childRect(children.first); + for (final SemanticsNodeUpdate child in children.skip(1)) { + effectiveRect = effectiveRect.expandToInclude(childRect(child)); + } + } + + final Int32List childIds = Int32List(children?.length ?? 0); + if (children != null) { + for (int i = 0; i < children.length; i++) { + childIds[i] = children[i].id; + } + } + + final SemanticsNodeUpdate update = SemanticsNodeUpdate( + id: id, + flags: flags, + actions: actions, + maxValueLength: maxValueLength ?? 0, + currentValueLength: currentValueLength ?? 0, + textSelectionBase: textSelectionBase ?? 0, + textSelectionExtent: textSelectionExtent ?? 0, + platformViewId: platformViewId ?? 0, + scrollChildren: scrollChildren ?? 0, + scrollIndex: scrollIndex ?? 0, + scrollPosition: scrollPosition ?? 0, + scrollExtentMax: scrollExtentMax ?? 0, + scrollExtentMin: scrollExtentMin ?? 0, + rect: effectiveRect, + label: label ?? '', + hint: hint ?? '', + value: value ?? '', + increasedValue: increasedValue ?? '', + decreasedValue: decreasedValue ?? '', + transform: transform != null ? toMatrix32(transform) : Matrix4.identity().storage, + elevation: elevation ?? 0, + thickness: thickness ?? 0, + childrenInTraversalOrder: childIds, + childrenInHitTestOrder: childIds, + additionalActions: additionalActions ?? Int32List(0), + ); + _nodeUpdates.add(update); + return update; + } + + /// Updates the HTML tree from semantics updates accumulated by this builder. + /// + /// This builder forgets previous updates and may be reused in future updates. + Map apply() { + owner.updateSemantics(SemanticsUpdate(nodeUpdates: _nodeUpdates)); + _nodeUpdates.clear(); + return owner.debugSemanticsTree!; + } + + /// Locates the semantics object with the given [id]. + SemanticsObject getSemanticsObject(int id) { + return owner.debugSemanticsTree![id]!; + } + + /// Locates the role manager of the semantics object with the give [id]. + RoleManager? getRoleManager(int id, Role role) { + return getSemanticsObject(id).debugRoleManagerFor(role); + } + + /// Locates the [TextField] role manager of the semantics object with the give [id]. + TextField getTextField(int id) { + return getRoleManager(id, Role.textField)! as TextField; + } +} + +/// Verifies the HTML structure of the current semantics tree. +void expectSemanticsTree(String semanticsHtml) { + expect( + canonicalizeHtml(appHostNode.querySelector('flt-semantics')!.outerHtml!), + canonicalizeHtml(semanticsHtml), + ); +} + +/// Finds the first HTML element in the semantics tree used for scrolling. +html.Element? findScrollable() { + return appHostNode.querySelectorAll('flt-semantics').cast().firstWhere( + (html.Element? element) => + element!.style.overflow == 'hidden' || + element.style.overflowY == 'scroll' || + element.style.overflowX == 'scroll', + orElse: () => null, + ); +} + +/// Logs semantics actions dispatched to [ui.window]. +class SemanticsActionLogger { + late StreamController _idLogController; + late StreamController _actionLogController; + + /// Semantics object ids that dispatched the actions. + Stream get idLog => _idLog; + late Stream _idLog; + + /// The actions that were dispatched to [ui.window]. + Stream get actionLog => _actionLog; + late Stream _actionLog; + + SemanticsActionLogger() { + _idLogController = StreamController(); + _actionLogController = StreamController(); + _idLog = _idLogController.stream.asBroadcastStream(); + _actionLog = _actionLogController.stream.asBroadcastStream(); + + // The browser kicks us out of the test zone when the browser event happens. + // We memorize the test zone so we can call expect when the callback is + // fired. + final Zone testZone = Zone.current; + + ui.window.onSemanticsAction = + (int id, ui.SemanticsAction action, ByteData? args) { + _idLogController.add(id); + _actionLogController.add(action); + testZone.run(() { + expect(args, null); + }); + }; + } +} diff --git a/lib/web_ui/test/engine/semantics/text_field_test.dart b/lib/web_ui/test/engine/semantics/text_field_test.dart new file mode 100644 index 0000000000000..018969d7864a1 --- /dev/null +++ b/lib/web_ui/test/engine/semantics/text_field_test.dart @@ -0,0 +1,433 @@ +// 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. + +@TestOn('chrome || safari || firefox') + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; + +import 'package:ui/src/engine.dart' hide window; +import 'package:ui/ui.dart' as ui; + +import 'semantics_tester.dart'; + +final InputConfiguration singlelineConfig = InputConfiguration( + inputType: EngineInputType.text, +); + +final InputConfiguration multilineConfig = InputConfiguration( + inputType: EngineInputType.multiline, + inputAction: 'TextInputAction.newline', +); + +EngineSemanticsOwner semantics() => EngineSemanticsOwner.instance; + +const MethodCodec codec = JSONMethodCodec(); + +DateTime _testTime = DateTime(2021, 4, 16); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + setUp(() { + EngineSemanticsOwner.debugResetSemantics(); + }); + + group('$SemanticsTextEditingStrategy', () { + late HybridTextEditing testTextEditing; + late SemanticsTextEditingStrategy strategy; + + setUp(() { + testTextEditing = HybridTextEditing(); + SemanticsTextEditingStrategy.ensureInitialized(testTextEditing); + strategy = SemanticsTextEditingStrategy.instance; + testTextEditing.debugTextEditingStrategyOverride = strategy; + testTextEditing.configuration = singlelineConfig; + }); + + test('renders a text field', () async { + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; + + createTextFieldSemantics(value: 'hello'); + + expectSemanticsTree(''' + + +'''); + + semantics().semanticsEnabled = false; + }); + + // TODO(yjbanov): this test will need to be adjusted for Safari when we add + // Safari testing. + test('sends a tap action when browser requests focus', () async { + final SemanticsActionLogger logger = SemanticsActionLogger(); + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; + + createTextFieldSemantics(value: 'hello'); + + final html.Element textField = appHostNode + .querySelector('input[data-semantics-role="text-field"]')!; + + expect(appHostNode.activeElement, isNot(textField)); + + textField.focus(); + + expect(appHostNode.activeElement, textField); + expect(await logger.idLog.first, 0); + expect(await logger.actionLog.first, ui.SemanticsAction.tap); + + semantics().semanticsEnabled = false; + }, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50590 + // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 + skip: browserEngine != BrowserEngine.blink); + + test('Syncs editing state from framework', () async { + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; + + expect(html.document.activeElement, html.document.body); + expect(appHostNode.activeElement, null); + + int changeCount = 0; + int actionCount = 0; + strategy.enable( + singlelineConfig, + onChange: (_) { + changeCount++; + }, + onAction: (_) { + actionCount++; + }, + ); + + // Create + final SemanticsObject textFieldSemantics = createTextFieldSemantics( + value: 'hello', + label: 'greeting', + isFocused: true, + rect: const ui.Rect.fromLTWH(0, 0, 10, 15), + ); + + final TextField textField = textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField; + expect(html.document.activeElement, domRenderer.glassPaneElement); + expect(appHostNode.activeElement, strategy.domElement); + expect(textField.editableElement, strategy.domElement); + expect((textField.editableElement as dynamic).value, 'hello'); + expect(textField.editableElement.getAttribute('aria-label'), 'greeting'); + expect(textField.editableElement.style.width, '10px'); + expect(textField.editableElement.style.height, '15px'); + + // Update + createTextFieldSemantics( + value: 'bye', + label: 'farewell', + isFocused: false, + rect: const ui.Rect.fromLTWH(0, 0, 12, 17), + ); + + expect(html.document.activeElement, html.document.body); + expect(appHostNode.activeElement, null); + expect(strategy.domElement, null); + expect((textField.editableElement as dynamic).value, 'bye'); + expect(textField.editableElement.getAttribute('aria-label'), 'farewell'); + expect(textField.editableElement.style.width, '12px'); + expect(textField.editableElement.style.height, '17px'); + + strategy.disable(); + semantics().semanticsEnabled = false; + + // There was no user interaction with the '''; }); }); test('pushTransform implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { return sceneBuilder.pushTransform( - Matrix4.translationValues(10, 20, 0).toFloat64(), - oldLayer: oldLayer); + (Matrix4.identity()..scale(html.window.devicePixelRatio as double)).toFloat64()); }, () { return ''''''; }); }); test('pushClipRect implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { - return sceneBuilder.pushClipRect(const Rect.fromLTRB(10, 20, 30, 40), - oldLayer: oldLayer); + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { + return sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(10, 20, 30, 40), + oldLayer: oldLayer as ui.ClipRectEngineLayer?); }, () { return ''' @@ -60,11 +56,11 @@ void testMain() { }); test('pushClipRRect implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { return sceneBuilder.pushClipRRect( - RRect.fromLTRBR(10, 20, 30, 40, const Radius.circular(3)), - oldLayer: oldLayer, - clipBehavior: Clip.none); + ui.RRect.fromLTRBR(10, 20, 30, 40, const ui.Radius.circular(3)), + oldLayer: oldLayer as ui.ClipRRectEngineLayer?, + clipBehavior: ui.Clip.none); }, () { return ''' @@ -75,9 +71,9 @@ void testMain() { }); test('pushClipPath implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { - final Path path = Path()..addRect(const Rect.fromLTRB(10, 20, 30, 40)); - return sceneBuilder.pushClipPath(path, oldLayer: oldLayer); + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { + final ui.Path path = ui.Path()..addRect(const ui.Rect.fromLTRB(10, 20, 30, 40)); + return sceneBuilder.pushClipPath(path, oldLayer: oldLayer as ui.ClipPathEngineLayer?); }, () { return ''' @@ -90,22 +86,22 @@ void testMain() { }); test('pushOpacity implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { - return sceneBuilder.pushOpacity(10, oldLayer: oldLayer); + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { + return sceneBuilder.pushOpacity(10, oldLayer: oldLayer as ui.OpacityEngineLayer?); }, () { return ''''''; }); }); test('pushPhysicalShape implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { - final Path path = Path()..addRect(const Rect.fromLTRB(10, 20, 30, 40)); + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { + final ui.Path path = ui.Path()..addRect(const ui.Rect.fromLTRB(10, 20, 30, 40)); return sceneBuilder.pushPhysicalShape( path: path, elevation: 2, - color: const Color.fromRGBO(0, 0, 0, 1), - shadowColor: const Color.fromRGBO(0, 0, 0, 1), - oldLayer: oldLayer, + color: const ui.Color.fromRGBO(0, 0, 0, 1), + shadowColor: const ui.Color.fromRGBO(0, 0, 0, 1), + oldLayer: oldLayer as ui.PhysicalShapeEngineLayer?, ); }, () { return ''''''; @@ -113,10 +109,10 @@ void testMain() { }); test('pushBackdropFilter implements surface lifecycle', () { - testLayerLifeCycle((SceneBuilder sceneBuilder, EngineLayer oldLayer) { + testLayerLifeCycle((ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer) { return sceneBuilder.pushBackdropFilter( - ImageFilter.blur(sigmaX: 1.0, sigmaY: 1.0), - oldLayer: oldLayer, + ui.ImageFilter.blur(sigmaX: 1.0, sigmaY: 1.0), + oldLayer: oldLayer as ui.BackdropFilterEngineLayer?, ); }, () { return '' @@ -133,8 +129,9 @@ void testMain() { () { final PersistedScene scene1 = PersistedScene(null); final PersistedClipRect clip1 = - PersistedClipRect(null, const Rect.fromLTRB(10, 10, 20, 20)); - final PersistedOpacity opacity = PersistedOpacity(null, 100, Offset.zero); + PersistedClipRect(null, const ui.Rect.fromLTRB(10, 10, 20, 20), + ui.Clip.antiAlias); + final PersistedOpacity opacity = PersistedOpacity(null, 100, ui.Offset.zero); final MockPersistedPicture picture = MockPersistedPicture(); scene1.appendChild(clip1); @@ -146,7 +143,7 @@ void testMain() { expect(picture.updateCount, 0); expect(picture.applyPaintCount, 0); - scene1.preroll(); + scene1.preroll(PrerollSurfaceContext()); scene1.build(); commitScene(scene1); expect(picture.retainCount, 0); @@ -158,13 +155,14 @@ void testMain() { // because the clip didn't change no repaints should happen. final PersistedScene scene2 = PersistedScene(scene1); final PersistedClipRect clip2 = - PersistedClipRect(clip1, const Rect.fromLTRB(10, 10, 20, 20)); + PersistedClipRect(clip1, const ui.Rect.fromLTRB(10, 10, 20, 20), + ui.Clip.antiAlias); clip1.state = PersistedSurfaceState.pendingUpdate; scene2.appendChild(clip2); opacity.state = PersistedSurfaceState.pendingRetention; clip2.appendChild(opacity); - scene2.preroll(); + scene2.preroll(PrerollSurfaceContext()); scene2.update(scene1); commitScene(scene2); expect(picture.retainCount, 1); @@ -176,21 +174,22 @@ void testMain() { // This should cause the picture to repaint despite being retained. final PersistedScene scene3 = PersistedScene(scene2); final PersistedClipRect clip3 = - PersistedClipRect(clip2, const Rect.fromLTRB(10, 10, 50, 50)); + PersistedClipRect(clip2, const ui.Rect.fromLTRB(10, 10, 50, 50), + ui.Clip.antiAlias); clip2.state = PersistedSurfaceState.pendingUpdate; scene3.appendChild(clip3); opacity.state = PersistedSurfaceState.pendingRetention; clip3.appendChild(opacity); - scene3.preroll(); + scene3.preroll(PrerollSurfaceContext()); scene3.update(scene2); commitScene(scene3); expect(picture.retainCount, 2); expect(picture.buildCount, 1); expect(picture.updateCount, 0); expect(picture.applyPaintCount, 2); - }, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 - skip: (browserEngine == BrowserEngine.firefox)); + }, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638 + skip: browserEngine == BrowserEngine.firefox); }); group('Compositing order', () { @@ -200,78 +199,115 @@ void testMain() { // canvas needs to have a -1 zIndex so it can preserve compositing order. test('Canvas element should retain -1 zIndex after update', () async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture picture1 = _drawPicture(); - EngineLayer oldLayer = builder.pushClipRect( - const Rect.fromLTRB(10, 10, 300, 300), + final ui.Picture picture1 = _drawPicture(); + final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 300, 300), ); - builder.addPicture(Offset.zero, picture1); + builder.addPicture(ui.Offset.zero, picture1); builder.pop(); - html.HtmlElement content = builder.build().webOnlyRootElement; - expect(content.querySelector('canvas').style.zIndex, '-1'); + final html.Element content = builder.build().webOnlyRootElement!; + expect(content.querySelector('canvas')!.style.zIndex, '-1'); // Force update to scene which will utilize reuse code path. final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); builder2.pushClipRect( - const Rect.fromLTRB(5, 10, 300, 300), + const ui.Rect.fromLTRB(5, 10, 300, 300), oldLayer: oldLayer ); - final Picture picture2 = _drawPicture(); - builder2.addPicture(Offset.zero, picture2); + final ui.Picture picture2 = _drawPicture(); + builder2.addPicture(ui.Offset.zero, picture2); builder2.pop(); - html.HtmlElement contentAfterReuse = builder2.build().webOnlyRootElement; - expect(contentAfterReuse.querySelector('canvas').style.zIndex, '-1'); + final html.Element contentAfterReuse = builder2.build().webOnlyRootElement!; + expect(contentAfterReuse.querySelector('canvas')!.style.zIndex, '-1'); }); test('Multiple canvas elements should retain zIndex after update', () async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture picture1 = _drawPathImagePath(); - EngineLayer oldLayer = builder.pushClipRect( - const Rect.fromLTRB(10, 10, 300, 300), + final ui.Picture picture1 = _drawPathImagePath(); + final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 300, 300), ); - builder.addPicture(Offset.zero, picture1); + builder.addPicture(ui.Offset.zero, picture1); builder.pop(); - html.HtmlElement content = builder.build().webOnlyRootElement; - expect(content.querySelector('canvas').style.zIndex, '-1'); + final html.Element content = builder.build().webOnlyRootElement!; + html.document.body!.append(content); + expect(content.querySelector('canvas')!.style.zIndex, '-1'); // Force update to scene which will utilize reuse code path. final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); builder2.pushClipRect( - const Rect.fromLTRB(5, 10, 300, 300), + const ui.Rect.fromLTRB(5, 10, 300, 300), oldLayer: oldLayer ); - final Picture picture2 = _drawPathImagePath(); - builder2.addPicture(Offset.zero, picture2); + final ui.Picture picture2 = _drawPathImagePath(); + builder2.addPicture(ui.Offset.zero, picture2); builder2.pop(); - html.HtmlElement contentAfterReuse = builder2.build().webOnlyRootElement; - List list = + final html.Element contentAfterReuse = builder2.build().webOnlyRootElement!; + final List list = contentAfterReuse.querySelectorAll('canvas'); expect(list[0].style.zIndex, '-1'); expect(list[1].style.zIndex, ''); }); }); - PersistedPicture findPictureSurfaceChild(PersistedContainerSurface parent) { - PersistedPicture pictureSurface; + /// Verify elementCache is passed during update to reuse existing + /// image elements. + test('Should retain same image element', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final ui.Picture picture1 = _drawPathImagePath(); + final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 300, 300), + ); + builder.addPicture(ui.Offset.zero, picture1); + builder.pop(); + + final html.Element content = builder.build().webOnlyRootElement!; + html.document.body!.append(content); + List list = content.querySelectorAll('img'); + for (final html.ImageElement image in list) { + image.alt = 'marked'; + } + + // Force update to scene which will utilize reuse code path. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushClipRect( + const ui.Rect.fromLTRB(5, 10, 300, 300), + oldLayer: oldLayer + ); + final ui.Picture picture2 = _drawPathImagePath(); + builder2.addPicture(ui.Offset.zero, picture2); + builder2.pop(); + + final html.Element contentAfterReuse = builder2.build().webOnlyRootElement!; + list = contentAfterReuse.querySelectorAll('img'); + for (final html.ImageElement image in list) { + expect(image.alt, 'marked'); + } + expect(list.length, 1); + }); + + PersistedPicture? findPictureSurfaceChild(PersistedContainerSurface parent) { + PersistedPicture? pictureSurface; parent.visitChildren((PersistedSurface child) { - pictureSurface = child; + pictureSurface = child as PersistedPicture; }); return pictureSurface; } test('skips painting picture when picture fully clipped out', () async { - final Picture picture = _drawPicture(); + final ui.Picture picture = _drawPicture(); // Picture not clipped out, so we should see a `` { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); builder.pushOffset(0, 0); - builder.addPicture(Offset.zero, picture); + builder.addPicture(ui.Offset.zero, picture); builder.pop(); - html.HtmlElement content = builder.build().webOnlyRootElement; + final html.Element content = builder.build().webOnlyRootElement!; expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); } @@ -279,51 +315,110 @@ void testMain() { { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); builder.pushOffset(0, 0); - final PersistedContainerSurface clip = builder.pushClipRect(const Rect.fromLTRB(1000, 1000, 2000, 2000)) as PersistedContainerSurface; - builder.addPicture(Offset.zero, picture); + final PersistedContainerSurface clip = builder.pushClipRect(const ui.Rect.fromLTRB(1000, 1000, 2000, 2000)) as PersistedContainerSurface; + builder.addPicture(ui.Offset.zero, picture); builder.pop(); builder.pop(); - html.HtmlElement content = builder.build().webOnlyRootElement; + final html.Element content = builder.build().webOnlyRootElement!; expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - expect(findPictureSurfaceChild(clip).debugCanvas, isNull); + expect(findPictureSurfaceChild(clip)!.canvas, isNull); } }); + test('does not skip painting picture when picture is ' + 'inside transform with offset', () async { + final ui.Picture picture = _drawPicture(); + // Picture should not be clipped out since transform will offset it to 500,500 + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushOffset(0, 0); + builder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 1000, 1000)) as PersistedContainerSurface; + builder.pushTransform((Matrix4.identity()..scale(0.5, 0.5)).toFloat64()); + builder.addPicture(const ui.Offset(1000, 1000), picture); + builder.pop(); + builder.pop(); + builder.pop(); + final html.Element content = builder.build().webOnlyRootElement!; + expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); + }); + + test('does not skip painting picture when picture is ' + 'inside transform', () async { + final ui.Picture picture = _drawPicture(); + // Picture should not be clipped out since transform will offset it to 500,500 + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushOffset(0, 0); + builder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 1000, 1000)) as PersistedContainerSurface; + builder.pushTransform((Matrix4.identity()..scale(0.5, 0.5)).toFloat64()); + builder.pushOffset(1000, 1000); + builder.addPicture(ui.Offset.zero, picture); + builder.pop(); + builder.pop(); + builder.pop(); + final html.Element content = builder.build().webOnlyRootElement!; + expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); + }); + + test( + 'skips painting picture when picture fully clipped out with' + ' transform and offset', () async { + final ui.Picture picture = _drawPicture(); + // Picture should be clipped out since transform will offset it to 500,500 + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushOffset(50, 50); + builder.pushClipRect( + const ui.Rect.fromLTRB(0, 0, 1000, 1000)) as PersistedContainerSurface; + builder.pushTransform((Matrix4.identity() + ..scale(2, 2)).toFloat64()); + builder.pushOffset(500, 500); + builder.addPicture(ui.Offset.zero, picture); + builder.pop(); + builder.pop(); + builder.pop(); + builder.pop(); + final html.Element content = builder + .build() + .webOnlyRootElement!; + expect(content + .querySelectorAll('flt-picture') + .single + .children, isEmpty); + }); + test('releases old canvas when picture is fully clipped out after addRetained', () async { - final Picture picture = _drawPicture(); + final ui.Picture picture = _drawPicture(); // Frame 1: picture visible final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); final PersistedOffset offset1 = builder1.pushOffset(0, 0) as PersistedOffset; - builder1.addPicture(Offset.zero, picture); + builder1.addPicture(ui.Offset.zero, picture); builder1.pop(); - html.HtmlElement content1 = builder1.build().webOnlyRootElement; + final html.Element content1 = builder1.build().webOnlyRootElement!; expect(content1.querySelectorAll('flt-picture').single.children, isNotEmpty); - expect(findPictureSurfaceChild(offset1).debugCanvas, isNotNull); + expect(findPictureSurfaceChild(offset1)!.canvas, isNotNull); // Frame 2: picture is clipped out after an update final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - final PersistedOffset offset2 = builder2.pushOffset(-10000, -10000, oldLayer: offset1); - builder2.addPicture(Offset.zero, picture); + final PersistedOffset offset2 = builder2.pushOffset(-10000, -10000, oldLayer: offset1) as PersistedOffset; + builder2.addPicture(ui.Offset.zero, picture); builder2.pop(); - html.HtmlElement content = builder2.build().webOnlyRootElement; + final html.Element content = builder2.build().webOnlyRootElement!; expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - expect(findPictureSurfaceChild(offset2).debugCanvas, isNull); + expect(findPictureSurfaceChild(offset2)!.canvas, isNull); }); test('releases old canvas when picture is fully clipped out after addRetained', () async { - final Picture picture = _drawPicture(); + final ui.Picture picture = _drawPicture(); // Frame 1: picture visible final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); final PersistedOffset offset1 = builder1.pushOffset(0, 0) as PersistedOffset; final PersistedOffset subOffset1 = builder1.pushOffset(0, 0) as PersistedOffset; - builder1.addPicture(Offset.zero, picture); + builder1.addPicture(ui.Offset.zero, picture); builder1.pop(); builder1.pop(); - html.HtmlElement content1 = builder1.build().webOnlyRootElement; + final html.Element content1 = builder1.build().webOnlyRootElement!; expect(content1.querySelectorAll('flt-picture').single.children, isNotEmpty); - expect(findPictureSurfaceChild(subOffset1).debugCanvas, isNotNull); + expect(findPictureSurfaceChild(subOffset1)!.canvas, isNotNull); // Frame 2: picture is clipped out after addRetained final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); @@ -334,20 +429,20 @@ void testMain() { // the clipped area. We should see the canvas being released. builder2.addRetained(subOffset1); builder2.pop(); - html.HtmlElement content = builder2.build().webOnlyRootElement; + final html.Element content = builder2.build().webOnlyRootElement!; expect(content.querySelectorAll('flt-picture').single.children, isEmpty); - expect(findPictureSurfaceChild(subOffset1).debugCanvas, isNull); + expect(findPictureSurfaceChild(subOffset1)!.canvas, isNull); }); test('auto-pops pushed layers', () async { - final Picture picture = _drawPicture(); + final ui.Picture picture = _drawPicture(); final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); builder.pushOffset(0, 0); builder.pushOffset(0, 0); builder.pushOffset(0, 0); builder.pushOffset(0, 0); builder.pushOffset(0, 0); - builder.addPicture(Offset.zero, picture); + builder.addPicture(ui.Offset.zero, picture); // Intentionally pop fewer layers than we pushed builder.pop(); @@ -355,7 +450,7 @@ void testMain() { builder.pop(); // Expect as many layers as we pushed (not popped). - html.HtmlElement content = builder.build().webOnlyRootElement; + final html.Element content = builder.build().webOnlyRootElement!; expect(content.querySelectorAll('flt-offset'), hasLength(5)); }); @@ -366,27 +461,30 @@ void testMain() { // an offset layer. Test cases use this to control how layers are reused. // Layers of the same type can be reused even if they are not explicitly // updated. Conversely, layers of different types are never reused. - EngineLayer pushChild(SurfaceSceneBuilder builder, String char, {EngineLayer oldLayer}) { + ui.EngineLayer pushChild(SurfaceSceneBuilder builder, String char, {ui.EngineLayer? oldLayer}) { // Numbers use opacity layers, letters use offset layers. This is used to // control DOM reuse. Layers of the same type can reuse DOM nodes from other // dropped layers. final bool useOffset = int.tryParse(char) == null; - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); - final Paragraph paragraph = (ParagraphBuilder(ParagraphStyle())..addText(char)).build(); - paragraph.layout(ParagraphConstraints(width: 1000)); - canvas.drawParagraph(paragraph, Offset.zero); - final EngineLayer newLayer = useOffset - ? builder.pushOffset(0, 0, oldLayer: oldLayer) - : builder.pushOpacity(100, oldLayer: oldLayer); - builder.addPicture(Offset.zero, recorder.endRecording()); + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); + final ui.Paragraph paragraph = (ui.ParagraphBuilder(ui.ParagraphStyle()) + ..pushStyle(ui.TextStyle(decoration: ui.TextDecoration.lineThrough)) + ..addText(char)) + .build(); + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + canvas.drawParagraph(paragraph, ui.Offset.zero); + final ui.EngineLayer newLayer = useOffset + ? builder.pushOffset(0, 0, oldLayer: oldLayer == null ? null : oldLayer as ui.OffsetEngineLayer) + : builder.pushOpacity(100, oldLayer: oldLayer == null ? null : oldLayer as ui.OpacityEngineLayer); + builder.addPicture(ui.Offset.zero, recorder.endRecording()); builder.pop(); return newLayer; } // Maps letters to layers used to render them in the last frame, used to // supply `oldLayer` to guarantee update. - final Map renderedLayers = {}; + final Map renderedLayers = {}; // Pump an empty scene to reset it, otherwise the first frame will attempt // to diff left-overs from a previous test, which results in unpredictable @@ -396,19 +494,18 @@ void testMain() { // Renders a `string` by breaking it up into individual characters and // rendering each character into its own layer. Future testCase(String string, String description, { int deletions = 0, int additions = 0, int moves = 0 }) { - print('Testing "$string" - $description'); final Set actualDeletions = {}; final Set actualAdditions = {}; // Watches DOM mutations and counts deletions and additions to the child // list of the `` element. - final html.MutationObserver observer = html.MutationObserver((List mutations, _) { - for (html.MutationRecord record in mutations.cast()) { - actualDeletions.addAll(record.removedNodes); - actualAdditions.addAll(record.addedNodes); + final html.MutationObserver observer = html.MutationObserver((List mutations, _) { + for (final html.MutationRecord record in mutations.cast()) { + actualDeletions.addAll(record.removedNodes!); + actualAdditions.addAll(record.addedNodes!); } }); - observer.observe(SurfaceSceneBuilder.debugLastFrameScene.rootElement, childList: true); + observer.observe(SurfaceSceneBuilder.debugLastFrameScene!.rootElement!, childList: true); final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); for (int i = 0; i < string.length; i++) { @@ -416,13 +513,13 @@ void testMain() { renderedLayers[char] = pushChild(builder, char, oldLayer: renderedLayers[char]); } final SurfaceScene scene = builder.build(); - final List pTags = scene.webOnlyRootElement.querySelectorAll('p'); + final List pTags = scene.webOnlyRootElement!.querySelectorAll('p'); expect(pTags, hasLength(string.length)); expect( - scene.webOnlyRootElement.querySelectorAll('p').map((p) => p.innerText).join(''), + scene.webOnlyRootElement!.querySelectorAll('p').map((html.Element p) => p.innerText).join(''), string, ); - renderedLayers.removeWhere((key, value) => !string.contains(key)); + renderedLayers.removeWhere((String key, ui.EngineLayer value) => !string.contains(key)); // Inject a zero-duration timer to allow mutation observers to receive notification. return Future.delayed(Duration.zero).then((_) { @@ -485,10 +582,108 @@ void testMain() { await testCase('be', 'remove in the middle', deletions: 2); await testCase('', 'remove all', deletions: 2); }); + + test('Canvas should allocate fewer pixels when zoomed out', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final ui.Picture picture1 = _drawPicture(); + builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); + builder.addPicture(ui.Offset.zero, picture1); + builder.pop(); + + final html.Element content = builder.build().webOnlyRootElement!; + final html.CanvasElement canvas = content.querySelector('canvas')! as html.CanvasElement; + final int unscaledWidth = canvas.width!; + final int unscaledHeight = canvas.height!; + + // Force update to scene which will utilize reuse code path. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushOffset(0, 0); + builder2.pushTransform(Matrix4.identity().scaled(0.5, 0.5).toFloat64()); + builder2.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 300, 300), + ); + builder2.addPicture(ui.Offset.zero, picture1); + builder2.pop(); + builder2.pop(); + builder2.pop(); + + final html.Element contentAfterScale = builder2.build().webOnlyRootElement!; + final html.CanvasElement canvas2 = contentAfterScale.querySelector('canvas')! as html.CanvasElement; + // Although we are drawing same picture, due to scaling the new canvas + // should have fewer pixels. + expect(canvas2.width! < unscaledWidth, isTrue); + expect(canvas2.height! < unscaledHeight, isTrue); + }); + + test('Canvas should allocate more pixels when zoomed in', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final ui.Picture picture1 = _drawPicture(); + builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); + builder.addPicture(ui.Offset.zero, picture1); + builder.pop(); + + final html.Element content = builder.build().webOnlyRootElement!; + final html.CanvasElement canvas = content.querySelector('canvas')! as html.CanvasElement; + final int unscaledWidth = canvas.width!; + final int unscaledHeight = canvas.height!; + + // Force update to scene which will utilize reuse code path. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushOffset(0, 0); + builder2.pushTransform(Matrix4.identity().scaled(2, 2).toFloat64()); + builder2.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 300, 300), + ); + builder2.addPicture(ui.Offset.zero, picture1); + builder2.pop(); + builder2.pop(); + builder2.pop(); + + final html.Element contentAfterScale = builder2.build().webOnlyRootElement!; + final html.CanvasElement canvas2 = contentAfterScale.querySelector('canvas')! as html.CanvasElement; + // Although we are drawing same picture, due to scaling the new canvas + // should have more pixels. + expect(canvas2.width! > unscaledWidth, isTrue); + expect(canvas2.height! > unscaledHeight, isTrue); + }); + + test('Should recycle canvas once', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final ui.Picture picture1 = _drawPicture(); + final ui.ClipRectEngineLayer oldLayer = builder.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 300, 300), + ); + builder.addPicture(ui.Offset.zero, picture1); + builder.pop(); + builder.build(); + + // Force update to scene which will utilize reuse code path. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + final ui.ClipRectEngineLayer oldLayer2 = builder2.pushClipRect( + const ui.Rect.fromLTRB(5, 10, 300, 300), + oldLayer: oldLayer + ); + builder2.addPicture(ui.Offset.zero, _drawEmptyPicture()); + builder2.pop(); + + final html.Element contentAfterReuse = builder2.build().webOnlyRootElement!; + expect(contentAfterReuse, isNotNull); + + final SurfaceSceneBuilder builder3 = SurfaceSceneBuilder(); + builder3.pushClipRect( + const ui.Rect.fromLTRB(25, 10, 300, 300), + oldLayer: oldLayer2 + ); + builder3.addPicture(ui.Offset.zero, _drawEmptyPicture()); + builder3.pop(); + // This build will crash if canvas gets recycled twice. + final html.Element contentAfterReuse2 = builder3.build().webOnlyRootElement!; + expect(contentAfterReuse2, isNotNull); + }); } -typedef TestLayerBuilder = EngineLayer Function( - SceneBuilder sceneBuilder, EngineLayer oldLayer); +typedef TestLayerBuilder = ui.EngineLayer Function( + ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer); typedef ExpectedHtmlGetter = String Function(); void testLayerLifeCycle( @@ -498,8 +693,8 @@ void testLayerLifeCycle( SurfaceSceneBuilder.debugForgetFrameScene(); // Build: builds a brand new layer. - SceneBuilder sceneBuilder = SceneBuilder(); - final EngineLayer layer1 = layerBuilder(sceneBuilder, null); + SurfaceSceneBuilder sceneBuilder = SurfaceSceneBuilder(); + final ui.EngineLayer layer1 = layerBuilder(sceneBuilder, null); final Type surfaceType = layer1.runtimeType; sceneBuilder.pop(); @@ -513,24 +708,24 @@ void testLayerLifeCycle( } final PersistedSurface surface1 = findSurface(); - final html.Element surfaceElement1 = surface1.rootElement; + final html.Element surfaceElement1 = surface1.rootElement!; // Retain: reuses a layer as is along with its DOM elements. - sceneBuilder = SceneBuilder(); + sceneBuilder = SurfaceSceneBuilder(); sceneBuilder.addRetained(layer1); tester = SceneTester(sceneBuilder.build()); tester.expectSceneHtml(expectedHtmlGetter()); final PersistedSurface surface2 = findSurface(); - final html.Element surfaceElement2 = surface2.rootElement; + final html.Element surfaceElement2 = surface2.rootElement!; expect(surface2, same(surface1)); expect(surfaceElement2, same(surfaceElement1)); // Reuse: reuses a layer's DOM elements by matching it. - sceneBuilder = SceneBuilder(); - final EngineLayer layer3 = layerBuilder(sceneBuilder, layer1); + sceneBuilder = SurfaceSceneBuilder(); + final ui.EngineLayer layer3 = layerBuilder(sceneBuilder, layer1); sceneBuilder.pop(); expect(layer3, isNot(same(layer1))); tester = SceneTester(sceneBuilder.build()); @@ -538,13 +733,13 @@ void testLayerLifeCycle( final PersistedSurface surface3 = findSurface(); expect(surface3, same(layer3)); - final html.Element surfaceElement3 = surface3.rootElement; + final html.Element surfaceElement3 = surface3.rootElement!; expect(surface3, isNot(same(surface2))); expect(surfaceElement3, isNotNull); expect(surfaceElement3, same(surfaceElement2)); // Recycle: discards all the layers. - sceneBuilder = SceneBuilder(); + sceneBuilder = SurfaceSceneBuilder(); tester = SceneTester(sceneBuilder.build()); tester.expectSceneHtml(''); @@ -553,36 +748,36 @@ void testLayerLifeCycle( // Retain again: the framework should be able to request that a layer is added // as retained even after it has been recycled. In this case the // engine would "rehydrate" the layer with new DOM elements. - sceneBuilder = SceneBuilder(); + sceneBuilder = SurfaceSceneBuilder(); sceneBuilder.addRetained(layer3); tester = SceneTester(sceneBuilder.build()); tester.expectSceneHtml(expectedHtmlGetter()); expect(surface3.rootElement, isNotNull); // offset3 should be rehydrated. // Make sure we clear retained surface list. - expect(debugRetainedSurfaces, isEmpty); + expect(retainedSurfaces, isEmpty); } class MockPersistedPicture extends PersistedPicture { factory MockPersistedPicture() { - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); // Use the largest cull rect so that layer clips are effective. The tests // rely on this. - recorder.beginRecording(Rect.largest)..drawPaint(Paint()); + recorder.beginRecording(ui.Rect.largest).drawPaint(SurfacePaint()); return MockPersistedPicture._(recorder.endRecording()); } - MockPersistedPicture._(Picture picture) : super(0, 0, picture, 0); + MockPersistedPicture._(EnginePicture picture) : super(0, 0, picture, 0); int retainCount = 0; int buildCount = 0; int updateCount = 0; int applyPaintCount = 0; - final BitmapCanvas _fakeCanvas = BitmapCanvas(const Rect.fromLTRB(0, 0, 10, 10)); + final BitmapCanvas _fakeCanvas = BitmapCanvas(const ui.Rect.fromLTRB(0, 0, 10, 10), RenderStrategy()); @override - EngineCanvas get debugCanvas { + EngineCanvas get canvas { return _fakeCanvas; } @@ -592,7 +787,7 @@ class MockPersistedPicture extends PersistedPicture { } @override - Matrix4 get localTransformInverse => null; + Matrix4 get localTransformInverse => Matrix4.identity(); @override void build() { @@ -607,7 +802,7 @@ class MockPersistedPicture extends PersistedPicture { } @override - void applyPaint(EngineCanvas oldCanvas) { + void applyPaint(EngineCanvas? oldCanvas) { applyPaintCount++; } @@ -621,69 +816,101 @@ class MockPersistedPicture extends PersistedPicture { int get bitmapPixelCount => 0; } -Picture _drawPicture() { +/// Draw 4 circles within 50, 50, 120, 120 bounds +ui.Picture _drawPicture() { const double offsetX = 50; const double offsetY = 50; - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); + recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); + final ui.Shader gradient = ui.Gradient.radial( + const ui.Offset(100, 100), 50, + const [ + ui.Color.fromARGB(255, 0, 0, 0), + ui.Color.fromARGB(255, 0, 0, 255), + ], + ); canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), 10, Paint()..style = PaintingStyle.fill); + const ui.Offset(offsetX + 10, offsetY + 10), 10, + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..shader = gradient); canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), + const ui.Offset(offsetX + 60, offsetY + 10), 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)); + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(255, 0, 0, 1)); canvas.drawCircle( - Offset(offsetX + 10, offsetY + 60), + const ui.Offset(offsetX + 10, offsetY + 60), 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1)); + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(0, 255, 0, 1)); canvas.drawCircle( - Offset(offsetX + 60, offsetY + 60), + const ui.Offset(offsetX + 60, offsetY + 60), 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1)); + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(0, 0, 255, 1)); return recorder.endRecording(); } -Picture _drawPathImagePath() { +EnginePicture _drawEmptyPicture() { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); + return recorder.endRecording(); +} + +EnginePicture _drawPathImagePath() { const double offsetX = 50; const double offsetY = 50; - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); + recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 400, 400)); + final ui.Shader gradient = ui.Gradient.radial( + const ui.Offset(100, 100), 50, + const [ + ui.Color.fromARGB(255, 0, 0, 0), + ui.Color.fromARGB(255, 0, 0, 255), + ], + ); canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), 10, Paint()..style = PaintingStyle.fill); + const ui.Offset(offsetX + 10, offsetY + 10), 10, + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..shader = gradient); canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), + const ui.Offset(offsetX + 60, offsetY + 10), 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)); + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(255, 0, 0, 1)); canvas.drawCircle( - Offset(offsetX + 10, offsetY + 60), + const ui.Offset(offsetX + 10, offsetY + 60), 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1)); - canvas.drawImage(createTestImage(), Offset(0, 0), Paint()); + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(0, 255, 0, 1)); + canvas.drawImage(createTestImage(), const ui.Offset(0, 0), SurfacePaint()); + canvas.drawCircle( + const ui.Offset(offsetX + 10, offsetY + 10), 10, + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..shader = gradient); canvas.drawCircle( - Offset(offsetX + 60, offsetY + 60), + const ui.Offset(offsetX + 60, offsetY + 60), 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1)); + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(0, 0, 255, 1)); return recorder.endRecording(); } HtmlImage createTestImage({int width = 100, int height = 50}) { - html.CanvasElement canvas = - new html.CanvasElement(width: width, height: height); - html.CanvasRenderingContext2D ctx = canvas.context2D; + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, 33, 50); ctx.fill(); @@ -693,7 +920,7 @@ HtmlImage createTestImage({int width = 100, int height = 50}) { ctx.fillStyle = '#2040E0'; ctx.fillRect(66, 0, 33, 50); ctx.fill(); - html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); + final html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; return HtmlImage(imageElement, width, height); } diff --git a/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart b/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart new file mode 100644 index 0000000000000..2aae2af94a970 --- /dev/null +++ b/lib/web_ui/test/engine/surface/shaders/normalized_gradient_test.dart @@ -0,0 +1,124 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine/html/shaders/normalized_gradient.dart'; +import 'package:ui/ui.dart' as ui hide window; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('Shader Normalized Gradient', () { + test('3 stop at start', () { + final NormalizedGradient gradient = NormalizedGradient(const [ + ui.Color(0xFF000000), ui.Color(0xFFFF7f3f) + ], stops: [0.0, 0.5]); + int res = _computeColorAt(gradient, 0.0); + assert(res == 0xFF000000); + res = _computeColorAt(gradient, 0.25); + assert(res == 0xFF7f3f1f); + res = _computeColorAt(gradient, 0.5); + assert(res == 0xFFFF7f3f); + res = _computeColorAt(gradient, 0.7); + assert(res == 0xFFFF7f3f); + res = _computeColorAt(gradient, 1.0); + assert(res == 0xFFFF7f3f); + }); + + test('3 stop at end', () { + final NormalizedGradient gradient = NormalizedGradient(const [ + ui.Color(0xFF000000), ui.Color(0xFFFF7f3f) + ], stops: [0.5, 1.0]); + int res = _computeColorAt(gradient, 0.0); + assert(res == 0xFF000000); + res = _computeColorAt(gradient, 0.25); + assert(res == 0xFF000000); + res = _computeColorAt(gradient, 0.5); + assert(res == 0xFF000000); + res = _computeColorAt(gradient, 0.75); + assert(res == 0xFF7f3f1f); + res = _computeColorAt(gradient, 1.0); + assert(res == 0xFFFF7f3f); + }); + + test('4 stop', () { + final NormalizedGradient gradient = NormalizedGradient(const [ + ui.Color(0xFF000000), ui.Color(0xFFFF7f3f) + ], stops: [0.25, 0.5]); + int res = _computeColorAt(gradient, 0.0); + assert(res == 0xFF000000); + res = _computeColorAt(gradient, 0.25); + assert(res == 0xFF000000); + res = _computeColorAt(gradient, 0.4); + assert(res == 0xFF994c25); + res = _computeColorAt(gradient, 0.5); + assert(res == 0xFFFF7f3f); + res = _computeColorAt(gradient, 0.75); + assert(res == 0xFFFF7f3f); + res = _computeColorAt(gradient, 1.0); + assert(res == 0xFFFF7f3f); + }); + + test('5 stop', () { + final NormalizedGradient gradient = NormalizedGradient(const [ + ui.Color(0x10000000), ui.Color(0x20FF0000), + ui.Color(0x4000FF00), ui.Color(0x800000FF), + ui.Color(0xFFFFFFFF) + ], stops: [0.0, 0.1, 0.2, 0.5, 1.0]); + int res = _computeColorAt(gradient, 0.0); + assert(res == 0x10000000); + res = _computeColorAt(gradient, 0.05); + assert(res == 0x187f0000); + res = _computeColorAt(gradient, 0.1); + assert(res == 0x20ff0000); + res = _computeColorAt(gradient, 0.15); + assert(res == 0x307f7f00); + res = _computeColorAt(gradient, 0.2); + assert(res == 0x4000ff00); + res = _computeColorAt(gradient, 0.4); + assert(res == 0x6a0054a9); + res = _computeColorAt(gradient, 0.5); + assert(res == 0x800000fe); + res = _computeColorAt(gradient, 0.9); + assert(res == 0xe5ccccff); + res = _computeColorAt(gradient, 1.0); + assert(res == 0xffffffff); + }); + + test('2 stops at ends', () { + final NormalizedGradient gradient = NormalizedGradient(const [ + ui.Color(0x00000000), ui.Color(0xFFFFFFFF) + ]); + int res = _computeColorAt(gradient, 0.0); + assert(res == 0); + res = _computeColorAt(gradient, 1.0); + assert(res == 0xFFFFFFFF); + res = _computeColorAt(gradient, 0.5); + assert(res == 0x7f7f7f7f); + }); + }); +} + +int _computeColorAt(NormalizedGradient gradient, double t) { + int i = 0; + while (t > gradient.thresholdAt(i + 1)) { + ++i; + } + final double r = t * gradient.scaleAt(i * 4) + gradient.biasAt(i * 4); + final double g = t * gradient.scaleAt(i * 4 + 1) + gradient.biasAt(i * 4 + 1); + final double b = t * gradient.scaleAt(i * 4 + 2) + gradient.biasAt(i * 4 + 2); + final double a = t * gradient.scaleAt(i * 4 + 3) + gradient.biasAt(i * 4 + 3); + int val = 0; + val |= (a * 0xFF).toInt() & 0xFF; + val<<=8; + val |= (r * 0xFF).toInt() & 0xFF; + val<<=8; + val |= (g * 0xFF).toInt() & 0xFF; + val<<=8; + val |= (b * 0xFF).toInt() & 0xFF; + return val; +} diff --git a/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart b/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart new file mode 100644 index 0000000000000..1629165ed1679 --- /dev/null +++ b/lib/web_ui/test/engine/surface/shaders/shader_builder_test.dart @@ -0,0 +1,207 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + const String mat2Sample = 'mat2(1.1, 2.1, 1.2, 2.2)'; + const String mat3Sample = 'mat3(1.1, 2.1, 3.1, // first column (not row!)\n' + '1.2, 2.2, 3.2, // second column\n' + '1.3, 2.3, 3.3 // third column\n' + ')'; + const String mat4Sample = 'mat3(1.1, 2.1, 3.1, 4.1,\n' + '1.2, 2.2, 3.2, 4.2,\n' + '1.3, 2.3, 3.3, 4.3,\n' + '1.4, 2.4, 3.4, 4.4,\n' + ')'; + + setUpAll(() async { + webOnlyInitializeEngine(); + }); + + group('Shader Declarations', () { + test('Constant declaration WebGL1', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl1); + builder.addConst(ShaderType.kBool, 'false'); + builder.addConst(ShaderType.kInt, '0'); + builder.addConst(ShaderType.kFloat, '1.0'); + builder.addConst(ShaderType.kBVec2, 'bvec2(false, false)'); + builder.addConst(ShaderType.kBVec3, 'bvec3(false, false, true)'); + builder.addConst(ShaderType.kBVec4, 'bvec4(true, true, false, false)'); + builder.addConst(ShaderType.kIVec2, 'ivec2(1, 2)'); + builder.addConst(ShaderType.kIVec3, 'ivec3(1, 2, 3)'); + builder.addConst(ShaderType.kIVec4, 'ivec4(1, 2, 3, 4)'); + builder.addConst(ShaderType.kVec2, 'vec2(1.0, 2.0)'); + builder.addConst(ShaderType.kVec3, 'vec3(1.0, 2.0, 3.0)'); + builder.addConst(ShaderType.kVec4, 'vec4(1.0, 2.0, 3.0, 4.0)'); + builder.addConst(ShaderType.kMat2, mat2Sample); + builder.addConst(ShaderType.kMat2, mat2Sample, name: 'transform1'); + builder.addConst(ShaderType.kMat3, mat3Sample); + builder.addConst(ShaderType.kMat4, mat4Sample); + expect( + builder.build(), + 'const bool c_0 = false;\n' + 'const int c_1 = 0;\n' + 'const float c_2 = 1.0;\n' + 'const bvec2 c_3 = bvec2(false, false);\n' + 'const bvec3 c_4 = bvec3(false, false, true);\n' + 'const bvec4 c_5 = bvec4(true, true, false, false);\n' + 'const ivec2 c_6 = ivec2(1, 2);\n' + 'const ivec3 c_7 = ivec3(1, 2, 3);\n' + 'const ivec4 c_8 = ivec4(1, 2, 3, 4);\n' + 'const vec2 c_9 = vec2(1.0, 2.0);\n' + 'const vec3 c_10 = vec3(1.0, 2.0, 3.0);\n' + 'const vec4 c_11 = vec4(1.0, 2.0, 3.0, 4.0);\n' + 'const mat2 c_12 = $mat2Sample;\n' + 'const mat2 transform1 = $mat2Sample;\n' + 'const mat3 c_13 = $mat3Sample;\n' + 'const mat4 c_14 = $mat4Sample;\n'); + }); + + test('Constant declaration WebGL2', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); + builder.addConst(ShaderType.kBool, 'false'); + builder.addConst(ShaderType.kInt, '0'); + builder.addConst(ShaderType.kFloat, '1.0'); + builder.addConst(ShaderType.kBVec2, 'bvec2(false, false)'); + builder.addConst(ShaderType.kBVec3, 'bvec3(false, false, true)'); + builder.addConst(ShaderType.kBVec4, 'bvec4(true, true, false, false)'); + builder.addConst(ShaderType.kIVec2, 'ivec2(1, 2)'); + builder.addConst(ShaderType.kIVec3, 'ivec3(1, 2, 3)'); + builder.addConst(ShaderType.kIVec4, 'ivec4(1, 2, 3, 4)'); + builder.addConst(ShaderType.kVec2, 'vec2(1.0, 2.0)'); + builder.addConst(ShaderType.kVec3, 'vec3(1.0, 2.0, 3.0)'); + builder.addConst(ShaderType.kVec4, 'vec4(1.0, 2.0, 3.0, 4.0)'); + builder.addConst(ShaderType.kMat2, mat2Sample); + builder.addConst(ShaderType.kMat2, mat2Sample, name: 'transform2'); + builder.addConst(ShaderType.kMat3, mat3Sample); + builder.addConst(ShaderType.kMat4, mat4Sample); + expect( + builder.build(), + '#version 300 es\n' + 'const bool c_0 = false;\n' + 'const int c_1 = 0;\n' + 'const float c_2 = 1.0;\n' + 'const bvec2 c_3 = bvec2(false, false);\n' + 'const bvec3 c_4 = bvec3(false, false, true);\n' + 'const bvec4 c_5 = bvec4(true, true, false, false);\n' + 'const ivec2 c_6 = ivec2(1, 2);\n' + 'const ivec3 c_7 = ivec3(1, 2, 3);\n' + 'const ivec4 c_8 = ivec4(1, 2, 3, 4);\n' + 'const vec2 c_9 = vec2(1.0, 2.0);\n' + 'const vec3 c_10 = vec3(1.0, 2.0, 3.0);\n' + 'const vec4 c_11 = vec4(1.0, 2.0, 3.0, 4.0);\n' + 'const mat2 c_12 = $mat2Sample;\n' + 'const mat2 transform2 = $mat2Sample;\n' + 'const mat3 c_13 = $mat3Sample;\n' + 'const mat4 c_14 = $mat4Sample;\n'); + }); + + test('Attribute declaration WebGL1', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl1); + builder.addIn(ShaderType.kVec4, name: 'position'); + builder.addIn(ShaderType.kVec4); + expect( + builder.build(), + 'attribute vec4 position;\n' + 'attribute vec4 attr_0;\n'); + }); + + test('in declaration WebGL1', () { + final ShaderBuilder builder = ShaderBuilder.fragment(WebGLVersion.webgl1); + builder.addIn(ShaderType.kVec4, name: 'position'); + builder.addIn(ShaderType.kVec4); + expect( + builder.build(), + 'varying vec4 position;\n' + 'varying vec4 attr_0;\n'); + }); + + test('Attribute declaration WebGL2', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); + builder.addIn(ShaderType.kVec4, name: 'position'); + builder.addIn(ShaderType.kVec4); + expect( + builder.build(), + '#version 300 es\n' + 'in vec4 position;\n' + 'in vec4 attr_0;\n'); + }); + + test('Uniform declaration WebGL1', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl1); + final ShaderDeclaration variable = + builder.addUniform(ShaderType.kVec4, name: 'v1'); + expect(variable.name, 'v1'); + expect(variable.dataType, ShaderType.kVec4); + expect(variable.storage, ShaderStorageQualifier.kUniform); + builder.addUniform(ShaderType.kVec4); + expect( + builder.build(), + 'uniform vec4 v1;\n' + 'uniform vec4 uni_0;\n'); + }); + + test('Uniform declaration WebGL2', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); + final ShaderDeclaration variable = + builder.addUniform(ShaderType.kVec4, name: 'v1'); + expect(variable.name, 'v1'); + expect(variable.dataType, ShaderType.kVec4); + expect(variable.storage, ShaderStorageQualifier.kUniform); + builder.addUniform(ShaderType.kVec4); + expect( + builder.build(), + '#version 300 es\n' + 'uniform vec4 v1;\n' + 'uniform vec4 uni_0;\n'); + }); + + test('Float precision', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); + builder.floatPrecision = ShaderPrecision.kLow; + builder.addUniform(ShaderType.kFloat, name: 'f1'); + expect( + builder.build(), + '#version 300 es\n' + 'precision lowp float;\n' + 'uniform float f1;\n'); + }); + + test('Integer precision', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); + builder.integerPrecision = ShaderPrecision.kLow; + builder.addUniform(ShaderType.kInt, name: 'i1'); + expect( + builder.build(), + '#version 300 es\n' + 'precision lowp int;\n' + 'uniform int i1;\n'); + }); + + test('Method', () { + final ShaderBuilder builder = ShaderBuilder(WebGLVersion.webgl2); + builder.floatPrecision = ShaderPrecision.kMedium; + final ShaderDeclaration variable = + builder.addUniform(ShaderType.kFloat, name: 'f1'); + final ShaderMethod m = builder.addMethod('main'); + m.addStatement('f1 = 5.0;'); + expect( + builder.build(), + '#version 300 es\n' + 'precision mediump float;\n' + 'uniform float ${variable.name};\n' + 'void main() {\n' + ' f1 = 5.0;\n' + '}\n'); + }); + }); +} diff --git a/lib/web_ui/test/engine/surface/surface_test.dart b/lib/web_ui/test/engine/surface/surface_test.dart index d8c2cea4a8811..90911fdb23109 100644 --- a/lib/web_ui/test/engine/surface/surface_test.dart +++ b/lib/web_ui/test/engine/surface/surface_test.dart @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -23,7 +21,7 @@ void testMain() { test('debugAssertSurfaceState produces a human-readable message', () { final SceneBuilder builder = SceneBuilder(); - final PersistedOpacity opacityLayer = builder.pushOpacity(100); + final PersistedOpacity opacityLayer = builder.pushOpacity(100) as PersistedOpacity; try { debugAssertSurfaceState(opacityLayer, PersistedSurfaceState.active, PersistedSurfaceState.pendingRetention); fail('Expected $PersistedSurfaceException'); @@ -39,69 +37,69 @@ void testMain() { test('is created', () { final SceneBuilder builder = SceneBuilder(); - final PersistedOpacity opacityLayer = builder.pushOpacity(100); + final PersistedOpacity opacityLayer = builder.pushOpacity(100) as PersistedOpacity; builder.pop(); expect(opacityLayer, isNotNull); expect(opacityLayer.rootElement, isNull); - expect(opacityLayer.isCreated, true); + expect(opacityLayer.isCreated, isTrue); builder.build(); - expect(opacityLayer.rootElement.tagName.toLowerCase(), 'flt-opacity'); - expect(opacityLayer.isActive, true); + expect(opacityLayer.rootElement!.tagName.toLowerCase(), 'flt-opacity'); + expect(opacityLayer.isActive, isTrue); }); test('is released', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; builder1.pop(); builder1.build(); - expect(opacityLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); SceneBuilder().build(); - expect(opacityLayer.isReleased, true); + expect(opacityLayer.isReleased, isTrue); expect(opacityLayer.rootElement, isNull); }); test('discarding is recursive', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; final PersistedTransform transformLayer = - builder1.pushTransform(Matrix4.identity().toFloat64()); + builder1.pushTransform(Matrix4.identity().toFloat64()) as PersistedTransform; builder1.pop(); builder1.pop(); builder1.build(); - expect(opacityLayer.isActive, true); - expect(transformLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); + expect(transformLayer.isActive, isTrue); SceneBuilder().build(); - expect(opacityLayer.isReleased, true); - expect(transformLayer.isReleased, true); + expect(opacityLayer.isReleased, isTrue); + expect(transformLayer.isReleased, isTrue); expect(opacityLayer.rootElement, isNull); expect(transformLayer.rootElement, isNull); }); test('is updated', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100) as PersistedOpacity; builder1.pop(); builder1.build(); - expect(opacityLayer1.isActive, true); - final html.Element element = opacityLayer1.rootElement; + expect(opacityLayer1.isActive, isTrue); + final html.Element element = opacityLayer1.rootElement!; final SceneBuilder builder2 = SceneBuilder(); final PersistedOpacity opacityLayer2 = - builder2.pushOpacity(200, oldLayer: opacityLayer1); - expect(opacityLayer1.isPendingUpdate, true); - expect(opacityLayer2.isCreated, true); + builder2.pushOpacity(200, oldLayer: opacityLayer1) as PersistedOpacity; + expect(opacityLayer1.isPendingUpdate, isTrue); + expect(opacityLayer2.isCreated, isTrue); expect(opacityLayer2.oldLayer, same(opacityLayer1)); builder2.pop(); builder2.build(); - expect(opacityLayer1.isReleased, true); + expect(opacityLayer1.isReleased, isTrue); expect(opacityLayer1.rootElement, isNull); - expect(opacityLayer2.isActive, true); + expect(opacityLayer2.isActive, isTrue); expect( opacityLayer2.rootElement, element); // adopts old surface's element expect(opacityLayer2.oldLayer, isNull); @@ -110,28 +108,28 @@ void testMain() { test('ignores released surface when updated', () { // Build a surface final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100) as PersistedOpacity; builder1.pop(); builder1.build(); - expect(opacityLayer1.isActive, true); - final html.Element element = opacityLayer1.rootElement; + expect(opacityLayer1.isActive, isTrue); + final html.Element element = opacityLayer1.rootElement!; // Release it SceneBuilder().build(); - expect(opacityLayer1.isReleased, true); + expect(opacityLayer1.isReleased, isTrue); expect(opacityLayer1.rootElement, isNull); // Attempt to update it final SceneBuilder builder2 = SceneBuilder(); final PersistedOpacity opacityLayer2 = - builder2.pushOpacity(200, oldLayer: opacityLayer1); + builder2.pushOpacity(200, oldLayer: opacityLayer1) as PersistedOpacity; builder2.pop(); - expect(opacityLayer1.isReleased, true); - expect(opacityLayer2.isCreated, true); + expect(opacityLayer1.isReleased, isTrue); + expect(opacityLayer2.isCreated, isTrue); builder2.build(); - expect(opacityLayer1.isReleased, true); - expect(opacityLayer2.isActive, true); + expect(opacityLayer1.isReleased, isTrue); + expect(opacityLayer2.isActive, isTrue); expect(opacityLayer2.rootElement, isNot(equals(element))); }); @@ -159,10 +157,11 @@ void testMain() { final _LoggingTestSurface logger = _LoggingTestSurface(); final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); final PersistedTransform a1 = - builder1.pushTransform(Matrix4.identity().toFloat64()); - final PersistedOpacity b1 = builder1.pushOpacity(100); + builder1.pushTransform( + (Matrix4.identity()..scale(html.window.devicePixelRatio as double)).toFloat64()) as PersistedTransform; + final PersistedOpacity b1 = builder1.pushOpacity(100) as PersistedOpacity; final PersistedTransform c1 = - builder1.pushTransform(Matrix4.identity().toFloat64()); + builder1.pushTransform(Matrix4.identity().toFloat64()) as PersistedTransform; builder1.debugAddSurface(logger); builder1.pop(); builder1.pop(); @@ -170,28 +169,30 @@ void testMain() { builder1.build(); expect(logger.log, ['build', 'createElement', 'apply']); - final html.Element elementA = a1.rootElement; - final html.Element elementB = b1.rootElement; - final html.Element elementC = c1.rootElement; + final html.Element elementA = a1.rootElement!; + final html.Element elementB = b1.rootElement!; + final html.Element elementC = c1.rootElement!; expect(elementC.parent, elementB); expect(elementB.parent, elementA); final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); final PersistedTransform a2 = - builder2.pushTransform(Matrix4.identity().toFloat64(), oldLayer: a1); + builder2.pushTransform( + (Matrix4.identity()..scale(html.window.devicePixelRatio as double)).toFloat64(), + oldLayer: a1) as PersistedTransform; final PersistedTransform c2 = - builder2.pushTransform(Matrix4.identity().toFloat64(), oldLayer: c1); + builder2.pushTransform(Matrix4.identity().toFloat64(), oldLayer: c1) as PersistedTransform; builder2.addRetained(logger); builder2.pop(); builder2.pop(); - expect(c1.isPendingUpdate, true); - expect(c2.isCreated, true); + expect(c1.isPendingUpdate, isTrue); + expect(c2.isCreated, isTrue); builder2.build(); expect(logger.log, ['build', 'createElement', 'apply', 'retain']); - expect(c1.isReleased, true); - expect(c2.isActive, true); + expect(c1.isReleased, isTrue); + expect(c2.isActive, isTrue); expect(a2.rootElement, elementA); expect(b1.rootElement, isNull); @@ -201,78 +202,78 @@ void testMain() { expect(elementB.parent, null); }, // This method failed on iOS Safari. - // TODO: https://github.com/flutter/flutter/issues/60036 - skip: (browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs)); + // TODO(ferhat): https://github.com/flutter/flutter/issues/60036 + skip: browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs); test('is retained', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; builder1.pop(); builder1.build(); - expect(opacityLayer.isActive, true); - final html.Element element = opacityLayer.rootElement; + expect(opacityLayer.isActive, isTrue); + final html.Element element = opacityLayer.rootElement!; final SceneBuilder builder2 = SceneBuilder(); - expect(opacityLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); builder2.addRetained(opacityLayer); - expect(opacityLayer.isPendingRetention, true); + expect(opacityLayer.isPendingRetention, isTrue); builder2.build(); - expect(opacityLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); expect(opacityLayer.rootElement, element); }); test('revives released surface when retained', () { final SurfaceSceneBuilder builder1 = SurfaceSceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; final _LoggingTestSurface logger = _LoggingTestSurface(); builder1.debugAddSurface(logger); builder1.pop(); builder1.build(); - expect(opacityLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); expect(logger.log, ['build', 'createElement', 'apply']); - final html.Element element = opacityLayer.rootElement; + final html.Element element = opacityLayer.rootElement!; SceneBuilder().build(); - expect(opacityLayer.isReleased, true); + expect(opacityLayer.isReleased, isTrue); expect(opacityLayer.rootElement, isNull); expect(logger.log, ['build', 'createElement', 'apply', 'discard']); final SceneBuilder builder2 = SceneBuilder(); builder2.addRetained(opacityLayer); - expect(opacityLayer.isCreated, true); // revived + expect(opacityLayer.isCreated, isTrue); // revived expect(logger.log, ['build', 'createElement', 'apply', 'discard', 'revive']); builder2.build(); - expect(opacityLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); expect(opacityLayer.rootElement, isNot(equals(element))); }); test('reviving is recursive', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer = builder1.pushOpacity(100) as PersistedOpacity; final PersistedTransform transformLayer = - builder1.pushTransform(Matrix4.identity().toFloat64()); + builder1.pushTransform(Matrix4.identity().toFloat64()) as PersistedTransform; builder1.pop(); builder1.pop(); builder1.build(); - expect(opacityLayer.isActive, true); - expect(transformLayer.isActive, true); - final html.Element opacityElement = opacityLayer.rootElement; - final html.Element transformElement = transformLayer.rootElement; + expect(opacityLayer.isActive, isTrue); + expect(transformLayer.isActive, isTrue); + final html.Element opacityElement = opacityLayer.rootElement!; + final html.Element transformElement = transformLayer.rootElement!; SceneBuilder().build(); final SceneBuilder builder2 = SceneBuilder(); builder2.addRetained(opacityLayer); - expect(opacityLayer.isCreated, true); // revived - expect(transformLayer.isCreated, true); // revived + expect(opacityLayer.isCreated, isTrue); // revived + expect(transformLayer.isCreated, isTrue); // revived builder2.build(); - expect(opacityLayer.isActive, true); - expect(transformLayer.isActive, true); + expect(opacityLayer.isActive, isTrue); + expect(transformLayer.isActive, isTrue); expect(opacityLayer.rootElement, isNot(equals(opacityElement))); expect(transformLayer.rootElement, isNot(equals(transformElement))); }); @@ -296,28 +297,28 @@ void testMain() { // D test('reparents DOM elements when retained', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity a1 = builder1.pushOpacity(10); - final PersistedOpacity b1 = builder1.pushOpacity(20); + final PersistedOpacity a1 = builder1.pushOpacity(10) as PersistedOpacity; + final PersistedOpacity b1 = builder1.pushOpacity(20) as PersistedOpacity; builder1.pop(); - final PersistedOpacity c1 = builder1.pushOpacity(30); - final PersistedOpacity d1 = builder1.pushOpacity(40); + final PersistedOpacity c1 = builder1.pushOpacity(30) as PersistedOpacity; + final PersistedOpacity d1 = builder1.pushOpacity(40) as PersistedOpacity; builder1.pop(); builder1.pop(); builder1.pop(); builder1.build(); - final html.Element elementA = a1.rootElement; - final html.Element elementB = b1.rootElement; - final html.Element elementC = c1.rootElement; - final html.Element elementD = d1.rootElement; + final html.Element elementA = a1.rootElement!; + final html.Element elementB = b1.rootElement!; + final html.Element elementC = c1.rootElement!; + final html.Element elementD = d1.rootElement!; expect(elementB.parent, elementA); expect(elementC.parent, elementA); expect(elementD.parent, elementC); final SceneBuilder builder2 = SceneBuilder(); - final PersistedOpacity a2 = builder2.pushOpacity(10, oldLayer: a1); - final PersistedOpacity b2 = builder2.pushOpacity(20, oldLayer: b1); + final PersistedOpacity a2 = builder2.pushOpacity(10, oldLayer: a1) as PersistedOpacity; + final PersistedOpacity b2 = builder2.pushOpacity(20, oldLayer: b1) as PersistedOpacity; builder2.addRetained(c1); builder2.pop(); builder2.pop(); @@ -330,9 +331,9 @@ void testMain() { expect( [ - elementD.parent, - elementC.parent, - elementB.parent, + elementD.parent!, + elementC.parent!, + elementB.parent!, ], [elementC, elementB, elementA], ); @@ -340,22 +341,22 @@ void testMain() { test('is updated by matching', () { final SceneBuilder builder1 = SceneBuilder(); - final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100); + final PersistedOpacity opacityLayer1 = builder1.pushOpacity(100) as PersistedOpacity; builder1.pop(); builder1.build(); - expect(opacityLayer1.isActive, true); - final html.Element element = opacityLayer1.rootElement; + expect(opacityLayer1.isActive, isTrue); + final html.Element element = opacityLayer1.rootElement!; final SceneBuilder builder2 = SceneBuilder(); - final PersistedOpacity opacityLayer2 = builder2.pushOpacity(200); - expect(opacityLayer1.isActive, true); - expect(opacityLayer2.isCreated, true); + final PersistedOpacity opacityLayer2 = builder2.pushOpacity(200) as PersistedOpacity; + expect(opacityLayer1.isActive, isTrue); + expect(opacityLayer2.isCreated, isTrue); builder2.pop(); builder2.build(); - expect(opacityLayer1.isReleased, true); + expect(opacityLayer1.isReleased, isTrue); expect(opacityLayer1.rootElement, isNull); - expect(opacityLayer2.isActive, true); + expect(opacityLayer2.isActive, isTrue); expect( opacityLayer2.rootElement, element); // adopts old surface's element }); @@ -369,9 +370,9 @@ void testMain() { () { final SceneBuilder builder1 = SceneBuilder(); final Path path = Path(); - path.addPolygon([Offset(50, 0), Offset(100, 80), Offset(20, 40)], true); - PersistedPhysicalShape shape = builder1.pushPhysicalShape(path: path, - color: Color(0xFF00FF00), elevation: 1); + path.addPolygon(const [Offset(50, 0), Offset(100, 80), Offset(20, 40)], true); + final PersistedPhysicalShape shape = builder1.pushPhysicalShape(path: path, + color: const Color(0xFF00FF00), elevation: 1) as PersistedPhysicalShape; builder1.build(); expect(() => shape.apply(), returnsNormally); }); @@ -383,6 +384,7 @@ class _LoggingTestSurface extends PersistedContainerSurface { _LoggingTestSurface() : super(null); + @override void build() { log.add('build'); super.build(); @@ -405,11 +407,13 @@ class _LoggingTestSurface extends PersistedContainerSurface { super.update(oldSurface); } + @override void adoptElements(covariant PersistedSurface oldSurface) { log.add('adoptElements'); super.adoptElements(oldSurface); } + @override void retain() { log.add('retain'); super.retain(); @@ -421,13 +425,14 @@ class _LoggingTestSurface extends PersistedContainerSurface { super.discard(); } + @override void revive() { log.add('revive'); super.revive(); } @override - double matchForUpdate(PersistedSurface existingSurface) { + double matchForUpdate(PersistedSurface? existingSurface) { return 1.0; } } diff --git a/lib/web_ui/test/engine/ulps_test.dart b/lib/web_ui/test/engine/ulps_test.dart index d0a26c45faad7..ff708227390df 100644 --- a/lib/web_ui/test/engine/ulps_test.dart +++ b/lib/web_ui/test/engine/ulps_test.dart @@ -5,7 +5,7 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/ulps.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -37,7 +37,7 @@ void testMain() { }); test('Should convert float to bits', () { - Float32List floatList = Float32List(1); + final Float32List floatList = Float32List(1); floatList[0] = 0; expect(float2Bits(floatList, 0), 0); floatList[0] = 0.1; @@ -54,40 +54,40 @@ void testMain() { test('Should compare equality based on ulps', () { // If number of floats between a=1.1 and b are below 16, equals should // return true. - final double a = 1.1; - int aBits = floatAs2sCompliment(a); + const double a = 1.1; + final int aBits = floatAs2sCompliment(a); double b = twosComplimentAsFloat(aBits + 1); - expect(almostEqualUlps(a, b), true); + expect(almostEqualUlps(a, b), isTrue); b = twosComplimentAsFloat(aBits + 15); - expect(almostEqualUlps(a, b), true); + expect(almostEqualUlps(a, b), isTrue); b = twosComplimentAsFloat(aBits + 16); - expect(almostEqualUlps(a, b), false); + expect(almostEqualUlps(a, b), isFalse); // Test between variant of equalUlps. b = twosComplimentAsFloat(aBits + 1); - expect(almostBequalUlps(a, b), true); + expect(almostBequalUlps(a, b), isTrue); b = twosComplimentAsFloat(aBits + 1); - expect(almostBequalUlps(a, b), true); + expect(almostBequalUlps(a, b), isTrue); b = twosComplimentAsFloat(aBits + 2); - expect(almostBequalUlps(a, b), false); + expect(almostBequalUlps(a, b), isFalse); }); test('Should compare 2 coordinates based on ulps', () { double a = 1.1; int aBits = floatAs2sCompliment(a); double b = twosComplimentAsFloat(aBits + 1); - expect(approximatelyEqual(5.0, a, 5.0, b), true); + expect(approximatelyEqual(5.0, a, 5.0, b), isTrue); b = twosComplimentAsFloat(aBits + 16); - expect(approximatelyEqual(5.0, a, 5.0, b), true); + expect(approximatelyEqual(5.0, a, 5.0, b), isTrue); // Increase magnitude which should start checking with ulps rather than // fltEpsilon. a = 3000000.1; aBits = floatAs2sCompliment(a); b = twosComplimentAsFloat(aBits + 1); - expect(approximatelyEqual(5.0, a, 5.0, b), true); + expect(approximatelyEqual(5.0, a, 5.0, b), isTrue); b = twosComplimentAsFloat(aBits + 16); - expect(approximatelyEqual(5.0, a, 5.0, b), false); + expect(approximatelyEqual(5.0, a, 5.0, b), isFalse); }); }); } diff --git a/lib/web_ui/test/engine/util_test.dart b/lib/web_ui/test/engine/util_test.dart index 9ff47eb2b3e14..ff710261251dc 100644 --- a/lib/web_ui/test/engine/util_test.dart +++ b/lib/web_ui/test/engine/util_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; diff --git a/lib/web_ui/test/engine/web_experiments_test.dart b/lib/web_ui/test/engine/web_experiments_test.dart index c0f0a4f7f05f2..fd31fc9b97ed1 100644 --- a/lib/web_ui/test/engine/web_experiments_test.dart +++ b/lib/web_ui/test/engine/web_experiments_test.dart @@ -2,15 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'dart:js_util' as js_util; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; - -const bool _defaultUseCanvasText = true; +import 'package:ui/src/engine/web_experiments.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -22,52 +19,7 @@ void testMain() { }); tearDown(() { - WebExperiments.instance.reset(); - }); - - test('default web experiment values', () { - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - }); - - test('can turn on/off web experiments', () { - WebExperiments.instance.updateExperiment('useCanvasText', true); - expect(WebExperiments.instance.useCanvasText, true); - - WebExperiments.instance.updateExperiment('useCanvasText', false); - expect(WebExperiments.instance.useCanvasText, false); - - WebExperiments.instance.updateExperiment('useCanvasText', null); - // Goes back to default value. - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - }); - - test('ignores unknown experiments', () { - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - WebExperiments.instance.updateExperiment('foobarbazqux', true); - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - WebExperiments.instance.updateExperiment('foobarbazqux', false); - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - }); - - test('can reset web experiments', () { - WebExperiments.instance.updateExperiment('useCanvasText', false); - WebExperiments.instance.reset(); - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - - WebExperiments.instance.updateExperiment('useCanvasText', false); - WebExperiments.instance.updateExperiment('foobarbazqux', true); - WebExperiments.instance.reset(); - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - }); - - test('js interop also works', () { - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); - - expect(() => jsUpdateExperiment('useCanvasText', true), returnsNormally); - expect(WebExperiments.instance.useCanvasText, true); - - expect(() => jsUpdateExperiment('useCanvasText', null), returnsNormally); - expect(WebExperiments.instance.useCanvasText, _defaultUseCanvasText); + WebExperiments.instance!.reset(); }); test('js interop throws on wrong type', () { diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart index 15e381440125a..8fa9850d36aff 100644 --- a/lib/web_ui/test/engine/window_test.dart +++ b/lib/web_ui/test/engine/window_test.dart @@ -2,16 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; -import 'dart:js_util' as js_util; import 'dart:html' as html; +import 'dart:js_util' as js_util; import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart' as ui; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +const int kPhysicalKeyA = 0x00070004; +const int kLogicalKeyA = 0x00000000061; void main() { internalBootstrapBrowserTest(() => testMain); @@ -22,192 +24,240 @@ void testMain() { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onTextScaleFactorChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onTextScaleFactorChanged, same(callback)); }); - window.invokeOnTextScaleFactorChanged(); + EnginePlatformDispatcher.instance.invokeOnTextScaleFactorChanged(); }); test('onPlatformBrightnessChanged preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onPlatformBrightnessChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onPlatformBrightnessChanged, same(callback)); }); - window.invokeOnPlatformBrightnessChanged(); + EnginePlatformDispatcher.instance.invokeOnPlatformBrightnessChanged(); }); test('onMetricsChanged preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onMetricsChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onMetricsChanged, same(callback)); }); - window.invokeOnMetricsChanged(); + EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); }); test('onLocaleChanged preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onLocaleChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onLocaleChanged, same(callback)); }); - window.invokeOnLocaleChanged(); + EnginePlatformDispatcher.instance.invokeOnLocaleChanged(); }); test('onBeginFrame preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.FrameCallback callback = (_) { + void callback(Duration _) { expect(Zone.current, innerZone); - }; + } window.onBeginFrame = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onBeginFrame, same(callback)); }); - window.invokeOnBeginFrame(null); + EnginePlatformDispatcher.instance.invokeOnBeginFrame(Duration.zero); }); test('onReportTimings preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.TimingsCallback callback = (_) { + void callback(List _) { expect(Zone.current, innerZone); - }; + } window.onReportTimings = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onReportTimings, same(callback)); }); - window.invokeOnReportTimings(null); + EnginePlatformDispatcher.instance.invokeOnReportTimings([]); }); test('onDrawFrame preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onDrawFrame = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onDrawFrame, same(callback)); }); - window.invokeOnDrawFrame(); + EnginePlatformDispatcher.instance.invokeOnDrawFrame(); }); test('onPointerDataPacket preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.PointerDataPacketCallback callback = (_) { + void callback(ui.PointerDataPacket _) { expect(Zone.current, innerZone); - }; + } window.onPointerDataPacket = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onPointerDataPacket, same(callback)); }); - window.invokeOnPointerDataPacket(null); + EnginePlatformDispatcher.instance.invokeOnPointerDataPacket(const ui.PointerDataPacket()); + }); + + test('invokeOnKeyData returns normally when onKeyData is null', () { + const ui.KeyData keyData = ui.KeyData( + timeStamp: Duration(milliseconds: 1), + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + synthesized: true, + ); + expect(() { + EnginePlatformDispatcher.instance.invokeOnKeyData(keyData, (bool result) { + expect(result, isFalse); + }); + }, returnsNormally); + }); + + test('onKeyData preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + bool onKeyData(ui.KeyData _) { + expect(Zone.current, innerZone); + return false; + } + window.onKeyData = onKeyData; + + // Test that the getter returns the exact same onKeyData, e.g. it doesn't + // wrap it. + expect(window.onKeyData, same(onKeyData)); + }); + + const ui.KeyData keyData = ui.KeyData( + timeStamp: Duration(milliseconds: 1), + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + synthesized: true, + ); + EnginePlatformDispatcher.instance.invokeOnKeyData(keyData, (bool result) { + expect(result, isFalse); + }); + + window.onKeyData = null; }); test('onSemanticsEnabledChanged preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onSemanticsEnabledChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onSemanticsEnabledChanged, same(callback)); }); - window.invokeOnSemanticsEnabledChanged(); + EnginePlatformDispatcher.instance.invokeOnSemanticsEnabledChanged(); }); test('onSemanticsAction preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.SemanticsActionCallback callback = (_, __, ___) { + void callback(int _, ui.SemanticsAction __, ByteData? ___) { expect(Zone.current, innerZone); - }; + } window.onSemanticsAction = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onSemanticsAction, same(callback)); }); - window.invokeOnSemanticsAction(null, null, null); + EnginePlatformDispatcher.instance.invokeOnSemanticsAction(0, ui.SemanticsAction.tap, null); }); test('onAccessibilityFeaturesChanged preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.VoidCallback callback = () { + void callback() { expect(Zone.current, innerZone); - }; + } window.onAccessibilityFeaturesChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onAccessibilityFeaturesChanged, same(callback)); }); - window.invokeOnAccessibilityFeaturesChanged(); + EnginePlatformDispatcher.instance.invokeOnAccessibilityFeaturesChanged(); }); test('onPlatformMessage preserves the zone', () { final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { - final ui.PlatformMessageCallback callback = (_, __, ___) { + void callback(String _, ByteData? __, void Function(ByteData?)? ___) { expect(Zone.current, innerZone); - }; + } window.onPlatformMessage = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. expect(window.onPlatformMessage, same(callback)); }); - window.invokeOnPlatformMessage(null, null, null); + EnginePlatformDispatcher.instance.invokeOnPlatformMessage('foo', null, (ByteData? data) { + // Not testing anything here. + }); }); test('sendPlatformMessage preserves the zone', () async { @@ -220,7 +270,7 @@ void testMain() { window.sendPlatformMessage( 'flutter/debug-echo', inputData, - (outputData) { + (ByteData? outputData) { expect(Zone.current, innerZone); completer.complete(); }, @@ -238,7 +288,7 @@ void testMain() { window.sendPlatformMessage( 'flutter/__unknown__channel__', null, - (outputData) { + (ByteData? outputData) { responded = true; expect(outputData, isNull); }, @@ -250,36 +300,33 @@ void testMain() { /// Regression test for https://github.com/flutter/flutter/issues/66128. test('setPreferredOrientation responds even if browser doesn\'t support api', () async { - final html.Screen screen = html.window.screen; + final html.Screen screen = html.window.screen!; js_util.setProperty(screen, 'orientation', null); - bool responded = false; - final Completer completer = Completer(); - final ByteData inputData = JSONMethodCodec().encodeMethodCall(MethodCall( + final Completer completer = Completer(); + final ByteData inputData = const JSONMethodCodec().encodeMethodCall(const MethodCall( 'SystemChrome.setPreferredOrientations', - [])); + []))!; window.sendPlatformMessage( 'flutter/platform', inputData, - (outputData) { - responded = true; - completer.complete(); + (ByteData? outputData) { + completer.complete(true); }, ); - await completer.future; - expect(responded, true); + + expect(await completer.future, isTrue); }); - test('Window implements locale, locales, and locale change notifications', () async { + test('SingletonFlutterWindow implements locale, locales, and locale change notifications', () async { // This will count how many times we notified about locale changes. int localeChangedCount = 0; window.onLocaleChanged = () { localeChangedCount += 1; }; - // Cause DomRenderer to initialize itself. - domRenderer; + ensureDomRendererInitialized(); // We populate the initial list of locales automatically (only test that we // got some locales; some contributors may be in different locales, so we @@ -290,8 +337,9 @@ void testMain() { // Trigger a change notification (reset locales because the notification // doesn't actually change the list of languages; the test only observes // that the list is populated again). - window.debugResetLocales(); - expect(window.locales, null); + EnginePlatformDispatcher.instance.debugResetLocales(); + expect(window.locales, isEmpty); + expect(window.locale, equals(const ui.Locale.fromSubtags())); expect(localeChangedCount, 0); html.window.dispatchEvent(html.Event('languagechange')); expect(window.locales, isNotEmpty); @@ -307,7 +355,7 @@ void testMain() { window.sendPlatformMessage( 'flutter/service_worker', ByteData(0), - (outputData) { }, + (ByteData? outputData) { }, ); }); diff --git a/lib/web_ui/test/frame_timings_common.dart b/lib/web_ui/test/frame_timings_common.dart new file mode 100644 index 0000000000000..697373baf2a66 --- /dev/null +++ b/lib/web_ui/test/frame_timings_common.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; + +/// Tests frame timings in a renderer-agnostic way. +/// +/// See CanvasKit-specific and HTML-specific test files `frame_timings_test.dart`. +Future runFrameTimingsTest() async { + List? timings; + ui.window.onReportTimings = (List data) { + timings = data; + }; + Completer frameDone = Completer(); + ui.window.onDrawFrame = () { + final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); + sceneBuilder + ..pushOffset(0, 0) + ..pop(); + ui.window.render(sceneBuilder.build()); + frameDone.complete(); + }; + + // Frame 1. + ui.window.scheduleFrame(); + await frameDone.future; + expect(timings, isNull, reason: '100 ms hasn\'t passed yet'); + await Future.delayed(const Duration(milliseconds: 150)); + + // Frame 2. + frameDone = Completer(); + ui.window.scheduleFrame(); + await frameDone.future; + expect(timings, hasLength(2), reason: '100 ms passed. 2 frames pumped.'); + for (final ui.FrameTiming timing in timings!) { + expect(timing.vsyncOverhead, greaterThanOrEqualTo(Duration.zero)); + expect(timing.buildDuration, greaterThanOrEqualTo(Duration.zero)); + expect(timing.rasterDuration, greaterThanOrEqualTo(Duration.zero)); + expect(timing.totalSpan, greaterThanOrEqualTo(Duration.zero)); + } +} diff --git a/lib/web_ui/test/geometry_test.dart b/lib/web_ui/test/geometry_test.dart index 07ad7601d8d5d..8383941dc60fb 100644 --- a/lib/web_ui/test/geometry_test.dart +++ b/lib/web_ui/test/geometry_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:math' as math show sqrt; import 'dart:math' show pi; diff --git a/lib/web_ui/test/gesture_settings_test.dart b/lib/web_ui/test/gesture_settings_test.dart new file mode 100644 index 0000000000000..21e896edb2df3 --- /dev/null +++ b/lib/web_ui/test/gesture_settings_test.dart @@ -0,0 +1,68 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/ui.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + test('GestureSettings has a reasonable toString', () { + const GestureSettings gestureSettings = + GestureSettings(physicalDoubleTapSlop: 2.0, physicalTouchSlop: 1.0); + + expect(gestureSettings.toString(), + 'GestureSettings(physicalTouchSlop: 1, physicalDoubleTapSlop: 2)'); + }); + + test('GestureSettings has a correct equality', () { + // don't refactor these to be const, that defeats the point! + final double value = nonconst(2.0); + final GestureSettings settingsA = + GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 1.0); + final GestureSettings settingsB = + GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 3.0); + final GestureSettings settingsC = + GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 1.0); + + expect(settingsA, equals(settingsC)); + expect(settingsC, equals(settingsA)); + + expect(settingsA, isNot(equals(settingsB))); + expect(settingsC, isNot(equals(settingsB))); + + expect(settingsB, isNot(equals(settingsA))); + expect(settingsB, isNot(equals(settingsC))); + }); + + test('GestureSettings copyWith preserves already set values', () { + const GestureSettings initial = + GestureSettings(physicalDoubleTapSlop: 1.0, physicalTouchSlop: 1.0); + + final GestureSettings copyA = initial.copyWith(); + + expect(copyA.physicalDoubleTapSlop, 1.0); + expect(copyA.physicalTouchSlop, 1.0); + + final GestureSettings copyB = + copyA.copyWith(physicalDoubleTapSlop: 2.0, physicalTouchSlop: 2.0); + + expect(copyB.physicalDoubleTapSlop, 2.0); + expect(copyB.physicalTouchSlop, 2.0); + }); + + test('GestureSettings constructor defaults to null', () { + const GestureSettings settings = GestureSettings(); + + expect(settings.physicalDoubleTapSlop, null); + expect(settings.physicalTouchSlop, null); + }); +} + +// Prevent the linter from complaining about a const value so that +// non-identical equality can be tested. +T nonconst(T value) => value; diff --git a/lib/web_ui/test/golden_tests/engine/backdrop_filter_golden_test.dart b/lib/web_ui/test/golden_tests/engine/backdrop_filter_golden_test.dart deleted file mode 100644 index 930b15094b198..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/backdrop_filter_golden_test.dart +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (html.Node scene in html.document.querySelectorAll('flt-scene')) { - scene.remove(); - } - - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - // The black circle on the left should not be blurred since it is outside - // the clip boundary around backdrop filter. However there should be only - // one red dot since the other one should be blurred by filter. - test('Background should only blur at ancestor clip boundary', () async { - final Rect region = Rect.fromLTWH(0, 0, 190, 130); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(region); - builder.addPicture(Offset.zero, backgroundPicture); - - builder.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - ); - final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); - builder.addPicture(Offset.zero, circles1); - - builder.pushClipRect( - const Rect.fromLTRB(60, 10, 180, 120), - ); - builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - oldLayer: null); - final Picture circles2 = _drawTestPictureWithCircles(region, 90, 30); - builder.addPicture(Offset.zero, circles2); - builder.pop(); - builder.pop(); - builder.pop(); - - html.document.body.append(builder - .build() - .webOnlyRootElement); - - await matchGoldenFile('backdrop_filter_clip.png', region: region); - }); - - test('Background should only blur at ancestor clip boundary after move', () async { - final Rect region = Rect.fromLTWH(0, 0, 190, 130); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(region); - builder.addPicture(Offset.zero, backgroundPicture); - ClipRectEngineLayer clipEngineLayer = builder.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - ); - final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); - builder.addPicture(Offset.zero, circles1); - ClipRectEngineLayer clipEngineLayer2 = builder.pushClipRect( - const Rect.fromLTRB(60, 10, 180, 120), - ); - BackdropFilterEngineLayer oldBackdropFilterLayer = - builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - oldLayer: null); - final Picture circles2 = _drawTestPictureWithCircles(region, 90, 30); - builder.addPicture(Offset.zero, circles2); - builder.pop(); - builder.pop(); - builder.pop(); - builder.build(); - - // Now reparent filter layer in next scene. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.addPicture(Offset.zero, backgroundPicture); - builder2.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - oldLayer: clipEngineLayer - ); - builder2.addPicture(Offset.zero, circles1); - builder2.pushClipRect( - const Rect.fromLTRB(10, 75, 180, 120), - oldLayer: clipEngineLayer2 - ); - builder2.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - oldLayer: oldBackdropFilterLayer); - builder2.addPicture(Offset.zero, circles2); - builder2.pop(); - builder2.pop(); - builder2.pop(); - - html.document.body.append(builder2 - .build() - .webOnlyRootElement); - - await matchGoldenFile('backdrop_filter_clip_moved.png', region: region); - }); -} - -Picture _drawTestPictureWithCircles(Rect region, double offsetX, double offsetY) { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(region); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), 10, Paint()..style = PaintingStyle.fill); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 60), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1)); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 60), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1)); - return recorder.endRecording(); -} - -Picture _drawBackground(Rect region) { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(region); - canvas.drawRect( - region.deflate(8.0), - Paint() - ..style = PaintingStyle.fill - ..color = Color(0xFFE0FFE0) - ); - return recorder.endRecording(); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_arc_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_arc_golden_test.dart deleted file mode 100644 index 888a3f4f1e27a..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_arc_golden_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 400, 600); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws arcs with largeArc , anticlockwise variations', () async { - paintArc(canvas, Offset(0, 0), - largeArc: false, clockwise: false, distance: 20); - paintArc(canvas, Offset(200, 0), - largeArc: true, clockwise: false, distance: 20); - paintArc(canvas, Offset(0, 150), - largeArc: false, clockwise: true, distance: 20); - paintArc(canvas, Offset(200, 150), - largeArc: true, clockwise: true, distance: 20); - paintArc(canvas, Offset(0, 300), - largeArc: false, clockwise: false, distance: -20); - paintArc(canvas, Offset(200, 300), - largeArc: true, clockwise: false, distance: -20); - paintArc(canvas, Offset(0, 400), - largeArc: false, clockwise: true, distance: -20); - paintArc(canvas, Offset(200, 400), - largeArc: true, clockwise: true, distance: -20); - - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_arc_to_point.png', region: region); - }); - - test('Path.addArc that starts new path has correct start point', () async { - final Rect rect = Rect.fromLTWH(20, 20, 200, 200); - final Path p = Path() - ..fillType = PathFillType.evenOdd - ..addRect(rect) - ..addArc(Rect.fromCircle(center: rect.center, - radius: rect.size.shortestSide / 2), 0.25 * math.pi, 1.5 * math.pi); - canvas.drawPath(p, SurfacePaintData() - ..color = Color(0xFFFF9800) // orange - ..style = PaintingStyle.fill); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_addarc.png', region: region); - }); - - test('Should render counter clockwise arcs', () async { - final Path path = Path(); - path.moveTo(149.999999999999997, 50); - path.lineTo(149.999999999999997, 20); - path.arcTo(Rect.fromLTRB(20, 20, 280, 280), 4.71238898038469, - 5.759586531581287 - 4.71238898038469, true); - path.lineTo(236.60254037844385, 99.99999999999999); - path.arcTo(Rect.fromLTRB(50, 50, 250, 250), 5.759586531581287, - 4.71238898038469 - 5.759586531581287, true); - path.lineTo(149.999999999999997, 20); - canvas.drawPath(path, SurfacePaintData() - ..color = Color(0xFFFF9800) // orange - ..style = PaintingStyle.fill); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_addarc_ccw.png', region: region); - }); -} - -void paintArc(BitmapCanvas canvas, Offset offset, - {bool largeArc = false, bool clockwise = false, double distance = 0}) { - - final Offset startP = - Offset(75 - distance + offset.dx, 75 - distance + offset.dy); - final Offset endP = - Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); - canvas.drawRect( - Rect.fromLTRB(startP.dx, startP.dy, endP.dx, endP.dy), - SurfacePaintData() - ..strokeWidth = 1 - ..color = Color(0xFFFF9800) // orange - ..style = PaintingStyle.stroke); - final Path path = Path(); - path.moveTo(startP.dx, startP.dy); - path.arcToPoint(endP, - rotation: 45, - radius: const Radius.elliptical(40, 60), - largeArc: largeArc, - clockwise: clockwise); - canvas.drawPath( - path, - SurfacePaintData() - ..strokeWidth = 2 - ..color = Color(0x61000000) // black38 - ..style = PaintingStyle.stroke); -} 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 deleted file mode 100644 index 39cfcb4b27feb..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:js_util' as js_util; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500), - double maxDiffRatePercent = 0.0}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: maxDiffRatePercent); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('Blend circles with difference and color', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.drawRect( - Rect.fromLTRB(0, 0, 400, 400), - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(255, 255, 255, 255)); - rc.drawCircle( - Offset(100, 100), - 80.0, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0) - ..blendMode = BlendMode.difference); - - rc.drawCircle( - Offset(170, 100), - 80.0, - Paint() - ..style = PaintingStyle.fill - ..blendMode = BlendMode.color - ..color = const Color.fromARGB(128, 0, 255, 0)); - - rc.drawCircle( - Offset(135, 170), - 80.0, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0)); - rc.restore(); - await _checkScreenshot(rc, 'canvas_blend_circle_diff_color', - maxDiffRatePercent: operatingSystem == OperatingSystem.macOs ? 2.95 : 0); - }); - - test('Blend circle and text with multiply', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.drawRect( - Rect.fromLTRB(0, 0, 400, 400), - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(255, 255, 255, 255)); - rc.drawCircle( - Offset(100, 100), - 80.0, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0) - ..blendMode = BlendMode.difference); - rc.drawCircle( - Offset(170, 100), - 80.0, - Paint() - ..style = PaintingStyle.fill - ..blendMode = BlendMode.color - ..color = const Color.fromARGB(128, 0, 255, 0)); - - rc.drawCircle( - Offset(135, 170), - 80.0, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromARGB(128, 255, 0, 0)); - rc.drawImage(createTestImage(), Offset(135.0, 130.0), - Paint()..blendMode = BlendMode.multiply); - rc.restore(); - await _checkScreenshot(rc, 'canvas_blend_image_multiply', - maxDiffRatePercent: operatingSystem == OperatingSystem.macOs ? 2.95 : 0); - }); -} - -HtmlImage createTestImage() { - const int width = 100; - const int height = 50; - html.CanvasElement canvas = - new html.CanvasElement(width: width, height: height); - html.CanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(33, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(66, 0, 33, 50); - ctx.fill(); - html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return HtmlImage(imageElement, width, height); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart deleted file mode 100644 index 1436e934a59fa..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:js_util' as js_util; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart' as engine; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(engine.RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { - final engine.EngineCanvas engineCanvas = engine.BitmapCanvas(screenRect); - - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: 0.0); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - // Regression test for https://github.com/flutter/flutter/issues/48683 - // Should clip image with oval. - test('Clips image with oval clip path', () async { - final engine.RecordingCanvas rc = - engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - final Path path = Path(); - path.addOval(Rect.fromLTWH(100, 30, testWidth, testHeight)); - rc.clipPath(path); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTWH(100, 30, testWidth, testHeight), Paint()); - rc.restore(); - await _checkScreenshot(rc, 'image_clipped_by_oval'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/48683 - test('Clips triangle with oval clip path', () async { - final engine.RecordingCanvas rc = - engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - double testWidth = 200; - double testHeight = 150; - final Path path = Path(); - path.addOval(Rect.fromLTWH(100, 30, testWidth, testHeight)); - rc.clipPath(path); - final Path paintPath = new Path(); - paintPath.moveTo(testWidth / 2, 0); - paintPath.lineTo(testWidth, testHeight); - paintPath.lineTo(0, testHeight); - paintPath.close(); - rc.drawPath( - paintPath, - Paint() - ..color = Color(0xFF00FF00) - ..style = PaintingStyle.fill); - rc.restore(); - await _checkScreenshot(rc, 'triangle_clipped_by_oval'); - }); -} - -engine.HtmlImage createTestImage({int width = 200, int height = 150}) { - html.CanvasElement canvas = - new html.CanvasElement(width: width, height: height); - html.CanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, width / 3, height); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(width / 3, 0, width / 3, height); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(2 * width / 3, 0, width / 3, height); - ctx.fill(); - html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return engine.HtmlImage(imageElement, width, height); -} 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 deleted file mode 100644 index 226b8c8a61095..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart' as engine; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -/// Tests context save/restore. -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(engine.RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { - final engine.EngineCanvas engineCanvas = engine.BitmapCanvas(screenRect); - - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - // TODO(yjbanov): 10% diff rate is excessive. Update goldens. - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: 10); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - // Regression test for https://github.com/flutter/flutter/issues/49429 - // Should clip with correct transform. - test('Clips image with oval clip path', () async { - final engine.RecordingCanvas rc = - engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - final Paint paint = Paint() - ..color = Color(0xFF00FF00) - ..style = PaintingStyle.fill; - rc.save(); - final Path ovalPath = Path(); - ovalPath.addOval(Rect.fromLTWH(100, 30, 200, 100)); - rc.clipPath(ovalPath); - rc.translate(-500, -500); - rc.save(); - rc.translate(500, 500); - rc.drawPath(ovalPath, paint); - // The line below was causing SaveClipStack to incorrectly set - // transform before path painting. - rc.translate(-1000, -1000); - rc.save(); - rc.restore(); - rc.restore(); - rc.restore(); - // The rectangle should paint without clipping since we restored - // context. - rc.drawRect(Rect.fromLTWH(0, 0, 4, 200), paint); - await _checkScreenshot(rc, 'context_save_restore_transform'); - }); - - test('Should restore clip path', () async { - final engine.RecordingCanvas rc = - engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - final Paint goodPaint = Paint() - ..color = Color(0x8000FF00) - ..style = PaintingStyle.fill; - final Paint badPaint = Paint() - ..color = Color(0xFFFF0000) - ..style = PaintingStyle.fill; - rc.save(); - final Path ovalPath = Path(); - ovalPath.addOval(Rect.fromLTWH(100, 30, 200, 100)); - rc.clipPath(ovalPath); - rc.translate(-500, -500); - rc.save(); - rc.restore(); - // The rectangle should be clipped against oval. - rc.drawRect(Rect.fromLTWH(0, 0, 300, 300), badPaint); - rc.restore(); - // The rectangle should paint without clipping since we restored - // context. - rc.drawRect(Rect.fromLTWH(0, 0, 200, 200), goodPaint); - await _checkScreenshot(rc, 'context_save_restore_clip'); - }); -} 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 deleted file mode 100644 index 799ab41050a2e..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; -import 'dart:js_util' as js_util; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -import 'scuba.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500), - double maxDiffRatePercent = 0.0}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', - region: region, maxDiffRatePercent: maxDiffRatePercent); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - }); - - setUpStableTestFonts(); - - test('Paints image', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.drawImage(createTestImage(), Offset(0, 0), new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image'); - }); - - test('Paints image with transform', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 4.0); - rc.drawImage(createTestImage(), Offset(0, 0), new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image_with_transform'); - }); - - test('Paints image with transform and offset', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 4.0); - rc.drawImage(createTestImage(), Offset(30, 20), new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image_with_transform_and_offset'); - }); - - test('Paints image with transform using destination', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 4.0); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image_rect_with_transform'); - }); - - test('Paints image with source and destination', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.drawImageRect( - testImage, - Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image_rect_with_source'); - }); - - test('Paints image with source and destination and round clip', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.save(); - rc.clipRRect(RRect.fromLTRBR( - 100, 30, 2 * testWidth, 2 * testHeight, Radius.circular(16))); - rc.drawImageRect( - testImage, - Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image_rect_with_source_and_clip'); - }); - - test('Paints image with transform using source and destination', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - rc.translate(50.0, 100.0); - rc.rotate(math.pi / 6.0); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.drawImageRect( - testImage, - Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), - new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_image_rect_with_transform_source'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image not below. - test('Paints on top of image', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_on_image'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should below image not on top. - test('Paints below image', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_below_image'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rect. - test('Paints on top of image with clip rect', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.clipRect(Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_on_image_clip_rect'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rect and transform. - test('Paints on top of image with clip rect with transform', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - // Rotate around center of circle. - rc.translate(100, 100); - rc.rotate(math.pi / 4.0); - rc.translate(-100, -100); - rc.clipRect(Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_on_image_clip_rect_with_transform'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with stack of clip rect and transforms. - test('Paints on top of image with clip rect with stack', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - // Rotate around center of circle. - rc.translate(100, 100); - rc.rotate(-math.pi / 4.0); - rc.save(); - rc.translate(-100, -100); - rc.clipRect(Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), new Paint()); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.restore(); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_on_image_clip_rect_with_stack'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rrect. - test('Paints on top of image with clip rrect', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - rc.clipRRect(RRect.fromLTRBR(75, 75, 160, 160, Radius.circular(5))); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), Paint()); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_on_image_clip_rrect'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/44845 - // Circle should draw on top of image with clip rrect. - test('Paints on top of image with clip path', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - final Path path = Path(); - // Triangle. - path.moveTo(118, 57); - path.lineTo(75, 160); - path.lineTo(160, 160); - rc.clipPath(path); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), Paint()); - rc.drawCircle( - Offset(100, 100), - 50.0, - Paint() - ..strokeWidth = 3 - ..color = Color.fromARGB(128, 0, 0, 0)); - rc.restore(); - await _checkScreenshot(rc, 'draw_circle_on_image_clip_path'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/53078 - // Verified that Text+Image+Text+Rect+Text composites correctly. - // Yellow text should be behind image and rectangle. - // Cyan text should be above everything. - test('Paints text above and below image', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); - rc.save(); - Image testImage = createTestImage(); - double testWidth = testImage.width.toDouble(); - double testHeight = testImage.height.toDouble(); - final Paragraph paragraph1 = createTestParagraph( - 'should be below...............', - color: Color(0xFFFFFF40)); - paragraph1.layout(const ParagraphConstraints(width: 400.0)); - rc.drawParagraph(paragraph1, const Offset(20, 100)); - rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), - Rect.fromLTRB(100, 100, 200, 200), Paint()); - rc.drawRect( - Rect.fromLTWH(50, 50, 100, 200), - Paint() - ..strokeWidth = 3 - ..color = Color(0xA0000000)); - final Paragraph paragraph2 = createTestParagraph( - 'Should be above...............', - color: Color(0xFF00FFFF)); - paragraph2.layout(const ParagraphConstraints(width: 400.0)); - rc.drawParagraph(paragraph2, const Offset(20, 150)); - rc.restore(); - await _checkScreenshot(rc, 'draw_text_composite_order_below', - maxDiffRatePercent: 1.0); - }); - - // Creates a picture - test('Paints nine slice image', () async { - Rect region = const Rect.fromLTWH(0, 0, 500, 500); - EnginePictureRecorder recorder = EnginePictureRecorder(); - final Canvas canvas = Canvas(recorder, region); - Image testImage = createNineSliceImage(); - canvas.clipRect(Rect.fromLTWH(0, 0, 420, 200)); - canvas.drawImageNine(testImage, Rect.fromLTWH(20, 20, 20, 20), - Rect.fromLTWH(20, 20, 400, 400), Paint()); - Picture picture = recorder.endRecording(); - - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.addPicture(Offset(0, 0), picture); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(builder.build().webOnlyRootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('draw_nine_slice.png', - region: region, maxDiffRatePercent: 0); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - }); - - // Regression test for https://github.com/flutter/flutter/issues/61691 - // - // The bug in bitmap_canvas.dart was that when we transformed and clipped - // the image we did not apply `transform-origin: 0 0 0` to the clipping - // element which resulted in an undesirable offset. - test('Paints clipped and transformed image', () async { - final Rect region = const Rect.fromLTRB(0, 0, 60, 70); - final RecordingCanvas canvas = RecordingCanvas(region); - canvas.translate(10, 10); - canvas.transform(Matrix4.rotationZ(0.4).storage); - canvas.clipPath(Path() - ..moveTo(10, 10) - ..lineTo(50, 10) - ..lineTo(50, 30) - ..lineTo(10, 30) - ..close()); - canvas.drawImage(createNineSliceImage(), Offset.zero, Paint()); - await _checkScreenshot(canvas, 'draw_clipped_and_transformed_image', - region: region, maxDiffRatePercent: 1.0); - }); -} - -// 9 slice test image that has a shiny/glass look. -const String base64ImageData = 'data:image/png;base64,iVBORw0KGgoAAAANSUh' - 'EUgAAADwAAAA8CAYAAAA6/NlyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPo' - 'AAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAApGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQA' - 'AARoABQAAAAEAAABKARsABQAAAAEAAABSATEAAgAAACAAAABah2kABAAAAAEAAAB6AAAAAAAA' - 'AEgAAAABAAAASAAAAAFBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpAAADoAEAAwAA' - 'AAEAAQAAoAIABAAAAAEAAAA8oAMABAAAAAEAAAA8AAAAAKgRPeEAAAAJcEhZcwAACxMAAAs' - 'TAQCanBgAAATqaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOn' - 'g9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+CiAgIDxyZGY6Uk' - 'RGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW' - '5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAg' - 'IHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICA' - 'gICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1J' - 'lc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlL' - 'mNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2' - 'JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmF' - 'kb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAua' - 'WlkOjMxRTc0MTc5ODQwQTExRUE5OEU4QUI4OTRCMjhDRUE3PC94bXBNTTpJbnN0YW5jZUl' - 'EPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjMxRTc0MTdBODQwQTExR' - 'UE5OEU4QUI4OTRCMjhDRUE3PC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU0' - '6RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c' - '3RSZWY6aW5zdGFuY2VJRD54bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4MjJBQUI1NDhBQTA' - 'zMDNBPC9zdFJlZjppbnN0YW5jZUlEPgogICAgICAgICAgICA8c3RSZWY6ZG9jdW1lbnRJR' - 'D54bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4MjJBQUI1NDhBQTAzMDNBPC9zdFJlZjpkb2N' - '1bWVudElEPgogICAgICAgICA8L3htcE1NOkRlcml2ZWRGcm9tPgogICAgICAgICA8ZXhpZ' - 'jpQaXhlbFlEaW1lbnNpb24+NjA8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8' - 'ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl' - '4ZWxYRGltZW5zaW9uPjYwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPHhtcD' - 'pDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpPC94bXA6Q3Jl' - 'YXRvclRvb2w+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YX' - 'Rpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZ' - 'XRhPgpq1fpCAAAUDUlEQVRoBd1beYxd11n/3fvu2+fNm9VbPB6PHS+ldt0kNKWJk0YkaVJS' - 'UtRQhFCqSggRCVWgSKUKEgi1FNSKlvIH/1dVoWqatBKkUYqgAVRaNSTN0jg2buIkE4/XmbF' - 'ne/O2u/D7ffedN2/GM9hJCRAf+76z3LN8+/nuOd94jUYjwRWmJFnp6nkeVI+jCJ7vw+Mc9l' - 'Z9+M7qLK+MYHPPOmrvjulp17yunxvr+inXWmvh6Bl+2WJw2R7qwJUFehLHiIUEH58LWxIAa' - 'RerOoBi1Qi8S71AujaXW69O314k3XvXprURhrZ+ijyJ45HYIlLPWm7cevkVIUw+GieThFzN' - 'pAt0EUj0LiYQRNFBxnISs2L/00YRa6NkwHYAdsCLoBqpx7V5WjuTWZEMrat5faIvBgjxjRb' - 'ptG+IsKjJ6ToU5UTZLFdPENaXUL+4gNqFi1iuMT8/jcb8AtqkfBxHQNREVG8CzTaLbbaJGJ' - 'KKlCApQCkRCCHXELycvyMx2VyWawUI8jnEhQIJnEEuk0U2F6AyOopCdQil/ipKQwMsV+GrH' - '9UqabdJjAAZzuWLABukdREWgB5hSjSRR4py0fq5aUwdfxFTzzyDsz9+FrOPfR9NLIIoot2Z' - 'XMsIAeUSeL1zybWpvaMMxr2QdY1RUh89SkYI5qobl5ln+JDsKO89hNFbb8Tm91+P7e+9Hps' - 'm9qBQLiNutZAE0vFUCtn1kuStZ7SEcGgUyyBi/tpTT+HFb3wTk1/7KuqcQgBnc2UEWzfD6+' - '9HJiBlCZokQhSWCIbkaIbzrKRU3AwBa+6gwQaSl91Y55iI3JFeKgkOgo6oIyVeo4XW0hzCU1' - 'MkdkqUYQxj3xc+jet+5R4M7tlt3M6SST6ZtF5ajTAXSAhs1A4RZTy0KarP/O3f4Sd/+Gksc3' - 'Rx/FrkByrItglio4FQFK0vI9PiuM4/P6KeCxjqcIa8T/wM5yTYFAOhLNSka8SG/YigyRgBZ' - 'LNPmfBi9ucEIl7skf8Udb2LcszzeVI6i7DAvEBRZvvykRfQYr7pPdfjji98Hrs+eJidfWS' - 'yGseRtiA7dNIqhEXRiIaACoX6wgU8+eW/xgtf+kuUt4wjPzyIzMISsFgzg5TJS9cyCKg3YC' - '6xFzdowmwNQ81LBVPlkDoiQiipn9W4XtBtVRvnYJtgpDLxl/NJ9yU1VK+wSQvdjii6DUQt2g' - 'zCkAz2o03k54++aCL/kUe+hQN3302KUZcFX8c2aF2lVQgL2TBu09408fRX/gY//LPPoXLgAI' - 'pL5OTcIjIUt6BYQEA9aS/MIbk4Y3oqJkmAhJ7AdA+LloSA3qdcTHVbZSVxx6UURVdL+7ua7I' - 'HGxPz1N29DQm5HFHEZSEljuGkAzZ+d5HzL+NgTj2P/bXcgCVuENTAVc/N0ERZ3k4hL+hGe' - 'feQRPPHJ30bfwQMoTC8gaDSJaAl+u4Vw5hS5QvHGZhQPvxulrVuR3TSKbLmIbDGPDAHJUI' - 'yzfLiaGT0ZvtinFSWnZJGFmCVxU1RSYqNHNUgS8pY6G4qjXC+hpW8vN9BcmEfz7HnUJ09h6' - 'cWnTIdt3NZxtGln2st1tLaOoH7iZTNsn3j+WWzet5/UpS0hHC51EY6ikJIc4OzRo/jmDdcDW' - '8ZRlvI3G0SmBO/kKzbRtt/8HYzdfhjl8TEUaLBy+QK8XJ6IkPJEJvYFuf6rLN6yxP3bF/9FVN' - 'HUkDahVsVgERH06Fc7hDp6FEuVI4q1CBHRZoRUqcbsBUwdewlnHv4Ozr3wY2B4K9qUunB+Ccn' - 'EGM4fO4J9n/wEPvqlv0Kxr2xwS02VDGFxty0LWW/gH//i8zjy5a9g9F0H4J2aRXagD94bL2P' - 'gF2/FdZ96AJsOXY+gr4iWWBNSwORgaPNn0jwCPwV8bSFFTP0um9h1ZR4irYp++Lj9Wos0Z8/' - 'jlSe+h6N//lkkpVE0Kjk0l+po7tiGaSJ936OP4roP30N1Ipwdq20Ia+OOyd3Xf/IMvn34ZpR' - '370d2oYY8N/XM1AmM3H433vfQZ9A3MU7Pjtok4yEgmDLCziCy6srPem0rby9T4qRdqnW6SjS' - '6RXJcHCvSIaERO/kvT+KpT/0eGpu3o91oo1XKYe7MSQzdchPu//o3UN60CVnBw8f4LP5EyzW' - '89Njjphs5ikeWRPDZVkIfbvj9P0Dfrp2o1SgyNAQJrV9Cj8jn9uDTG5LeasvQk2GbPTkSa9WT' - 'RcAxAdvWe1b37dgCN5fNW7C1wPF+Nk8YfLTqNdQpmdtvvxPv+tPPwj83hWy1j/ksqvt/Aad/' - '8COcePbZVNU6BAu0X3o0MLOnT+O1L34RA9vGgLMzFOV+RJOvYNdDf4zKnmvRpEUsFqgPpKwM' - 'SkxDIXeuVadehU3zcvw2jY30je9lLELqnSRfap0ktBHGto5odLglQybeaf+XcZMPrZ3EV5kup' - 'lW05Yl4lDgvTycnyCMfpMRLWnRhucbE7bfh1D/cjOnnfojc5jE6KEvgbo3nHvt77D98C/J9VE' - '3CRjOq7d/HGz990RzFUb7IzNW57yU2YHTvPhLEQ5YTty7OYuH0edROT2HxzHksT0/TanNrWp' - 'xDe47cX2zS11623FukJCR8QPE357PWQXF1JvRTYSUHUWaN+zslIdNHJ7JShkfxDCpFeCznRk' - 'eQHxlFfvMI+rdsQ9+27SiMDFm/fHUE2266CTNEOEvCtJeX0Ld1DCe/+jVMPfAA9hy6zvyHQK' - 'Itakz++w8ovtRJcjJDKnr1OnJEeenlE4j4YbA4OYmZ53+Kucef4t57qrt/Sie0RyoJcO23ZA' - 'vLFD0CH5M7Cbnho2rt9lqdzNCZL9SxwtSvkO5YXIe45l2gpFxIZ' - '+6VCdNBDtdGk9/3Pgx/4L0YPXgQxf4BxPQM5WuH3MpydNKSoQriM8Dkc89j97sPmsGjfPhYmj' - 'mLcw8/TiM1iICdNalPDicDI3j16w8jnJqhDz1tiGWE0vZxUrFIv5nOIMXEo5RojMRX24dhTgd' - 'eX1zypyW06cceO61JtLv2jjuQ9dIuZS3K9dCpIOrpO9vCKJE0nK1aDc3jT2ORz0nOWdq6D+0' - 'cR1a2GNPkoVFfDa43/uMZ1D92H8qVCrzlZjM5+q//hu/e9SEM7NqNwlwNOQJugAj2ptx0cmKE' - 'nfWJSD2FfGhZa+k/ERK3TCyJtRx/NbHRxqm0cXJ9lKcIC71VqbdK5ZauewFdWT6g6CbcqCOq' - 'UXxhiW2UzCLbOEEzDtEqF7AwfQFLYQ2/+9JRXLN7F4KQYnTu1RO2TBDkKJ+LhDoVnFgISI8' - 'oBUmDulhr2GQihv6TiUysSWytlKKZtkpuV5J7v9KikuvjciG9Ura+a6pqk1GM6SiBHqDsix' - 'm3kQF4VAUdPEgyJH0+9+T87u04f/w4zk9NYsvuCaoNnY25o/9psi+vxp1U2MRCih/1ssgxRV' - 'WeU8JHhDAR5Hvxtlvu1CWAaut9tG/31n+uMtcXHInEXfPSxoDISuJc0geKT8Low1X6fvZnL5' - 'sP4dcWFzFNdpO39JWlv5yB/7splc9utbcgz8rkd02flWV7e7/9ZVu3A7syfiSa/y+Ep0+8h' - 'naL3wR1OuW1J/8J5YBWtNmyTdph3MV7DUIOdHsv+VHq9LncmLTz2/TrQOnAI//eq9Vt95k5' - 'dgx17kZ+/eK8MdTbOgyPCK/i7tsE19s9rSO6T5H36AbnCv1Y+NHT/MZfhL8wP297aoZfPT6' - '3pKspybhm6GsHg1V+Zc3y0HERwdJSymFRQ6ZW1s1R6J2MvFxWJV9+An1xmrVUpBsLC7YRyD' - 'lQuhqQNTy0XxtGxEkWneXaInUYy/R9WdnALnWGvDOzlIVyQ7lFEYUGz+T8Fo9GVPG43zqKv' - 'DPRuxRq4SM+K5f/0tYu1KYVU0VOR/d8ifWrIUmP0zPu9CRVx1h+yA96S5JpfYhepcmJN7+3' - '13FWr0Kknbr6Olq1REvmDuOuJnx1iZd+oBIrougnvJW7WpPEWB8yxNl2oozO4YKgkH6B6' - 'VqMMSZedj6HDRL/EMK1Xoq9VgEV2e8gm7YqmEIMtjD6WYp4q6W9WxTIo6aSPyXElyJtD1de' - 'PWtrv3byV3c77JsYYTz6o1PF/q5+VfP48vWYm4KWeJsB3h2P6Vyv2Vzt8Lj3ltbOhtu9J5Nu' - 'r3VmhnBxHEqT27wKNbHsaXijzbrvJCm6skPPtJdA6s9D8JaTrj/8mvpDWiUQ7jeeQOvAdFHl' - 'f55SJv8m+5mZfbPMsyhFNavhmKOvr0jlH553lEITf+rVLL4kt4gC/Xqn9iAkXdgOapyKXde' - 'wIVmfIzqZJHHqTXaeSYi7vHu2oredfeqTKFjvJ1CE/H+4Lb/rpGaf5V63XA4cdMwlQfQ7qU' - 'LZlsIkQhqriI6VEK6OjdNg8bYiy7uh4vgOzLOR0Qr8Hu6cMffI9XqUXk0OrdKTCJCS/XbKPW' - '+vqNg7f+9J7aWD2ZP/hapbTwRSBEEcMHwjatshXmnnNt5pMToox7vd/p3j4AE974dChDwICH' - 'gIL0vtPqJF3bVxVu72sBcAfYAouXEa48rpmyv/dWM1pYU89Qx163SbiK04re8iJ2G6IIh5R' - 'h2dImaVKoZ4NROQ2zzSDVBlCEFmUxWtmQWGD+iMORUNi9USsppZTb2Prdbb4Lb3NE/dOQL' - 'AwQY0+7u2NBeQnXcu5wLuncRT45TU1ru4DuN7m9z8UhuVI74nrS0sohW3UPrQHRgYHGa8V6' - 'B7Yg+VkWEUb72LFwoXKAZB96xZYuGe9HaPQAgQW9Atqjxti7WQTheUs9mdReuGSO9c3eVS' - 'IYmry1172ldzpHOZlLCsddJHc6fv3Fpaz82j4xydnbfZh2E4GDnIOJUKr1HpSfoBbxWK1' - 'KKNxwy6oQy4zzUtkGUnbjzRMwveQRshygmSoZAR3/Y7g7bDZGefqZfHOsQdHnaznFr+ro1' - 'uvkaWLowchxjewymNnfYBu+PtdFu2stwK7qVijgKsgzry+eKGL12L86igIhnXHGO1lp3uy' - 'Y3HLFhkiymfpm68j+XdD/M1eCSvXAV5j39LxnX02394trJ0l5SPr0JeXqjWK7WuTMofeTDG' - 'LyG16q6jKf08nM4QIk36iOMxqnc/xsI52bQYrBKm8FmERUw1UHd/W3wEFpxSAtJPJVbvdN' - '9d62dfpfMs712SgnLPKOHWwar3JIfrYTRg82Y7R40SZV2sywh/7BIV6g826F6qbDeeR4E5e' - 'rVjB00wcs5KHNOK2Yoq29zMSL3I50t0R0bPJVeSqC6bv/rbIIm65lcNH6RXrYpliz9kAJjT' - 'cmkf2lG7HlwCEUc4ot40U7CcCditsQvZH+fBmje/bi9Md/HfVHHkXmGsY/zTUQkHLupNr7f' - '3bo1dk/bEvSNiaDGRLGNiP0xN2xe+/F8PAoSgqmoxcp48eQKSJM2S4WixjkCf3YnXebwVI8' - 'FDWd5l2UpMUU9Yh8Wk7F14mSxOmyj8Tuv3s2msPGCIbV4yPqjcGinC8l4gpQi4YraJycRPH' - 'u7Cd3O1jjFmRCGfMi5RUE2FVioxXLPNrYnRiJzY9+CAWF2YQ9pWIPENEOaFy3YFbmYuEsUSe' - 'Wxj3git5Qo3Z4Iloztefg3rKdwqUSccShs4cEY1lyMjUFoNR236W8doeWoMlLE/xI4jE2Xnf' - 'r6I6NESEKwz8YfgFtyRjLt9ZIS+xrvRjqbKI8cO3YplUmnv0O8iN74Q/v8xbt4YFbsu8amB' - 'qxVlTiM6VpMt16xj71VOlQmsxWRIhCarrR5ui9sjur+maDJQZ2dvkzf8CJj73JxjZuxvV/g' - 'rKjKPuPai070EhoEi1EsW6f6CKBm/Wd37013Bsjlep//x9ZHdsZ1QNY6Pma3bJbArCMRrH' - '7T81zbIITGpJk9OwtIVbZ' - '/dNp0M3036d0iP91QvNopo9Ukg3P8v2wWAv6CuXaIz4TR9OMoaaXtXYZx7EjusY7FIYRJkI' - 'Z/XBQJV1qYuwsZsvyuU+9FWXMRBuw+77fwuvV4ex8O1vGY4Bg1kUkp8wfkLy7dH39hW84hE' - 'KKrhCfB2atgDFz074VaFYbpgkMcJI/V1im7Z4kyb9rQPjtuRTh5QoT1sMjVDCLTVcpN/w2qt' - 'kvI+df/QQdrz/RnJ2AGXaozIZmMsytkvi3Jm3i7omli6X+PU0PDBkccnJNWMIPn4fzk3swpn' - 'vfRf1I0eM4hokAvtFXqLn6aRkeDhGZ0VzWFwIraVO/HVzJ7ETB21AZ9G1meGr+WR9mDS3mC' - 'pLpQhbyi2LJDJvDqKFOkMw0vsw9VaYUumee3HNbb/M6NkJDPDIapB/DzHAs7oU2RWZ05Tda' - 'Fr3nRuRS8u8YJtnZMDsLAPReJ1aUxzI1CnMv/o65l9/A0tnX0Vy/gKi4ycMOIePANAjgJ' - 'cu8pO9VTWe0dxV1/bpnb10TjldirDPO4roHDttciMbEFpxziqu8YxvHMC5aFhcpTMoqEaH' - 'BxEf9+KsZLBcqmLsBqEtE4JZN6XGZi2xNjKixcY9sNQgTrDl5o892rTMLQajHHk+5CE8di' - 'fDKCVVXgvDwLptJiOcT7N5XTXglLdqmtQ1l+h6EqTYmFjJR0KZc5QlBWhY5F5BjP70bgqnD' - 'nPHSVH9zHPrbNEH6LET0HZn37+xUu5xG8DSqp0V7D0ItwVacEikdSjjjJgoqwuGvXNXGOg' - 'Z2x0yEXi0PeqFO87AiFGCkemvOKYkSF/6yS1vnLOWTaHN/VcmnSWt0eHThELBHBiCGisGra' - 'SI5l6R3qD0q05ZR4TNVHES7bnltEgcg6JF3uVlyFsBpdB+lzgdRTMHeejneZR0H1BrnKECH' - '7GyVy1BAmcsr17WxYs78QNqQF4bppFXqX9BBqIrzmcExwueASjAFzlaWncpqEpJCXVc7wv' - 'Nj7eT/BbztCaofk+k0AAAAAElFTkSuQmCC'; - -HtmlImage createNineSliceImage() { - return HtmlImage( - html.ImageElement()..src = base64ImageData, - 60, - 60, - ); -} - -HtmlImage createTestImage({int width = 100, int height = 50}) { - html.CanvasElement canvas = - new html.CanvasElement(width: width, height: height); - html.CanvasRenderingContext2D ctx = canvas.context2D; - ctx.fillStyle = '#E04040'; - ctx.fillRect(0, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#40E080'; - ctx.fillRect(33, 0, 33, 50); - ctx.fill(); - ctx.fillStyle = '#2040E0'; - ctx.fillRect(66, 0, 33, 50); - ctx.fill(); - html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); - return HtmlImage(imageElement, width, height); -} - -Paragraph createTestParagraph(String text, - {Color color = const Color(0xFF000000)}) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - )); - builder.pushStyle(TextStyle(color: color)); - builder.addText(text); - return builder.build(); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart deleted file mode 100644 index b70666711f5ba..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -final Rect region = Rect.fromLTWH(0, 0, 500, 100); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (html.Node scene in html.document.querySelectorAll('flt-scene')) { - scene.remove(); - } - - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('draw growing picture across frames', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const Rect.fromLTRB(0, 0, 100, 100), - ); - - _drawTestPicture(builder, 100, false); - builder.pop(); - - html.Element elm1 = builder.build().webOnlyRootElement; - html.document.body.append(elm1); - - // Now draw picture again but at larger size. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect( - const Rect.fromLTRB(0, 0, 100, 100), - ); - // Now draw the picture at original target size, which will use a - // different code path that should normally not have width/height set - // on image element. - _drawTestPicture(builder2, 20, false); - builder2.pop(); - - elm1.remove(); - html.document.body.append(builder2.build().webOnlyRootElement); - - await matchGoldenFile('canvas_draw_picture_acrossframes.png', - region: region); - }); - - test('draw growing picture across frames clipped', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const Rect.fromLTRB(0, 0, 100, 100), - ); - - _drawTestPicture(builder, 100, true); - builder.pop(); - - html.Element elm1 = builder.build().webOnlyRootElement; - html.document.body.append(elm1); - - // Now draw picture again but at larger size. - final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - builder2.pushClipRect( - const Rect.fromLTRB(0, 0, 100, 100), - ); - _drawTestPicture(builder2, 20, true); - builder2.pop(); - - elm1.remove(); - html.document.body.append(builder2.build().webOnlyRootElement); - - await matchGoldenFile('canvas_draw_picture_acrossframes_clipped.png', - region: region); - }); -} - -HtmlImage sharedImage; - -void _drawTestPicture(SceneBuilder builder, double targetSize, bool clipped) { - sharedImage ??= _createRealTestImage(); - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); - canvas.debugEnforceArbitraryPaint(); - if (clipped) { - canvas.clipRRect( - RRect.fromLTRBR(0, 0, targetSize, targetSize, Radius.circular(4))); - } - canvas.drawImageRect(sharedImage, Rect.fromLTWH(0, 0, 20, 20), - Rect.fromLTWH(0, 0, targetSize, targetSize), Paint()); - final Picture picture = recorder.endRecording(); - builder.addPicture( - Offset.zero, - picture, - ); -} - -typedef PaintCallback = void Function(RecordingCanvas canvas); - -const String _base64Encoded20x20TestImage = - 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' - 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' - 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; - -HtmlImage _createRealTestImage() { - return HtmlImage( - html.ImageElement() - ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', - 20, - 20, - ); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_points_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_points_test.dart deleted file mode 100644 index c3e6adaa0469c..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_points_test.dart +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 400, 600); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws points in all 3 modes', () async { - final SurfacePaintData paint = SurfacePaintData(); - paint.strokeWidth = 2.0; - paint.color = Color(0xFF0000FF); - final Float32List points = offsetListToFloat32List([ - Offset(10, 10), - Offset(50, 10), - Offset(70, 70), - Offset(170, 70) - ]); - canvas.drawPoints(PointMode.points, points, paint); - final Float32List points2 = offsetListToFloat32List([ - Offset(10, 110), - Offset(50, 110), - Offset(70, 170), - Offset(170, 170) - ]); - canvas.drawPoints(PointMode.lines, points2, paint); - final Float32List points3 = offsetListToFloat32List([ - Offset(10, 210), - Offset(50, 210), - Offset(70, 270), - Offset(170, 270) - ]); - canvas.drawPoints(PointMode.polygon, points3, paint); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_draw_points.png', region: region); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart deleted file mode 100644 index a072d025974fd..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -import 'scuba.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 500, 100); - - BitmapCanvas canvas; - - void appendToScene() { - // Create a element to make sure our CSS reset applies correctly. - final html.Element testScene = html.Element.tag('flt-scene'); - testScene.append(canvas.rootElement); - html.document.querySelector('flt-scene-host').append(testScene); - } - - setUpStableTestFonts(); - - tearDown(() { - html.document.querySelector('flt-scene').remove(); - }); - - /// Draws several lines, some aligned precisely with the pixel grid, and some - /// that are offset by 0.5 vertically or horizontally. - /// - /// The produced picture stresses the antialiasing generated by the browser - /// when positioning and rasterizing `` tags. Aliasing artifacts can - /// be seen depending on pixel alignment and whether antialiasing happens - /// before or after rasterization. - void drawMisalignedLines(BitmapCanvas canvas) { - final SurfacePaintData linePaint = (SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1) - .paintData; - - final SurfacePaintData fillPaint = - (SurfacePaint()..style = PaintingStyle.fill).paintData; - - canvas.translate(10, 10); - - canvas.drawRect( - const Rect.fromLTWH(0, 0, 40, 40), - linePaint, - ); - - canvas.drawLine( - const Offset(10, 0), - const Offset(10, 40), - linePaint, - ); - - canvas.drawLine( - const Offset(20.5, 0), - const Offset(20, 40), - linePaint, - ); - - canvas.drawCircle(const Offset(30, 10), 3, fillPaint); - canvas.drawCircle(const Offset(30.5, 30), 3, fillPaint); - } - - test('renders pixels that are not aligned inside the canvas', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 60, 60)); - - drawMisalignedLines(canvas); - - appendToScene(); - - await matchGoldenFile('misaligned_pixels_in_canvas_test.png', region: region); - }); - - test('compensates for misalignment of the canvas', () async { - // Notice the 0.5 offset in the bounds rectangle. It's what causes the - // misalignment of canvas relative to the pixel grid. BitmapCanvas will - // shift its position back to 0.0 and at the same time it will it will - // compensate by shifting the contents of the canvas in the opposite - // direction. - canvas = BitmapCanvas(const Rect.fromLTWH(0.5, 0.5, 60, 60)); - - drawMisalignedLines(canvas); - - appendToScene(); - - await matchGoldenFile('misaligned_canvas_test.png', region: region); - }); - - test('fill the whole canvas with color even when transformed', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50)); - - canvas.translate(25, 25); - canvas.drawColor(const Color.fromRGBO(0, 255, 0, 1.0), BlendMode.src); - - appendToScene(); - - await matchGoldenFile('bitmap_canvas_fills_color_when_transformed.png', region: region); - }); - - test('fill the whole canvas with paint even when transformed', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50)); - - canvas.translate(25, 25); - canvas.drawPaint(SurfacePaintData() - ..color = const Color.fromRGBO(0, 255, 0, 1.0) - ..style = PaintingStyle.fill); - - appendToScene(); - - await matchGoldenFile('bitmap_canvas_fills_paint_when_transformed.png', region: region); - }); - - // This test reproduces text blurriness when two pieces of text appear inside - // two nested clips: - // - // ┌───────────────────────┐ - // │ text in outer clip │ - // │ ┌────────────────────┐│ - // │ │ text in inner clip ││ - // │ └────────────────────┘│ - // └───────────────────────┘ - // - // This test clips using canvas. See a similar test in `compositing_golden_test.dart`, - // which clips using layers. - // - // More details: https://github.com/flutter/flutter/issues/32274 - test('renders clipped DOM text with high quality', () async { - final Paragraph paragraph = - (ParagraphBuilder(ParagraphStyle(fontFamily: 'Roboto'))..addText('Am I blurry?')).build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - - final Rect canvasSize = Rect.fromLTRB( - 0, - 0, - paragraph.maxIntrinsicWidth + 16, - 2 * paragraph.height + 32, - ); - final Rect outerClip = - Rect.fromLTRB(0.5, 0.5, canvasSize.right, canvasSize.bottom); - final Rect innerClip = Rect.fromLTRB(0.5, canvasSize.bottom / 2 + 0.5, - canvasSize.right, canvasSize.bottom); - - canvas = BitmapCanvas(canvasSize); - canvas.debugChildOverdraw = true; - canvas.clipRect(outerClip); - canvas.drawParagraph(paragraph, const Offset(8.5, 8.5)); - canvas.clipRect(innerClip); - canvas.drawParagraph(paragraph, Offset(8.5, 8.5 + innerClip.top)); - - expect( - canvas.rootElement.querySelectorAll('p').map((e) => e.innerText).toList(), - ['Am I blurry?', 'Am I blurry?'], - reason: 'Expected to render text using HTML', - ); - - appendToScene(); - - await matchGoldenFile( - 'bitmap_canvas_draws_high_quality_text.png', - region: canvasSize, - maxDiffRatePercent: 0.0, - pixelComparison: PixelComparison.precise, - ); - }, testOn: 'chrome'); - - // NOTE: Chrome in --headless mode does not reproduce the bug that this test - // attempts to reproduce. However, it's still good to have this test - // for potential future regressions related to paint order. - test('draws text on top of canvas when transformed and clipped', () async { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontSize: 18, - )); - - const String text = 'This text is intentionally very long to make sure that it ' - 'breaks into multiple lines.'; - builder.addText(text); - - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 100)); - - final Rect canvasSize = Offset.zero & Size(500, 500); - - canvas = BitmapCanvas(canvasSize); - canvas.debugChildOverdraw = true; - - final SurfacePaintData pathPaint = SurfacePaintData() - ..color = const Color(0xFF7F7F7F) - ..style = PaintingStyle.fill; - - const double r = 200.0; - const double l = 50.0; - - final Path path = (Path() - ..moveTo(-l, -l) - ..lineTo(0, -r) - ..lineTo(l, -l) - ..lineTo(r, 0) - ..lineTo(l, l) - ..lineTo(0, r) - ..lineTo(-l, l) - ..lineTo(-r, 0) - ..close()).shift(const Offset(250, 250)); - - canvas.drawPath(path, pathPaint); - canvas.drawParagraph(paragraph, const Offset(180, 50)); - - expect( - canvas.rootElement.querySelectorAll('p').map((e) => e.innerText).toList(), - [text], - reason: 'Expected to render text using HTML', - ); - - final SceneBuilder sb = SceneBuilder(); - sb.pushTransform(Matrix4.diagonal3Values(EngineWindow.browserDevicePixelRatio, - EngineWindow.browserDevicePixelRatio, 1.0).toFloat64()); - sb.pushTransform(Matrix4.rotationZ(math.pi / 2).toFloat64()); - sb.pushOffset(0, -500); - sb.pushClipRect(canvasSize); - sb.pop(); - sb.pop(); - sb.pop(); - sb.pop(); - final SurfaceScene scene = sb.build(); - final html.Element sceneElement = scene.webOnlyRootElement; - - sceneElement.querySelector('flt-clip').append(canvas.rootElement); - html.document.querySelector('flt-scene-host').append(sceneElement); - - await matchGoldenFile( - 'bitmap_canvas_draws_text_on_top_of_canvas.png', - region: canvasSize, - maxDiffRatePercent: 0.0, - pixelComparison: PixelComparison.precise, - ); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_image_blend_mode_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_image_blend_mode_test.dart deleted file mode 100644 index e5581be96687a..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_image_blend_mode_test.dart +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500), - double maxDiffRatePercent = 0.0}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: maxDiffRatePercent); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - const Color red = Color(0xFFFF0000); - const Color green = Color(0xFF00FF00); - const Color blue = Color(0xFF2196F3); - const Color white = Color(0xFFFFFFFF); - const Color grey = Color(0xFF808080); - const Color black = Color(0xFF000000); - - List> modes = [[BlendMode.clear, BlendMode.src, BlendMode.dst, - BlendMode.srcOver, BlendMode.dstOver, BlendMode.srcIn, BlendMode.dstIn, BlendMode.srcOut], - [BlendMode.dstOut, BlendMode.srcATop, BlendMode.dstATop, BlendMode.xor, - BlendMode.plus, BlendMode.modulate, BlendMode.screen, BlendMode.overlay], - [BlendMode.darken, BlendMode.lighten, BlendMode.colorDodge, BlendMode.hardLight, - BlendMode.softLight, BlendMode.difference, BlendMode.exclusion, BlendMode.multiply], - [BlendMode.hue, BlendMode.saturation, BlendMode.color, - BlendMode.luminosity]]; - - for (int blendGroup = 0; blendGroup < 4; ++blendGroup) { - test('Draw image with Group$blendGroup blend modes', () async { - final RecordingCanvas rc = RecordingCanvas( - const Rect.fromLTRB(0, 0, 400, 400)); - rc.save(); - List blendModes = modes[blendGroup]; - for (int row = 0; row < blendModes.length; row++) { - // draw white background for first 4, black for next 4 blends. - double top = row * 50.0; - rc.drawRect(Rect.fromLTWH(0, top, 200, 50), Paint() - ..color = white); - rc.drawRect(Rect.fromLTWH(200, top, 200, 50), Paint() - ..color = grey); - BlendMode blendMode = blendModes[row]; - rc.drawImage(createTestImage(), Offset(0, top), - Paint() - ..colorFilter = EngineColorFilter.mode(red, blendMode)); - rc.drawImage(createTestImage(), Offset(50, top), - Paint() - ..colorFilter = EngineColorFilter.mode(green, blendMode)); - rc.drawImage(createTestImage(), Offset(100, top), - Paint() - ..colorFilter = EngineColorFilter.mode(blue, blendMode)); - rc.drawImage(createTestImage(), Offset(150, top), - Paint() - ..colorFilter = EngineColorFilter.mode(black, blendMode)); - rc.drawImage(createTestImage(), Offset(200, top), - Paint() - ..colorFilter = EngineColorFilter.mode(red, blendMode)); - rc.drawImage(createTestImage(), Offset(250, top), - Paint() - ..colorFilter = EngineColorFilter.mode(green, blendMode)); - rc.drawImage(createTestImage(), Offset(300, top), - Paint() - ..colorFilter = EngineColorFilter.mode(blue, blendMode)); - rc.drawImage(createTestImage(), Offset(350, top), - Paint() - ..colorFilter = EngineColorFilter.mode(black, blendMode)); - } - rc.restore(); - await _checkScreenshot(rc, 'canvas_image_blend_group$blendGroup', - maxDiffRatePercent: 8.0); - }, - skip: browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs); - } - - // Regression test for https://github.com/flutter/flutter/issues/56971 - test('Draws image and paragraph at same vertical position', () async { - final RecordingCanvas rc = RecordingCanvas( - const Rect.fromLTRB(0, 0, 400, 400)); - rc.save(); - rc.drawRect(Rect.fromLTWH(0, 50, 200, 50), Paint() - ..color = white); - rc.drawImage(createTestImage(), Offset(0, 50), - Paint() - ..colorFilter = EngineColorFilter.mode(red, BlendMode.srcIn)); - - final Paragraph paragraph = createTestParagraph(); - const double textLeft = 80.0; - const double textTop = 50.0; - const double widthConstraint = 300.0; - paragraph.layout(const ParagraphConstraints(width: widthConstraint)); - rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); - - rc.restore(); - await _checkScreenshot(rc, 'canvas_image_blend_and_text', - maxDiffRatePercent: 8.0); - }); -} - -Paragraph createTestParagraph() { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - )); - builder.addText('FOO'); - return builder.build(); -} - - -// 50x50 pixel flutter logo image. -const String _flutterLogoBase64 = - 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAKRlWElm' - 'TU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgExAAIAAAAg' - 'AAAAWodpAAQAAAABAAAAegAAAAAAAABIAAAAAQAAAEgAAAABQWRvYmUgUGhvdG9zaG9wIENT' - 'NiAoTWFjaW50b3NoKQAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAMqADAAQAAAABAAAAMgAA' - 'AABWBXsWAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEemlUWHRYTUw6Y29tLmFkb2JlLnhtcAAA' - 'AAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENv' - 'cmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5' - 'OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjph' - 'Ym91dD0iIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94' - 'YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5j' - 'b20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0i' - 'aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0i' - 'aHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8eG1wTU06SW5zdGFu' - 'Y2VJRD54bXAuaWlkOjMyOERERjc5ODRCRjExRUE5QUE4OEM5NTZDREM5QkUyPC94bXBNTTp' - 'JbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjMyOERERj' - 'dBODRCRjExRUE5QUE4OEM5NTZDREM5QkUyPC94bXBNTTpEb2N1bWVudElEPgogICAgICAgI' - 'CA8eG1wTU06T3JpZ2luYWxEb2N1bWVudElEPnhtcC5kaWQ6MDE4MDExNzQwNzIwNjgxMTgy' - 'MkFBQjU0OEFBMDMwM0E8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHht' - 'cE1NOkRlcml2ZWRGcm9tIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICA' - 'gPHN0UmVmOmluc3RhbmNlSUQ+eG1wLmlpZDowNDgwMTE3NDA3MjA2ODExODIyQUFCNTQ4QU' - 'EwMzAzQTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SU' - 'Q+eG1wLmRpZDowMTgwMTE3NDA3MjA2ODExODIyQUFCNTQ4QUEwMzAzQTwvc3RSZWY6ZG9jd' - 'W1lbnRJRD4KICAgICAgICAgPC94bXBNTTpEZXJpdmVkRnJvbT4KICAgICAgICAgPHhtcDpD' - 'cmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpPC94bXA6Q3JlYXRv' - 'clRvb2w+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb2' - '4+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhP' - 'gr/+ApQAAAQNUlEQVRoBbVaDYxdRRWemTv3vt2WbrctpRYxtCVU4io1LomElrjYWnbbgtr2' - 'CbISwGpR0UBUjD8YHyZCNCqIP5GmtLWSAn3SVYqw7PbnFUQFXGOMRUFlW9G2gKalu/veu38' - 'zfufce3ff275dWtBJ5s7cuTPnnO+cM2d+3pN2urDiVSHEVCFuOpgT35vVIuaLV8Tgtw8Icf' - 'PZkZgntDggrhNCbhHCuihD9J402Y4OLUulqNy1+iwvFIscaaqRMWqyQRofIxFH2ohpwqjDc' - 't/OZyyYSTAVHQUtSoVIfPiL7xWu3i2i0KA7PnFmstpaEVMLBnHm1rFHhEbiQZ9PKtl83pPF' - 'YnB0xVVne2HlKcdz5wgTC62cZLwlGdJUSxXtWnngFItIOhdzj3xeiWIxzrpPVmpAZg4EJjc' - 'tAcmqazqNxjnJVwEOnKjDhMmuX+/KDRuCoeVXn9EcDf/K0c4cEwYBBmgBjSUJZVbnJiuh9' - '1BZ4ZnYREpNucgtPfAMW7VYjHjM7Ldlg1MaJxYaxlnNzVCUP4THrLRTdRiVWVaEcQ54fpu2' - 'JoTTl9rid+3tBCL8a1d3S1M0/ARAnGWiyEcfjK+xaB0Icg0ZQXGekEoo4V0qdwNEatVR+q8' - '8O6kCqZ+Wx0QPD6jgeTop72Xxd26Yx0/xYlJAFmJa4xdZO74kcwIgPpObF//rce3qhSYMAw' - 'zIpaqsEYSqaE0Kcmso04F/q/fr/uKeE0AQm5OwCCwqvCzn90MzMHEbsijsJ4f1RBuysFCa' - 'TGUaA0C1bGKjKufFh/Zo111kopAsQXQnTABAvu9IR6OiunX/jocagpiQQv0HDYJkhiS1Jc' - 'V+Lupe0g71BRg7mNjsbmHnml7t6IswJ3wog9xpggR4Uhh4mFTapTjwCb17xzbblgSJCQa9' - 'ZvOkIXGy0SkIjihh5+odcKfl6cSeBAQoSrinhYm1qwDi887uHXdbml/7i2MKnYzxBN9eFx' - 'ArCgqWYBDBpWu2wp0+aAKOTll0m4AdQbBGuZ4DELcCxHfINcXAwIRBhAmdxGQ/ZSAEQogC' - '0w861/7Q9dyrGQTNqwkTzxaAEBFAaBOa7zq7dhTIqqJUogk/2XQ6uck+Ie8GH9i7O0oKjI3' - 'ftfabAPEpE/i00mPFz1IDmZKmUHmua6J4g7O753NMq9hGobDBADRaK7NcaJuNbkLMoaV5gn' - 'RqFunocGjr4XeuvcXT7heMXyWXIPLMiHk0FEsEsIQXh/F9zq6fX8/9CgWsxYWG4Zy/1zzmz' - 'n2e6U9VpMPGacIP47vbdqzaAxvCYEX+RtfRd2Jix8IaGq/qdJoteCkBdPCldnNxFO9EiL2' - 'cmmsDRdrtJIv3X+Rdc/6TQRiQAoj3qPyTGGuMNhhj/7QhCDrz61zlEAjonbwCIGrTOBD4xC' - 'BsFO3R/T2vCQKupCSiGkpsYcU25NORg7Q9eu6Fg7POu+lOMWN6kxyB65EUWXpNIAmIYlBen' - 'r/CddRGG4UEAiHUJrvAGmIZ0bQMEGJhiehpZ/Gi94n+nmTxzPZP4zqnr5mGSa7lyE3UDnB' - 'UiJmtrUJUA+u2NktaySEHPTjVazRrTUvb1ZWjnWz10vzKZi3vp9ULoacxiHpr+ADhYa/1p' - '8PD8zpkoWDYNTG/xrEY/5pJRuWx9GNoDBhjl18ul7EJULzFoBBYmyYEwpZ49FG/0pV/T07Z' - 'h0l+YwlJaolaKmk9VWegHJ1DdHpBTZ1+8Vt+c0eFN5SYXw2GTNREpNJ9P0qJzVgSVFDWA8g' - 'INASSudPIijUXuNbsojkFrYR1IGrppdZAE0A4HhR4WLkti+XPtxwjWhcMDJwKiEy2sTLlVc' - 'uyZp5zvxPmSAbC78q/TRtbgiV1DEcHYneU0GgFNACC1IemUCkFEOaoEqctlo9sOZLRGpPo/' - '1ers0jG+Njy/HzHmCeUElPh5yE6aZadHg1B4GCklItwU45k08Wy76eDGa3/jei1TDOK9W2' - 'jQHgyYmIPL/3wnGkqfsJx5EwTxwECRmKJ+nFsCSKJ5gjLFIGIY6kvyT123/4sSOCjPJVcEi' - 'WW55g4lk63TOjXLnlgttgd7fhAa5OuPgk/fzOBwHCP3b8WBDWkcwLfAULCPSWCSW6Z91jxab' - 'YEgkTBpmvMKUC5RF7CUa1VtNJ5pkGqFaT+s04ORhvCwY5rm07zjpccxznHhFEV3Wg7bngC0J' - 'h6GqQxCok43SkRW3mZ7r2/lFoCIAqqIJPtx3K7fOp0QWfoydM/8Xn1S6vVzXNu9ntF75xO0Z' - 'mNsbRI0mgc7vlJ9WSyjwnF1zbUfJZ3fJfWDk53URmvCQjqjmU1nc/ULUkSNzfQuMLNSGzVlb' - 'qv+HA6J/y8zTsAwVv8lfHSexGx3zsim4fQPwunqI4JwHXI1wIuj5/RL5eY86f+Otj31c6mTr' - 'o0mIOcbUcgDAHBZjIZnxLhHYbQe3EeWOLO6NeOWgwQw+iTA3AIkvQb8yLGQJToYAQUjo6ts0' - '73bX8guz1JLcEgVkXLtjrK6Q5tSCvz3FHLMpnkQQIlWiZvBWPpisA/duvXf3v7FtEhvp52' - 'HQPCwmRWyZRB73CNDpwH7PLV/8FqBxFZ97Ryk6gpnaxg5CSkxRyaYoy4ESA28ekOtyckU0E' - 'UmPqqeOkPYK6rQxMeBxtsKhOmia8k9XFWiRylWlUgbnu6+R8F0GoBH+qe5URaFiUJ91yted' - 'C+2Kq+HWutMT3KUdNgPbgW+SSW8vpMExFhlkDILzt9D97F84sWu4S2gnrtqnjZ7VKpGyIT0f' - '2lQxrJMt5JO2nmCw36FmuAcEJz1/bcL7+C79ibHKM+pLTaDFWTTKPg6uoKvs2+q/p7VsMQvT' - 'D1DHSukHsBJM0FIgYQ0ldStUTWfMPp+9ntPA7WJBAdogPbCBGvsku/BDG/iHu2oxhDWiRmpI' - 'AYQoAO5awuqa3iKDFdhXbjTm/fjeDPboCoReNGQaA9fZdp2xgtsGH6fPbmczNGymDRZRhUQn' - 'UmBKxAWxlBHxxmGmPvcPt6biENi/R0RyBKshQttUtuALvbsB/7NyihyygI0ABjChDImKoRWQ' - 'FtFSy4s5zAbtvplT6O/mJADIwGBLyOAsEcI2GjRLGkAIl60gY2JCPtPMAWu9IkDBewMMcrMe' - 'A3YNQK34ab2Qosejri8I+dXT2fpf50ZqfTXbttdwnEvOicaw6Yl+4oi+rLjlUkOBNHRxKYB' - 'E8si0WT2iAERcZZOrA7dub2daMu8ohq7aKdvlMiRVCdAWCK8ThugyIQSfGe0IL0iUXwkRODo' - 'WuZnTvL0puyCjr9Iz7QAacVN4Fbnf6eT1JHW8ANSgpiQA6EC6IFq+FyP8BN+n9eEEeiEVnF' - 'tbuC8BYMUhAQiN6BLgEhzUzHyN6HcvvWEk0K2UWOlPQ2mjIgMeYtA8KXpMzeUyuTpdki2VC' - 'Jicur/C+3HcWEXQlBDkN7jwDEtdSH5gWdLcgSBGK+Pfd9WBDvRph71REKBzZrB+3hqCICEF' - 'YQnMAkIFDSnCvD3VsB4okFB5rX4N122A5dlA1v3OuFz0DAAuRaRBd2G811QPBR0Lmc3ayv5' - '2VldCe2UeuonUHgLqvNtnkE4hx7zkUw8T3wAdwa2ypOKwYzPoyFiV4Qh7A1qAHDmpMj6DcN' - 'IH4/52/hmu+f+6hPIMg1iX6DlAEJMW+5DhdDCdciS3Od3pN8wjaeCLKbJdehB+md3alQiF' - 'NLBPPtwkU4c2zGygJCxoeGcPUqcQam5VuJEPP8gDgsFoi52MNoeKmhfdt0x8q/tLwi8pvO' - 'e3IonV+TnVPoG+UIkx3GQ+IYQnakOjKVaWoIhL7RTSKH2DbcPcGdUu2FC+yCc42JtkB4zB' - 'U7hJ4uaAa8XIIwgdEAU4EMg+KInC/mRtq6LdLEL055VV1535l7/r0errkBVs2EaFCSiKTtD' - 'AwDgbK4KzZfKYgxJMmXBpTqmmgni0PiPDvvPBk7GyH0TAwsI5TiB5w6xWAY9I8nxVEf7jZN' - 'TvEWSOdwa1V//P7mPxxotwLziwWsY0EvtI5A+dhfWPqVaSfybGS6RSGjyAP/PCTmf+w2MfuM' - 'aeI4lkefGKXphDmSfRhXMmAbOXOxZM2AiY+DOKYDbvKtCGBxMGOGXNJ7bGTgKeWPhH82T93' - 'bdYRAEM0Bub2G/Tgu9a9kDXLJzCqoS3oHD4nfXlCX+J7mkwNCoRHuf9D9+14o7CaAyAEQX' - 'Sv5VCLeBtiIMBPYLmHoaNyiDcaV4mePlje8Y9G06++5O5HzQ7HIb69d+JLm+if5DB0lKFe' - 'w5HHJ75JAMEDwIZ5JPjkgoADksdgr9KAc7MeK/DVw8gDCxx4NHgTCsAATNSCsXJwsnw/K2' - 'z8TlO98Z6wXRkfiYdXe3L35W0RKFAEG6xHXGz/IA+goQfdaTeBH9WY+xBnrsxUYAPFMrDQ' - 'ZMYwdly7BBETIBJiHsFP+NgRvggWqWMwpI3oBmMj5Vj7nl39xvV/5xrt85/zAt1W4obWHs' - 'OAvabpq0y1MFQGkARiyBCVypz3IjyD3wgMeQ/nwSLn6lMi5EWIvpocEIALFwHz2fXQ6+YR' - 'whc40+eOzo7OvwvRchw0JXaZBKTn8RvycrfReJ6ufXizkhcmSaHCKhPWgPEVXSrOMUL/wt1' - '33vYQpxTuOq8nrpM8FC5u683dh0SqDP8kxKv+pWYSY0BmcNoSIZAf1wW04Nf0E95dNiMJlg' - 'KgGu7v94NoLq84FcdUGsIVUsFbq7zguIigcQgC8tKl780eJXJI4eGcvXMIKKst79+7lZaJ9f' - 'Xeuis0sZKAzPeaNHc2nDoTYJGBoyZD/kINbrWl60Kq/5oJf5YcrH1tStctUNdROJdJuFf' - 'eSVei7CgCcMZbc5hDm1grvqs15tkbh1jrtMgtEFQrFlH/0o1fY5Q5VeIWniZ9k0ISpOb8' - '+IMyJLUM18aJ+blP49BW9w99Z5oUXNg+FWlWMlVV4DJUVSAMtphlC4NcICptHsIG9PNd9T' - 'xfmCs2XE8AwcTyKbc8ykMMWgZAOfpJ3z2QZZMP59QMhLmSZNPq89O4HNsYt7uPWiTUCJB1x' - 'y7AENomU+VcA/BKQvBNAjIQmASY2l+srN1+cgDnRxTIwSakoaJQ5SzpiEKAkvzEgRL0m+lS' - '3fnQjzn/PQEsKAQCLphgBGGQLUJSTd2onUABCIF9WNrrMvWLTu0QBR+zJwjJub2ENuiAhA' - 'NiEZtmOvHEg48Hcv24z/ABnGUnniFeh46HxGYCGpDVDCDzD+M0Ll14KkyDuyn3knrfWKia' - 'xQs2TNtcYC08YwrpFgOiaCVmB1v8ykTbZQnu19/zgSmXs6QjKFHKZD52U6pIBJPoGc+G6i' - 'koVGq9PFK/5FyISxuA7p7R+7c1vEqGzAusRBQwk2j0mabSSNbzhMgPTsVe7Zx54O85TYz9b' - 'G7q0QYqx43NQp5J+DyaxgBqC4d9IEn9j8Z8VxRvogo76E5ik7C7gmmjkHXjFDz64oKFxDs5' - 'rMQ4IqP4fUo0219/tijMXppoFq1LKbmHySy2/PY/v9H50hhEz8KvE0RkS2xhsP8YlvvGZ3S' - 'yapiT0mk/DqjIsBcr/Atffr8/hCjApAAAAAElFTkSuQmCC'; - -HtmlImage createTestImage() { - return HtmlImage( - html.ImageElement() - ..src = 'data:text/plain;base64,$_flutterLogoBase64', - 50, - 50, - ); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_lines_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_lines_golden_test.dart deleted file mode 100644 index 8447c26806d47..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_lines_golden_test.dart +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 300, 300); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws lines with varying strokeWidth', () async { - - paintLines(canvas); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_lines_thickness.png', region: region); - }); -} - -void paintLines(BitmapCanvas canvas) { - final SurfacePaintData paint1 = SurfacePaintData() - ..color = Color(0xFF9E9E9E) // Colors.grey - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final SurfacePaintData paint2 = SurfacePaintData() - ..color = Color(0x7fff0000) - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final SurfacePaintData paint3 = SurfacePaintData() - ..color = Color(0xFF4CAF50) //Colors.green - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - // Draw markers around 100x100 box - canvas.drawLine(Offset(50, 50), Offset(52, 50), paint1); - canvas.drawLine(Offset(150, 50), Offset(148, 50), paint1); - canvas.drawLine(Offset(50, 150), Offset(52, 150), paint1); - canvas.drawLine(Offset(150, 150), Offset(148, 150), paint1); - // Draw diagonal - canvas.drawLine(Offset(50, 50), Offset(150, 150), paint2); - // Draw horizontal - paint3.strokeWidth = 1.0; - paint3.color = Color(0xFFFF0000); - canvas.drawLine(Offset(50, 55), Offset(150, 55), paint3); - paint3.strokeWidth = 2.0; - paint3.color = Color(0xFF2196F3); // Colors.blue; - canvas.drawLine(Offset(50, 60), Offset(150, 60), paint3); - paint3.strokeWidth = 4.0; - paint3.color = Color(0xFFFF9800); // Colors.orange; - canvas.drawLine(Offset(50, 70), Offset(150, 70), paint3); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_mask_filter_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_mask_filter_test.dart deleted file mode 100644 index 1f3184c67a097..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_mask_filter_test.dart +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' as ui; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, ui.Rect screenRect, {bool write = false}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: screenRect, maxDiffRatePercent: 0.0, write: write); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - ui.debugEmulateFlutterTesterEnvironment = true; - await ui.webOnlyInitializePlatform(); - ui.webOnlyFontCollection.debugRegisterTestFonts(); - await ui.webOnlyFontCollection.ensureFontsLoaded(); - }); - - tearDown(() { - ContextStateHandle.debugEmulateWebKitMaskFilter = false; - }); - - // Regression test for https://github.com/flutter/flutter/issues/55930 - void testMaskFilterBlur({bool isSafariMode}) { - final String browser = isSafariMode ? 'Safari' : 'Chrome'; - - test('renders MaskFilter.blur in $browser', () async { - const double screenWidth = 800.0; - const double screenHeight = 150.0; - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - ContextStateHandle.debugEmulateWebKitMaskFilter = isSafariMode; - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(0, 75); - - final SurfacePaint paint = SurfacePaint() - ..maskFilter = ui.MaskFilter.blur(ui.BlurStyle.normal, 5); - - rc.translate(50, 0); - rc.drawRect( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), - paint, - ); - - rc.translate(100, 0); - paint.color = ui.Color(0xFF00FF00); - rc.drawRRect( - ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), - ui.Radius.circular(20), - ), - paint, - ); - - rc.translate(100, 0); - paint.color = ui.Color(0xFF0000FF); - rc.drawCircle(ui.Offset.zero, 30, paint); - - rc.translate(100, 0); - paint.color = ui.Color(0xFF00FFFF); - rc.drawPath( - SurfacePath() - ..moveTo(-20, 0) - ..lineTo(0, -50) - ..lineTo(20, 0) - ..lineTo(0, 50) - ..close(), - paint, - ); - - rc.translate(100, 0); - paint.color = ui.Color(0xFFFF00FF); - rc.drawOval( - ui.Rect.fromCenter(center: ui.Offset.zero, width: 40, height: 100), - paint, - ); - - rc.translate(100, 0); - paint.color = ui.Color(0xFF888800); - paint.strokeWidth = 5; - rc.drawLine( - ui.Offset(-20, -50), - ui.Offset(20, 50), - paint, - ); - - rc.translate(100, 0); - paint.color = ui.Color(0xFF888888); - rc.drawDRRect( - ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 35), - ui.Radius.circular(20), - ), - ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 15), - ui.Radius.circular(7), - ), - paint, - ); - - rc.translate(100, 0); - paint.color = ui.Color(0xFF6500C9); - rc.drawRawPoints( - ui.PointMode.points, - Float32List.fromList([-10, -10, -10, 10, 10, -10, 10, 10]), - paint, - ); - - await _checkScreenshot(rc, 'mask_filter_$browser', screenRect); - }); - - test('renders transformed MaskFilter.blur in $browser', () async { - const double screenWidth = 300.0; - const double screenHeight = 300.0; - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - ContextStateHandle.debugEmulateWebKitMaskFilter = isSafariMode; - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(150, 150); - - final SurfacePaint paint = SurfacePaint() - ..maskFilter = ui.MaskFilter.blur(ui.BlurStyle.normal, 5); - - final List colors = [ - ui.Color(0xFF000000), - ui.Color(0xFF00FF00), - ui.Color(0xFF0000FF), - ui.Color(0xFF00FFFF), - ui.Color(0xFFFF00FF), - ui.Color(0xFF888800), - ui.Color(0xFF888888), - ui.Color(0xFF6500C9), - ]; - - for (ui.Color color in colors) { - paint.color = color; - rc.rotate(math.pi / 4); - rc.drawRect( - ui.Rect.fromCircle(center: const ui.Offset(90, 0), radius: 20), - paint, - ); - } - - await _checkScreenshot(rc, 'mask_filter_transformed_$browser', screenRect); - }); - } - - testMaskFilterBlur(isSafariMode: false); - testMaskFilterBlur(isSafariMode: true); - - for (int testDpr in [1, 2, 4]) { - test('MaskFilter.blur blurs correctly for device-pixel ratio $testDpr', () async { - window.debugOverrideDevicePixelRatio(testDpr.toDouble()); - const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 150, 150); - - final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.translate(0, 75); - - final SurfacePaint paint = SurfacePaint() - ..maskFilter = ui.MaskFilter.blur(ui.BlurStyle.normal, 5); - - rc.translate(75, 0); - rc.drawRect( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), - paint, - ); - - await _checkScreenshot(rc, 'mask_filter_blur_dpr_$testDpr', screenRect); - window.debugOverrideDevicePixelRatio(1.0); - }); - } -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_rect_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_rect_golden_test.dart deleted file mode 100644 index 07d19e51dc902..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_rect_golden_test.dart +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 150, 420); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws rect with flipped coordinates L > R, T > B', () async { - - paintRects(canvas); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_rect_flipped.png', region: region); - }); -} - -void paintRects(BitmapCanvas canvas) { - - canvas.drawRect(Rect.fromLTRB(30, 40, 100, 50), - SurfacePaintData() - ..color = Color(0xFF4CAF50) //Colors.green - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke); - - // swap left and right. - canvas.drawRect(Rect.fromLTRB(100, 150, 30, 140), - SurfacePaintData() - ..color = Color(0xFFF44336) //Colors.red - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke); - - // Repeat above for fill - canvas.drawRect(Rect.fromLTRB(30, 240, 100, 250), - SurfacePaintData() - ..color = Color(0xFF4CAF50) //Colors.green - ..style = PaintingStyle.fill); - - // swap left and right. - canvas.drawRect(Rect.fromLTRB(100, 350, 30, 340), - SurfacePaintData() - ..color = Color(0xFFF44336) //Colors.red - ..style = PaintingStyle.fill); -} 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 deleted file mode 100644 index 2bd62f258fa20..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - final Paint testPaint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFFFF00FF); - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - // Regression test for https://github.com/flutter/flutter/issues/51514 - test('Canvas is reused and z-index doesn\'t leak across paints', () async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - const Rect region = Rect.fromLTWH(0, 0, 500, 500); - - // Draw first frame into engine canvas. - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); - final Path path = Path() - ..moveTo(3, 0) - ..lineTo(100, 97); - rc.drawPath(path, testPaint); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - engineCanvas.endOfPaint(); - - html.Element sceneElement = html.Element.tag('flt-scene'); - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - - final html.CanvasElement canvas = html.document.querySelector('canvas'); - // ! Since canvas is first element, it should have zIndex = -1 for correct - // paint order. - expect(canvas.style.zIndex , '-1'); - - // Add id to canvas element to test for reuse. - const String kTestId = 'test-id-5698'; - canvas.id = kTestId; - - sceneElement.remove(); - // Clear so resources are marked for reuse. - - engineCanvas.clear(); - - // Now paint a second scene to same [BitmapCanvas] but paint an image - // before the path to move canvas element into second position. - final RecordingCanvas rc2 = - RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); - final Path path2 = Path() - ..moveTo(3, 0) - ..quadraticBezierTo(100, 0, 100, 100); - rc2.drawImage(_createRealTestImage(), Offset(0, 0), Paint()); - rc2.drawPath(path2, testPaint); - rc2.endRecording(); - rc2.apply(engineCanvas, screenRect); - - sceneElement = html.Element.tag('flt-scene'); - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - - final html.CanvasElement canvas2 = html.document.querySelector('canvas'); - // ZIndex should have been cleared since we have image element preceding - // canvas. - expect(canvas.style.zIndex != '-1', true); - expect(canvas2.id, kTestId); - await matchGoldenFile('bitmap_canvas_reuse_zindex.png', region: region); - }); -} - -const String _base64Encoded20x20TestImage = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' - 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' - 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; - -HtmlImage _createRealTestImage() { - return HtmlImage( - html.ImageElement() - ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', - 20, - 20, - ); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_rrect_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_rrect_golden_test.dart deleted file mode 100644 index 1506419d20e74..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_rrect_golden_test.dart +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(8, 8, 500, 100); // Compensate for old scuba tester padding - - BitmapCanvas canvas; - - final SurfacePaintData niceRRectPaint = SurfacePaintData() - ..color = const Color.fromRGBO(250, 186, 218, 1.0) // #fabada - ..style = PaintingStyle.fill; - - // Some values to see how the algo behaves as radius get absurdly large - const List rRectRadii = [0, 10, 20, 80, 8000]; - - const Radius someFixedRadius = Radius.circular(10); - - setUp(() { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 500, 100)); - canvas.translate(10, 10); // Center - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('round square with big (equal) radius ends up as a circle', () async { - for (int i = 0; i < 5; i++) { - canvas.drawRRect( - RRect.fromRectAndRadius(Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), - Radius.circular(rRectRadii[i])), - niceRRectPaint); - } - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_rrect_round_square.png', region: region); - }); - - test('round rect with big radius scale down smaller radius', () async { - for (int i = 0; i < 5; i++) { - final Radius growingRadius = Radius.circular(rRectRadii[i]); - final RRect rrect = RRect.fromRectAndCorners( - Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), - bottomRight: someFixedRadius, - topRight: growingRadius, - bottomLeft: growingRadius); - - canvas.drawRRect(rrect, niceRRectPaint); - } - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_rrect_overlapping_radius.png', region: region); - }); - - test('diff round rect with big radius scale down smaller radius', () async { - for (int i = 0; i < 5; i++) { - final Radius growingRadius = Radius.circular(rRectRadii[i]); - final RRect outerRRect = RRect.fromRectAndCorners( - Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), - bottomRight: someFixedRadius, - topRight: growingRadius, - bottomLeft: growingRadius); - - // Inner is half of outer, but offset a little so it looks nicer - final RRect innerRRect = RRect.fromRectAndCorners( - Rect.fromLTWH(100 * i.toDouble() + 5, 5, 40, 40), - bottomRight: someFixedRadius / 2, - topRight: growingRadius / 2, - bottomLeft: growingRadius / 2); - - canvas.drawDRRect(outerRRect, innerRRect, niceRRectPaint); - } - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_drrect_overlapping_radius.png', region: region); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_stroke_joins_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_stroke_joins_golden_test.dart deleted file mode 100644 index 8ae75205bfb47..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_stroke_joins_golden_test.dart +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 300, 300); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws stroke joins', () async { - - paintStrokeJoins(canvas); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_stroke_joins.png', region: region); - }); - -} - -void paintStrokeJoins(BitmapCanvas canvas) { - canvas.drawRect(Rect.fromLTRB(0, 0, 300, 300), - SurfacePaintData() - ..color = Color(0xFFFFFFFF) - ..style = PaintingStyle.fill); // white - - Offset start = Offset(20, 10); - Offset mid = Offset(120, 10); - Offset end = Offset(120, 20); - - var strokeCaps = [StrokeCap.butt, StrokeCap.round, StrokeCap.square]; - for (StrokeCap cap in strokeCaps) { - var joints = [StrokeJoin.miter, StrokeJoin.bevel, StrokeJoin.round]; - var colors = [Color(0xFFF44336), Color(0xFF4CAF50), Color(0xFF2196F3)]; // red, green, blue - for (int i = 0; i < joints.length; i++) { - var join = joints[i]; - var color = colors[i % colors.length]; - - Path path = new Path(); - path.moveTo(start.dx, start.dy); - path.lineTo(mid.dx, mid.dy); - path.lineTo(end.dx, end.dy); - canvas.drawPath(path, SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 4 - ..color = color - ..strokeJoin = join - ..strokeCap = cap); - - start = start.translate(0, 20); - mid = mid.translate(0, 20); - end = end.translate(0, 20); - } - } -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_stroke_rects_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_stroke_rects_golden_test.dart deleted file mode 100644 index 4a81d2fe52665..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_stroke_rects_golden_test.dart +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 300, 300); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws rects side by side with fill and stroke', () async { - - paintSideBySideRects(canvas); - - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_stroke_rects.png', region: region); - }); - -} - -void paintSideBySideRects(BitmapCanvas canvas) { - canvas.drawRect(Rect.fromLTRB(0, 0, 300, 300), - SurfacePaintData() - ..color = Color(0xFFFFFFFF) - ..style = PaintingStyle.fill); // white - - canvas.drawRect(Rect.fromLTRB(0, 20, 40, 60), - SurfacePaintData() - ..style = PaintingStyle.fill - ..color = Color(0x7f0000ff)); - canvas.drawRect(Rect.fromLTRB(40, 20, 80, 60), - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 4 - ..color = Color(0x7fff0000)); - - // Rotate 30 degrees (in rad: deg*pi/180) - canvas.transform(new Matrix4.rotationZ(30.0 * math.pi / 180.0).storage); - - canvas.drawRect(Rect.fromLTRB(100, 60, 140, 100), - SurfacePaintData() - ..style = PaintingStyle.fill - ..color = Color(0x7fff00ff)); - canvas.drawRect(Rect.fromLTRB(140, 60, 180, 100), - SurfacePaintData() - ..style = PaintingStyle.stroke - ..strokeWidth = 4 - ..color = Color(0x7fffff00)); -} diff --git a/lib/web_ui/test/golden_tests/engine/canvas_winding_rule_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_winding_rule_test.dart deleted file mode 100644 index e48001071a301..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/canvas_winding_rule_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 500, 500); - - BitmapCanvas canvas; - - setUp(() { - canvas = BitmapCanvas(region); - }); - - tearDown(() { - canvas.rootElement.remove(); - }); - - test('draws paths using nonzero and evenodd winding rules', () async { - paintPaths(canvas); - html.document.body.append(canvas.rootElement); - await matchGoldenFile('canvas_path_winding.png', region: region); - }); - -} - -void paintPaths(BitmapCanvas canvas) { - canvas.drawRect(Rect.fromLTRB(0, 0, 500, 500), - SurfacePaintData() - ..color = Color(0xFFFFFFFF) - ..style = PaintingStyle.fill); // white - - SurfacePaint paintFill = SurfacePaint() - ..style = PaintingStyle.fill - ..color = Color(0xFF00B0FF); - SurfacePaint paintStroke = SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2 - ..color = Color(0xFFE00000); - Path path1 = Path() - ..fillType = PathFillType.evenOdd - ..moveTo(50, 0) - ..lineTo(21, 90) - ..lineTo(98, 35) - ..lineTo(2, 35) - ..lineTo(79, 90) - ..close() - ..addRect(Rect.fromLTWH(20, 100, 200, 50)) - ..addRect(Rect.fromLTWH(40, 120, 160, 10)); - Path path2 = Path() - ..fillType = PathFillType.nonZero - ..moveTo(50, 200) - ..lineTo(21, 290) - ..lineTo(98, 235) - ..lineTo(2, 235) - ..lineTo(79, 290) - ..close() - ..addRect(Rect.fromLTWH(20, 300, 200, 50)) - ..addRect(Rect.fromLTWH(40, 320, 160, 10)); - canvas.drawPath(path1, paintFill.paintData); - canvas.drawPath(path2, paintFill.paintData); - canvas.drawPath(path1, paintStroke.paintData); - canvas.drawPath(path2, paintStroke.paintData); -} diff --git a/lib/web_ui/test/golden_tests/engine/color_filter_golden_test.dart b/lib/web_ui/test/golden_tests/engine/color_filter_golden_test.dart deleted file mode 100644 index 97ca81b29426f..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/color_filter_golden_test.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -final Rect region = Rect.fromLTWH(0, 0, 500, 500); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (html.Node scene in html.document.querySelectorAll('flt-scene')) { - scene.remove(); - } - - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('Should apply color filter to image', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Picture backgroundPicture = _drawBackground(); - builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter(EngineColorFilter.mode(Color(0xF0000080), - BlendMode.color)); - final Picture circles1 = _drawTestPictureWithCircles(30, 30); - builder.addPicture(Offset.zero, circles1); - builder.pop(); - html.document.body.append(builder - .build() - .webOnlyRootElement); - - await matchGoldenFile('color_filter_blendMode_color.png', region: region); - }); -} - -Picture _drawTestPictureWithCircles(double offsetX, double offsetY) { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), 10, Paint()..style = PaintingStyle.fill); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)); - canvas.drawCircle( - Offset(offsetX + 10, offsetY + 60), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1)); - canvas.drawCircle( - Offset(offsetX + 60, offsetY + 60), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1)); - return recorder.endRecording(); -} - -Picture _drawBackground() { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); - canvas.drawRect( - Rect.fromLTWH(8, 8, 400.0 - 16, 400.0 - 16), - Paint() - ..style = PaintingStyle.fill - ..color = Color(0xFFE0FFE0) - ); - return recorder.endRecording(); -} diff --git a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart deleted file mode 100644 index 04dce71d0b986..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart +++ /dev/null @@ -1,662 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - -import '../../matchers.dart'; -import 'package:web_engine_tester/golden_tester.dart'; - -final Rect region = Rect.fromLTWH(0, 0, 500, 100); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - setUp(() async { - debugShowClipLayers = true; - SurfaceSceneBuilder.debugForgetFrameScene(); - for (html.Node scene in html.document.querySelectorAll('flt-scene')) { - scene.remove(); - } - - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('pushClipRect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const Rect.fromLTRB(10, 10, 60, 60), - ); - _drawTestPicture(builder); - builder.pop(); - - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_shifted_clip_rect.png', region: region); - }); - - test('pushClipRect with offset and transform', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushOffset(0, 60); - builder.pushTransform( - Matrix4.diagonal3Values(1, -1, 1).toFloat64(), - ); - builder.pushClipRect( - const Rect.fromLTRB(10, 10, 60, 60), - ); - _drawTestPicture(builder); - builder.pop(); - builder.pop(); - builder.pop(); - - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_clip_rect_with_offset_and_transform.png', - region: region); - }); - - test('pushClipRRect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRRect( - RRect.fromLTRBR(10, 10, 60, 60, const Radius.circular(5)), - ); - _drawTestPicture(builder); - builder.pop(); - - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_shifted_clip_rrect.png', region: region); - }); - - test('pushPhysicalShape', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushPhysicalShape( - path: Path()..addRect(const Rect.fromLTRB(10, 10, 60, 60)), - clipBehavior: Clip.hardEdge, - color: const Color.fromRGBO(0, 0, 0, 0.3), - elevation: 0, - ); - _drawTestPicture(builder); - builder.pop(); - - builder.pushOffset(70, 0); - builder.pushPhysicalShape( - path: Path() - ..addRRect(RRect.fromLTRBR(10, 10, 60, 60, const Radius.circular(5))), - clipBehavior: Clip.hardEdge, - color: const Color.fromRGBO(0, 0, 0, 0.3), - elevation: 0, - ); - _drawTestPicture(builder); - builder.pop(); - builder.pop(); - - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_shifted_physical_shape_clip.png', - region: region); - }); - - test('pushImageFilter', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushImageFilter( - ImageFilter.blur(sigmaX: 1, sigmaY: 3), - ); - _drawTestPicture(builder); - builder.pop(); - - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_image_filter.png', region: region); - }); - - group('Cull rect computation', () { - _testCullRectComputation(); - }); -} - -void _testCullRectComputation() { - // Draw a picture larger that screen. Verify that cull rect is equal to screen - // bounds. - test('fills screen bounds', () async { - final SceneBuilder builder = SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - Offset.zero, 10000, Paint()..style = PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(0, 0, 500, 100)); - }, skip: '''TODO(https://github.com/flutter/flutter/issues/40395) - Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''); - - // Draw a picture that overflows the screen. Verify that cull rect is the - // intersection of screen bounds and paint bounds. - test('intersects with screen bounds', () async { - final SceneBuilder builder = SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(Offset.zero, 20, Paint()..style = PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(0, 0, 20, 20)); - }); - - // Draw a picture that's fully outside the screen bounds. Verify the cull rect - // is zero. - test('fully outside screen bounds', () async { - final SceneBuilder builder = SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - const Offset(-100, -100), 20, Paint()..style = PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, Rect.zero); - expect(picture.debugExactGlobalCullRect, Rect.zero); - }); - - // Draw a picture that's fully inside the screen. Verify that cull rect is - // equal to the paint bounds. - test('limits to paint bounds if no clip layers', () async { - final SceneBuilder builder = SceneBuilder(); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - const Offset(50, 50), 10, Paint()..style = PaintingStyle.fill); - }); - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(40, 40, 60, 60)); - }); - - // Draw a picture smaller than the screen. Offset it such that it remains - // fully inside the screen bounds. Verify that cull rect is still just the - // paint bounds. - test('offset does not affect paint bounds', () async { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(10, 10); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - const Offset(50, 50), 10, Paint()..style = PaintingStyle.fill); - }); - builder.pop(); - - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(40, 40, 60, 60)); - }); - - // Draw a picture smaller than the screen. Offset it such that the picture - // overflows screen bounds. Verify that the cull rect is the intersection - // between screen bounds and paint bounds. - test('offset overflows paint bounds', () async { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 90); - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(Offset.zero, 20, Paint()..style = PaintingStyle.fill); - }); - builder.pop(); - - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect( - picture.debugExactGlobalCullRect, const Rect.fromLTRB(0, 70, 20, 100)); - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(0, -20, 20, 10)); - }, skip: '''TODO(https://github.com/flutter/flutter/issues/40395) - Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''); - - // Draw a picture inside a layer clip but fill all available space inside it. - // Verify that the cull rect is equal to the layer clip. - test('fills layer clip rect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const Rect.fromLTWH(10, 10, 60, 60), - ); - - builder.pushClipRect( - const Rect.fromLTWH(40, 40, 60, 60), - ); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - Offset.zero, 10000, Paint()..style = PaintingStyle.fill); - }); - - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_cull_rect_fills_layer_clip.png', - region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(40, 40, 70, 70)); - }); - - // Draw a picture inside a layer clip but position the picture such that its - // paint bounds overflow the layer clip. Verify that the cull rect is the - // intersection between the layer clip and paint bounds. - test('intersects layer clip rect and paint bounds', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const Rect.fromLTWH(10, 10, 60, 60), - ); - - builder.pushClipRect( - const Rect.fromLTWH(40, 40, 60, 60), - ); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - const Offset(80, 55), 30, Paint()..style = PaintingStyle.fill); - }); - - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile( - 'compositing_cull_rect_intersects_clip_and_paint_bounds.png', - region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, const Rect.fromLTRB(50, 40, 70, 70)); - }); - - // Draw a picture inside a layer clip that's positioned inside the clip using - // an offset layer. Verify that the cull rect is the intersection between the - // layer clip and the offset paint bounds. - test('offsets picture inside layer clip rect', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const Rect.fromLTWH(10, 10, 60, 60), - ); - - builder.pushClipRect( - const Rect.fromLTWH(40, 40, 60, 60), - ); - - builder.pushOffset(55, 70); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(Offset.zero, 20, Paint()..style = PaintingStyle.fill); - }); - - builder.pop(); // pushOffset - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_cull_rect_offset_inside_layer_clip.png', - region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, - const Rect.fromLTRB(-15.0, -20.0, 15.0, 0.0)); - }); - - // Draw a picture inside a layer clip that's positioned an offset layer such - // that the picture is push completely outside the clip area. Verify that the - // cull rect is zero. - test('zero intersection with clip', () async { - final SceneBuilder builder = SceneBuilder(); - builder.pushClipRect( - const Rect.fromLTWH(10, 10, 60, 60), - ); - - builder.pushClipRect( - const Rect.fromLTWH(40, 40, 60, 60), - ); - - builder.pushOffset(100, 50); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle(Offset.zero, 20, Paint()..style = PaintingStyle.fill); - }); - - builder.pop(); // pushOffset - builder.pop(); // pushClipRect - builder.pop(); // pushClipRect - - builder.build(); - - final PersistedPicture picture = enumeratePictures().single; - expect(picture.optimalLocalCullRect, Rect.zero); - expect(picture.debugExactGlobalCullRect, Rect.zero); - }); - - // Draw a picture inside a rotated clip. Verify that the cull rect is big - // enough to fit the rotated clip. - test('rotates clip and the picture', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushOffset(80, 50); - - builder.pushTransform( - Matrix4.rotationZ(-math.pi / 4).toFloat64(), - ); - - builder.pushClipRect( - const Rect.fromLTRB(-10, -10, 10, 10), - ); - - builder.pushTransform( - Matrix4.rotationZ(math.pi / 4).toFloat64(), - ); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawPaint(Paint() - ..color = const Color.fromRGBO(0, 0, 255, 0.6) - ..style = PaintingStyle.fill); - canvas.drawRect( - const Rect.fromLTRB(-5, -5, 5, 5), - Paint() - ..color = const Color.fromRGBO(0, 255, 0, 1.0) - ..style = PaintingStyle.fill, - ); - }); - - builder.pop(); // pushTransform - builder.pop(); // pushClipRect - builder.pop(); // pushTransform - builder.pop(); // pushOffset - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_cull_rect_rotated.png', region: region); - - final PersistedPicture picture = enumeratePictures().single; - expect( - picture.optimalLocalCullRect, - within( - distance: 0.05, from: const Rect.fromLTRB(-14.1, -14.1, 14.1, 14.1)), - ); - }); - - test('pushClipPath', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - final Path path = Path(); - path..addRect(const Rect.fromLTRB(10, 10, 60, 60)); - builder.pushClipPath( - path, - ); - _drawTestPicture(builder); - builder.pop(); - - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_clip_path.png', region: region); - }); - - // Draw a picture inside a rotated clip. Verify that the cull rect is big - // enough to fit the rotated clip. - test('clips correctly when using 3d transforms', () async { - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushTransform(Matrix4.diagonal3Values( - EngineWindow.browserDevicePixelRatio, - EngineWindow.browserDevicePixelRatio, 1.0).toFloat64()); - - // TODO(yjbanov): see the TODO below. - // final double screenWidth = html.window.innerWidth.toDouble(); - // final double screenHeight = html.window.innerHeight.toDouble(); - - final Matrix4 scaleTransform = Matrix4.identity().scaled(0.5, 0.2); - builder.pushTransform( - scaleTransform.toFloat64(), - ); - - builder.pushOffset(400, 200); - - builder.pushClipRect( - const Rect.fromLTRB(-200, -200, 200, 200), - ); - - builder.pushTransform( - Matrix4.rotationY(45.0 * math.pi / 180.0).toFloat64() - ); - - builder.pushClipRect( - const Rect.fromLTRB(-140, -140, 140, 140), - ); - - builder.pushTransform(Matrix4.translationValues(0, 0, -50).toFloat64()); - - drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawPaint(Paint() - ..color = const Color.fromRGBO(0, 0, 255, 0.6) - ..style = PaintingStyle.fill); - // Rect will be clipped. - canvas.drawRect( - const Rect.fromLTRB(-150, -150, 150, 150), - Paint() - ..color = const Color.fromRGBO(0, 255, 0, 1.0) - ..style = PaintingStyle.fill, - ); - // Should be outside the clip range. - canvas.drawRect( - const Rect.fromLTRB(-150, -150, -140, -140), - Paint() - ..color = const Color.fromARGB(0xE0, 255, 0, 0) - ..style = PaintingStyle.fill, - ); - canvas.drawRect( - const Rect.fromLTRB(140, -150, 150, -140), - Paint() - ..color = const Color.fromARGB(0xE0, 255, 0, 0) - ..style = PaintingStyle.fill, - ); - canvas.drawRect( - const Rect.fromLTRB(-150, 140, -140, 150), - Paint() - ..color = const Color.fromARGB(0xE0, 255, 0, 0) - ..style = PaintingStyle.fill, - ); - canvas.drawRect( - const Rect.fromLTRB(140, 140, 150, 150), - Paint() - ..color = const Color.fromARGB(0xE0, 255, 0, 0) - ..style = PaintingStyle.fill, - ); - // Should be inside clip range - canvas.drawRect( - const Rect.fromLTRB(-100, -100, -90, -90), - Paint() - ..color = const Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = PaintingStyle.fill, - ); - canvas.drawRect( - const Rect.fromLTRB(90, -100, 100, -90), - Paint() - ..color = const Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = PaintingStyle.fill, - ); - canvas.drawRect( - const Rect.fromLTRB(-100, 90, -90, 100), - Paint() - ..color = const Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = PaintingStyle.fill, - ); - canvas.drawRect( - const Rect.fromLTRB(90, 90, 100, 100), - Paint() - ..color = const Color.fromARGB(0xE0, 0, 0, 0x80) - ..style = PaintingStyle.fill, - ); - }); - - builder.pop(); // pushTransform Z-50 - builder.pop(); // pushClipRect - builder.pop(); // pushTransform 3D rotate - builder.pop(); // pushClipRect - builder.pop(); // pushOffset - builder.pop(); // pushTransform scale - builder.pop(); // pushTransform scale devicepixelratio - html.document.body.append(builder.build().webOnlyRootElement); - - await matchGoldenFile('compositing_3d_rotate1.png', region: region); - - // ignore: unused_local_variable - final PersistedPicture picture = enumeratePictures().single; - // TODO(https://github.com/flutter/flutter/issues/40395): - // Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500. - // expect( - // picture.optimalLocalCullRect, - // within( - // distance: 0.05, - // from: Rect.fromLTRB( - // -140, -140, screenWidth - 360.0, screenHeight + 40.0)), - // ); - }); - - // This test reproduces text blurriness when two pieces of text appear inside - // two nested clips: - // - // ┌───────────────────────┐ - // │ text in outer clip │ - // │ ┌────────────────────┐│ - // │ │ text in inner clip ││ - // │ └────────────────────┘│ - // └───────────────────────┘ - // - // This test clips using layers. See a similar test in `canvas_golden_test.dart`, - // which clips using canvas. - // - // More details: https://github.com/flutter/flutter/issues/32274 - test( - 'renders clipped text with high quality', - () async { - // To reproduce blurriness we need real clipping. - debugShowClipLayers = false; - final Paragraph paragraph = - (ParagraphBuilder(ParagraphStyle(fontFamily: 'Roboto'))..addText('Am I blurry?')).build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - - final Rect canvasSize = Rect.fromLTRB( - 0, - 0, - paragraph.maxIntrinsicWidth + 16, - 2 * paragraph.height + 32, - ); - final Rect outerClip = - Rect.fromLTRB(0.5, 0.5, canvasSize.right, canvasSize.bottom); - final Rect innerClip = Rect.fromLTRB(0.5, canvasSize.bottom / 2 + 0.5, - canvasSize.right, canvasSize.bottom); - final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - - builder.pushClipRect(outerClip); - - { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(outerClip); - canvas.drawParagraph(paragraph, const Offset(8.5, 8.5)); - final Picture picture = recorder.endRecording(); - expect(canvas.hasArbitraryPaint, false); - - builder.addPicture( - Offset.zero, - picture, - ); - } - - builder.pushClipRect(innerClip); - { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(innerClip); - canvas.drawParagraph(paragraph, Offset(8.5, 8.5 + innerClip.top)); - final Picture picture = recorder.endRecording(); - expect(canvas.hasArbitraryPaint, false); - - builder.addPicture( - Offset.zero, - picture, - ); - } - builder.pop(); // inner clip - builder.pop(); // outer clip - - final html.Element sceneElement = builder.build().webOnlyRootElement; - expect( - sceneElement.querySelectorAll('p').map((e) => e.innerText).toList(), - ['Am I blurry?', 'Am I blurry?'], - reason: 'Expected to render text using HTML', - ); - html.document.body.append(sceneElement); - - await matchGoldenFile( - 'compositing_draw_high_quality_text.png', - region: canvasSize, - maxDiffRatePercent: 0.0, - pixelComparison: PixelComparison.precise, - ); - }, - testOn: 'chrome', - ); -} - -void _drawTestPicture(SceneBuilder builder) { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); - canvas.drawCircle( - const Offset(10, 10), 10, Paint()..style = PaintingStyle.fill); - canvas.drawCircle( - const Offset(60, 10), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)); - canvas.drawCircle( - const Offset(10, 60), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 255, 0, 1)); - canvas.drawCircle( - const Offset(60, 60), - 10, - Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(0, 0, 255, 1)); - final Picture picture = recorder.endRecording(); - - builder.addPicture( - Offset.zero, - picture, - ); -} - -typedef PaintCallback = void Function(RecordingCanvas canvas); - -void drawWithBitmapCanvas(SceneBuilder builder, PaintCallback callback, - {Rect bounds = Rect.largest}) { - final EnginePictureRecorder recorder = PictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(bounds); - - canvas.debugEnforceArbitraryPaint(); - callback(canvas); - final Picture picture = recorder.endRecording(); - - builder.addPicture( - Offset.zero, - picture, - ); -} 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 deleted file mode 100644 index 68a2136343791..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:typed_data'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500), - bool write = false}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile( - '$fileName.png', - region: region, - write: write, - maxDiffRatePercent: 0.0, - ); - } finally { - // The page is reused across tests, so remove the element after taking the - // golden screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - disposeWebGl(); - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - Future _testVertices(String fileName, Vertices vertices, - BlendMode blendMode, - Paint paint) async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - rc.drawVertices(vertices, blendMode, paint); - await _checkScreenshot(rc, fileName); - } - - test('Should draw green hairline triangles when colors array is null.', - () async { - final Vertices vertices = Vertices.raw(VertexMode.triangles, - Float32List.fromList([ - 20.0, 20.0, 220.0, 10.0, 110.0, 220.0, - 220.0, 320.0, 20.0, 310.0, 200.0, 420.0 - ])); - await _testVertices( - 'draw_vertices_hairline_triangle', - vertices, - BlendMode.srcOver, - Paint()..color = Color.fromARGB(255, 0, 128, 0)); - }); - - test('Should draw black hairline triangles when colors array is null' - ' and Paint() has no color.', - () async { - // ignore: unused_local_variable - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF]); - final Vertices vertices = Vertices.raw(VertexMode.triangles, - Float32List.fromList([ - 20.0, 20.0, 220.0, 10.0, 110.0, 220.0, - 220.0, 320.0, 20.0, 310.0, 200.0, 420.0 - ])); - await _testVertices( - 'draw_vertices_hairline_triangle_black', - vertices, - BlendMode.srcOver, - Paint()); - }); - - test('Should draw hairline triangleFan.', - () async { - final Vertices vertices = Vertices.raw(VertexMode.triangleFan, - Float32List.fromList([ - 150.0, 150.0, 20.0, 10.0, 80.0, 20.0, - 220.0, 15.0, 280.0, 30.0, 300.0, 420.0 - ])); - - await _testVertices( - 'draw_vertices_hairline_triangle_fan', - vertices, - BlendMode.srcOver, - Paint()..color = Color.fromARGB(255, 0, 128, 0)); - }); - - test('Should draw hairline triangleStrip.', - () async { - final Vertices vertices = Vertices.raw(VertexMode.triangleStrip, - Float32List.fromList([ - 20.0, 20.0, 220.0, 10.0, 110.0, 220.0, - 220.0, 320.0, 20.0, 310.0, 200.0, 420.0 - ])); - await _testVertices( - 'draw_vertices_hairline_triangle_strip', - vertices, - BlendMode.srcOver, - Paint()..color = Color.fromARGB(255, 0, 128, 0)); - }); - - test('Should draw triangles with colors.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF]); - final Vertices vertices = Vertices.raw(VertexMode.triangles, - Float32List.fromList([ - 150.0, 150.0, 20.0, 10.0, 80.0, 20.0, - 220.0, 15.0, 280.0, 30.0, 300.0, 420.0 - ]), colors: colors); - - await _testVertices( - 'draw_vertices_triangles', - vertices, - BlendMode.srcOver, - Paint()..color = Color.fromARGB(255, 0, 128, 0)); - }); - - test('Should draw triangleFan with colors.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF]); - final Vertices vertices = Vertices.raw(VertexMode.triangleFan, - Float32List.fromList([ - 150.0, 150.0, 20.0, 10.0, 80.0, 20.0, - 220.0, 15.0, 280.0, 30.0, 300.0, 420.0 - ]), colors: colors); - - await _testVertices( - 'draw_vertices_triangle_fan', - vertices, - BlendMode.srcOver, - Paint()..color = Color.fromARGB(255, 0, 128, 0)); - }); - - test('Should draw triangleStrip with colors.', - () async { - final Int32List colors = Int32List.fromList([ - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, - 0xFFFF0000, 0xFF00FF00, 0xFF0000FF]); - final Vertices vertices = Vertices.raw(VertexMode.triangleStrip, - Float32List.fromList([ - 20.0, 20.0, 220.0, 10.0, 110.0, 220.0, - 220.0, 320.0, 20.0, 310.0, 200.0, 420.0 - ]), colors: colors); - await _testVertices( - 'draw_vertices_triangle_strip', - vertices, - BlendMode.srcOver, - Paint()..color = Color.fromARGB(255, 0, 128, 0)); - }); -} 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 deleted file mode 100644 index 6eccec76d3234..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // golden screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('Should draw linear gradient using rectangle.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300); - final Paint paint = Paint()..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - [Color(0xFFcfdfd2), Color(0xFF042a85)]); - rc.drawRect(shaderRect, paint); - expect(rc.hasArbitraryPaint, isTrue); - await _checkScreenshot(rc, 'linear_gradient_rect'); - }); - - test('Should draw linear gradient with transform.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - List angles = [0.0, 90.0, 180.0]; - double yOffset = 0; - for (double angle in angles) { - final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); - final Paint paint = Paint() - ..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - [Color(0xFFFF0000), Color(0xFF042a85)], - null, - TileMode.clamp, - Matrix4 - .rotationZ((angle / 180) * math.pi) - .toFloat64()); - rc.drawRect(shaderRect, Paint() - ..color = Color(0xFF000000)); - rc.drawOval(shaderRect, paint); - yOffset += 120; - } - expect(rc.hasArbitraryPaint, isTrue); - await _checkScreenshot(rc, 'linear_gradient_oval_matrix'); - }); - - // Regression test for https://github.com/flutter/flutter/issues/50010 - test('Should draw linear gradient using rounded rect.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300); - final Paint paint = Paint()..shader = Gradient.linear( - Offset(shaderRect.left, shaderRect.top), - Offset(shaderRect.right, shaderRect.bottom), - [Color(0xFFcfdfd2), Color(0xFF042a85)]); - rc.drawRRect(RRect.fromRectAndRadius(shaderRect, Radius.circular(16)), paint); - expect(rc.hasArbitraryPaint, isTrue); - await _checkScreenshot(rc, 'linear_gradient_rounded_rect'); - }); -} 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 deleted file mode 100644 index eef5941349df9..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/multiline_text_clipping_golden_test.dart +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'scuba.dart'; - -typedef PaintTest = void Function(RecordingCanvas recordingCanvas); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - // Scuba doesn't give us viewport smaller than 472px wide. - final EngineScubaTester scuba = await EngineScubaTester.initialize( - viewportSize: const Size(600, 600), - ); - - setUpStableTestFonts(); - - void paintTest(EngineCanvas canvas, PaintTest painter) { - final Rect screenRect = const Rect.fromLTWH(0, 0, 600, 600); - final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); - painter(recordingCanvas); - recordingCanvas.endRecording(); - recordingCanvas.apply(canvas, screenRect); - } - - testEachCanvas( - 'clips multiline text against rectangle', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipRect); - return scuba.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_rect'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against rectangle with transform', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipRectTranslated); - return scuba.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_rect_translate'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against round rectangle', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipRoundRect); - return scuba.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_roundrect'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against path', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipPath); - return scuba.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_path'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against stack of rects', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - // TODO(flutter_web): https://github.com/flutter/flutter/issues/35086 - // This produces the wrong result when using [BitmapCanvas] but without - // the new experimental canvas mode. - paintTest(canvas, paintTextWithClipStack); - return scuba.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_stack1'); - } - return null; - }, - ); -} - -const Rect testBounds = Rect.fromLTRB(50, 50, 230, 220); - -void drawBackground(RecordingCanvas canvas) { - canvas.drawRect( - testBounds, - Paint() - ..style = PaintingStyle.fill - ..color = const Color(0xFF9E9E9E)); - canvas.drawRect( - testBounds.inflate(-40), - Paint() - ..strokeWidth = 1 - ..style = PaintingStyle.stroke - ..color = const Color(0xFF009688)); -} - -void drawQuickBrownFox(RecordingCanvas canvas) { - canvas.drawParagraph( - paragraph( - 'The quick brown fox jumps over the lazy dog', - textStyle: TextStyle( - color: const Color(0xFF000000), - decoration: TextDecoration.none, - fontFamily: 'Roboto', - fontSize: 30, - background: Paint()..color = const Color.fromRGBO(50, 255, 50, 1.0), - ), - maxWidth: 180, - ), - Offset(testBounds.left, testBounds.top)); -} - -void paintTextWithClipRect(RecordingCanvas canvas) { - drawBackground(canvas); - canvas.clipRect(testBounds.inflate(-40), ClipOp.intersect); - drawQuickBrownFox(canvas); -} - -void paintTextWithClipRectTranslated(RecordingCanvas canvas) { - drawBackground(canvas); - canvas.clipRect(testBounds.inflate(-40), ClipOp.intersect); - canvas.translate(30, 10); - drawQuickBrownFox(canvas); -} - -const Color deepOrange = Color(0xFFFF5722); - -void paintTextWithClipRoundRect(RecordingCanvas canvas) { - final RRect roundRect = RRect.fromRectAndCorners(testBounds.inflate(-40), - topLeft: Radius.zero, - topRight: const Radius.elliptical(45, 40), - bottomLeft: const Radius.elliptical(50, 40), - bottomRight: const Radius.circular(30)); - drawBackground(canvas); - canvas.drawRRect( - roundRect, - Paint() - ..color = deepOrange - ..style = PaintingStyle.fill); - canvas.clipRRect(roundRect); - drawQuickBrownFox(canvas); -} - -void paintTextWithClipPath(RecordingCanvas canvas) { - drawBackground(canvas); - final Path path = Path(); - const double delta = 40.0; - final Rect clipBounds = testBounds.inflate(-delta); - final double midX = (clipBounds.left + clipBounds.right) / 2.0; - final double midY = (clipBounds.top + clipBounds.bottom) / 2.0; - path.moveTo(clipBounds.left - delta, midY); - path.quadraticBezierTo(midX, midY, midX, clipBounds.top - delta); - path.quadraticBezierTo(midX, midY, clipBounds.right + delta, midY); - path.quadraticBezierTo(midX, midY, midX, clipBounds.bottom + delta); - path.quadraticBezierTo(midX, midY, clipBounds.left - delta, midY); - path.close(); - canvas.drawPath( - path, - Paint() - ..color = deepOrange - ..style = PaintingStyle.fill); - canvas.clipPath(path); - drawQuickBrownFox(canvas); -} - -void paintTextWithClipStack(RecordingCanvas canvas) { - drawBackground(canvas); - final Rect inflatedRect = testBounds.inflate(-40); - canvas.clipRect(inflatedRect, ClipOp.intersect); - canvas.rotate(math.pi / 8.0); - canvas.translate(40, -40); - canvas.clipRect(inflatedRect, ClipOp.intersect); - canvas.drawRect( - inflatedRect, - Paint() - ..color = deepOrange - ..style = PaintingStyle.fill); - drawQuickBrownFox(canvas); -} 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 deleted file mode 100644 index 380036ba18dde..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; -import '../../matchers.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - const Color black12Color = Color(0x1F000000); - const Color redAccentColor = Color(0xFFFF1744); - const double kDashLength = 5.0; - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('Should calculate tangent on line', () async { - final Path path = Path(); - path.moveTo(50, 130); - path.lineTo(150, 20); - - PathMetric metric = path.computeMetrics().first; - Tangent t = metric.getTangentForOffset(50.0); - expect(t.position.dx, within(from: 83.633, distance: 0.01)); - expect(t.position.dy, within(from: 93.0, distance: 0.01)); - expect(t.vector.dx, within(from: 0.672, distance: 0.01)); - expect(t.vector.dy, within(from: -0.739, distance: 0.01)); - }); - - test('Should calculate tangent on cubic curve', () async { - final Path path = Path(); - double p1x = 240; - double p1y = 120; - double p2x = 320; - double p2y = 25; - path.moveTo(150, 20); - path.quadraticBezierTo(p1x, p1y, p2x, p2y); - PathMetric metric = path.computeMetrics().first; - Tangent t = metric.getTangentForOffset(50.0); - expect(t.position.dx, within(from: 187.25, distance: 0.01)); - expect(t.position.dy, within(from: 53.33, distance: 0.01)); - expect(t.vector.dx, within(from: 0.82, distance: 0.01)); - expect(t.vector.dy, within(from: 0.56, distance: 0.01)); - }); - - test('Should calculate tangent on quadratic curve', () async { - final Path path = Path(); - double p0x = 150; - double p0y = 20; - double p1x = 320; - double p1y = 25; - path.moveTo(150, 20); - path.quadraticBezierTo(p0x, p0y, p1x, p1y); - PathMetric metric = path.computeMetrics().first; - Tangent t = metric.getTangentForOffset(50.0); - expect(t.position.dx, within(from: 199.82, distance: 0.01)); - expect(t.position.dy, within(from: 21.46, distance: 0.01)); - expect(t.vector.dx, within(from: 0.99, distance: 0.01)); - expect(t.vector.dy, within(from: 0.02, distance: 0.01)); - }); - - // Test for extractPath to draw 5 pixel length dashed line using quad curve. - test('Should draw dashed line on quadratic curve.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Paint paint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 3 - ..color = black12Color; - final Paint redPaint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = redAccentColor; - - final Path path = Path(); - path.moveTo(50, 130); - path.lineTo(150, 20); - double p1x = 240; - double p1y = 120; - double p2x = 320; - double p2y = 25; - path.quadraticBezierTo(p1x, p1y, p2x, p2y); - - rc.drawPath(path, paint); - - double t0 = 0.2; - double t1 = 0.7; - - List metrics = path.computeMetrics().toList(); - double totalLength = 0; - for (PathMetric m in metrics) { - totalLength += m.length; - } - Path dashedPath = Path(); - for (final PathMetric measurePath in path.computeMetrics()) { - double distance = totalLength * t0; - bool draw = true; - while (distance < measurePath.length * t1) { - final double length = kDashLength; - if (draw) { - dashedPath.addPath( - measurePath.extractPath(distance, distance + length), - Offset.zero); - } - distance += length; - draw = !draw; - } - } - rc.drawPath(dashedPath, redPaint); - await _checkScreenshot(rc, 'path_dash_quadratic'); - }); - - // Test for extractPath to draw 5 pixel length dashed line using cubic curve. - test('Should draw dashed line on cubic curve.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Paint paint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 3 - ..color = black12Color; - final Paint redPaint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..color = redAccentColor; - - final Path path = Path(); - path.moveTo(50, 130); - path.lineTo(150, 20); - double p1x = 40; - double p1y = 120; - double p2x = 300; - double p2y = 130; - double p3x = 320; - double p3y = 25; - path.cubicTo(p1x, p1y, p2x, p2y, p3x, p3y); - - rc.drawPath(path, paint); - - double t0 = 0.2; - double t1 = 0.7; - - List metrics = path.computeMetrics().toList(); - double totalLength = 0; - for (PathMetric m in metrics) { - totalLength += m.length; - } - Path dashedPath = Path(); - for (final PathMetric measurePath in path.computeMetrics()) { - double distance = totalLength * t0; - bool draw = true; - while (distance < measurePath.length * t1) { - final double length = kDashLength; - if (draw) { - dashedPath.addPath( - measurePath.extractPath(distance, distance + length), - Offset.zero); - } - distance += length; - draw = !draw; - } - } - rc.drawPath(dashedPath, redPaint); - await _checkScreenshot(rc, 'path_dash_cubic'); - }); -} 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 deleted file mode 100644 index 03d9f0c1ad2e7..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/path_to_svg_golden_test.dart +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final Rect region = Rect.fromLTWH(8, 8, 600, 800); // Compensate for old scuba tester padding - - Future testPath(Path path, String scubaFileName, {Paint paint, double maxDiffRatePercent = null}) async { - const Rect canvasBounds = Rect.fromLTWH(0, 0, 600, 800); - final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds); - final RecordingCanvas canvas = RecordingCanvas(canvasBounds); - - paint ??= Paint() - ..color = const Color(0x807F7F7F) - ..style = PaintingStyle.fill; - - canvas.drawPath(path, paint); - - paint = Paint() - ..strokeWidth = 2 - ..color = const Color(0xFFFF0000) - ..style = PaintingStyle.stroke; - - canvas.drawPath(path, paint); - - final html.Element svgElement = pathToSvgElement(path, paint); - - html.document.body.append(bitmapCanvas.rootElement); - html.document.body.append(svgElement); - - canvas.endRecording(); - canvas.apply(bitmapCanvas, canvasBounds); - - await matchGoldenFile('$scubaFileName.png', region: region, maxDiffRatePercent: maxDiffRatePercent); - - bitmapCanvas.rootElement.remove(); - svgElement.remove(); - } - - tearDown(() { - html.document.body.children.clear(); - }); - - test('render line strokes', () async { - final Path path = Path(); - path.moveTo(50, 60); - path.lineTo(200, 300); - await testPath(path, 'svg_stroke_line', - paint: Paint() - ..color = const Color(0xFFFF0000) - ..strokeWidth = 2.0 - ..style = PaintingStyle.stroke); - }); - - test('render quad bezier curve', () async { - final Path path = Path(); - path.moveTo(50, 60); - path.quadraticBezierTo(200, 60, 50, 200); - await testPath(path, 'svg_quad_bezier'); - }); - - test('render cubic curve', () async { - final Path path = Path(); - path.moveTo(50, 60); - path.cubicTo(200, 60, -100, -50, 150, 200); - await testPath(path, 'svg_cubic_bezier'); - }); - - test('render arcs', () async { - final List arcs = [ - ArcSample(const Offset(0, 0), - largeArc: false, clockwise: false, distance: 20), - ArcSample(const Offset(200, 0), - largeArc: true, clockwise: false, distance: 20), - ArcSample(const Offset(0, 150), - largeArc: false, clockwise: true, distance: 20), - ArcSample(const Offset(200, 150), - largeArc: true, clockwise: true, distance: 20), - ArcSample(const Offset(0, 300), - largeArc: false, clockwise: false, distance: -20), - ArcSample(const Offset(200, 300), - largeArc: true, clockwise: false, distance: -20), - ArcSample(const Offset(0, 450), - largeArc: false, clockwise: true, distance: -20), - ArcSample(const Offset(200, 450), - largeArc: true, clockwise: true, distance: -20) - ]; - int sampleIndex = 0; - for (ArcSample sample in arcs) { - ++sampleIndex; - final Path path = sample.createPath(); - await testPath(path, 'svg_arc_$sampleIndex'); - } - }); - - test('render rect', () async { - final Path path = Path(); - path.addRect(const Rect.fromLTRB(15, 15, 60, 20)); - path.addRect(const Rect.fromLTRB(35, 160, 15, 100)); - await testPath(path, 'svg_rect', maxDiffRatePercent: 1.0); - }); - - test('render notch', () async { - final Path path = Path(); - path.moveTo(0, 0); - path.lineTo(83, 0); - path.quadraticBezierTo(98, 0, 99.97, 7.8); - path.arcToPoint(const Offset(162, 7.8), - radius: const Radius.circular(32), - largeArc: false, - clockwise: false, - rotation: 0); - path.lineTo(200, 7.8); - path.lineTo(200, 80); - path.lineTo(0, 80); - path.lineTo(0, 10); - await testPath(path, 'svg_notch'); - }); -} - -html.Element pathToSvgElement(Path path, Paint paint) { - final Rect bounds = path.getBounds(); - final StringBuffer sb = StringBuffer(); - sb.write( - ''); - sb.write(''); - sb.write(''); - final html.Element svgElement = - html.Element.html(sb.toString(), treeSanitizer: _NullTreeSanitizer()); - svgElement.style.transform = 'translate(200px, 0px)'; - return svgElement; -} - -class _NullTreeSanitizer implements html.NodeTreeSanitizer { - @override - void sanitizeTree(html.Node node) {} -} - -class ArcSample { - final Offset offset; - final bool largeArc; - final bool clockwise; - final double distance; - ArcSample(this.offset, - {this.largeArc = false, this.clockwise = false, this.distance = 0}); - - Path createPath() { - final Offset startP = - Offset(75 - distance + offset.dx, 75 - distance + offset.dy); - final Offset endP = - Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); - final Path path = Path(); - path.moveTo(startP.dx, startP.dy); - path.arcToPoint(endP, - rotation: 60, - radius: const Radius.elliptical(40, 60), - largeArc: largeArc, - clockwise: clockwise); - return path; - } - - // Returns bounds of start/end point of arc. - Rect getBounds() { - final Offset startP = - Offset(75 - distance + offset.dx, 75 - distance + offset.dy); - final Offset endP = - Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); - return Rect.fromLTRB(startP.dx, startP.dy, endP.dx, endP.dy); - } -} 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 deleted file mode 100644 index 0f968ba72b056..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/path_transform_test.dart +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; -import 'dart:math' as math; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500), - double maxDiffRatePercent = null}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: maxDiffRatePercent); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - test('Should draw transformed line.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Path path = Path(); - path.moveTo(0, 0); - path.lineTo(300, 200); - rc.drawPath( - path, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = - Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, 20); - transformedPath.addPath(path, Offset.zero, - matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0)); - await _checkScreenshot(rc, 'path_transform_with_line'); - }); - - test('Should draw transformed line.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Path path = Path(); - path.addRect(Rect.fromLTRB(50, 40, 300, 100)); - rc.drawPath( - path, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = - Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, 20); - transformedPath.addPath(path, Offset.zero, - matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0)); - await _checkScreenshot(rc, 'path_transform_with_rect'); - }); - - test('Should draw transformed quadratic curve.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Path path = Path(); - path.moveTo(100, 100); - path.quadraticBezierTo(100, 300, 400, 300); - rc.drawPath( - path, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = - Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, -80); - transformedPath.addPath(path, Offset.zero, - matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0)); - await _checkScreenshot(rc, 'path_transform_with_quadratic_curve'); - }); - - test('Should draw transformed conic.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - const double yStart = 20; - - const Offset p0 = Offset(25, yStart + 25); - const Offset pc = Offset(60, yStart + 150); - const Offset p2 = Offset(100, yStart + 50); - - final Path path = Path(); - path.moveTo(p0.dx, p0.dy); - path.conicTo(pc.dx, pc.dy, p2.dx, p2.dy, 0.5); - path.close(); - path.moveTo(p0.dx, p0.dy + 100); - path.conicTo(pc.dx, pc.dy + 100, p2.dx, p2.dy + 100, 10); - path.close(); - - rc.drawPath( - path, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = - Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, -80); - transformedPath.addPath(path, Offset.zero, - matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0)); - await _checkScreenshot(rc, 'path_transform_with_conic'); - }); - - test('Should draw transformed arc.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Path path = Path(); - path.moveTo(350, 280); - path.arcToPoint(Offset(450, 90), - radius: Radius.elliptical(200, 50), - rotation: -math.pi / 6.0, - largeArc: true, - clockwise: true); - path.close(); - - rc.drawPath( - path, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); - - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = - Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, 10); - transformedPath.addPath(path, Offset.zero, - matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0)); - await _checkScreenshot(rc, 'path_transform_with_arc', - maxDiffRatePercent: 1.4); - }); - - test('Should draw transformed rrect.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - - final Path path = Path(); - path.addRRect(RRect.fromLTRBR(50, 50, 300, 200, Radius.elliptical(4, 8))); - - rc.drawPath( - path, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); - - final Path transformedPath = Path(); - final Matrix4 testMatrixTranslateRotate = - Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, -80); - transformedPath.addPath(path, Offset.zero, - matrix4: testMatrixTranslateRotate.toFloat64()); - rc.drawPath( - transformedPath, - Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color.fromRGBO(0, 128, 255, 1.0)); - await _checkScreenshot(rc, 'path_transform_with_rrect'); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart b/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart deleted file mode 100644 index 1c61fff196a09..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/picture_golden_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() { - group('Picture', () { - test('toImage produces an image', () async { - final EnginePictureRecorder recorder = ui.PictureRecorder(); - final RecordingCanvas canvas = recorder.beginRecording(ui.Rect.fromLTRB(0, 0, 200, 100)); - canvas.drawCircle( - const ui.Offset(100, 50), - 40, - ui.Paint() - ..color = ui.Color.fromARGB(255, 255, 100, 100), - ); - final ui.Picture picture = recorder.endRecording(); - final HtmlImage image = await picture.toImage(200, 100); - expect(image, isNotNull); - html.document.body - ..style.margin = '0' - ..append(image.imgElement); - try { - await matchGoldenFile( - 'picture_to_image.png', - region: ui.Rect.fromLTRB(0, 0, 200, 100), - ); - } finally { - image.imgElement.remove(); - } - }); - }); -} 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 deleted file mode 100644 index 098233fe1cfb3..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/radial_gradient_golden_test.dart +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:html' as html; - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; -import 'package:ui/src/engine.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - const double screenWidth = 600.0; - const double screenHeight = 800.0; - const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - - // Commit a recording canvas to a bitmap, and compare with the expected - Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500), - bool write = false}) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.endRecording(); - rc.apply(engineCanvas, screenRect); - - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, write: write); - } finally { - // The page is reused across tests, so remove the element after taking the - // golden screenshot. - sceneElement.remove(); - } - } - - setUp(() async { - debugEmulateFlutterTesterEnvironment = true; - await webOnlyInitializePlatform(); - webOnlyFontCollection.debugRegisterTestFonts(); - await webOnlyFontCollection.ensureFontsLoaded(); - }); - - Future _testGradient(String fileName, Shader shader, - {Rect paintRect = const Rect.fromLTRB(50, 50, 300, 300), - Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300)}) async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); - final Paint paint = Paint()..shader = shader; - final Path path = Path(); - path.addRect(paintRect); - rc.drawPath(path, paint); - await _checkScreenshot(rc, fileName); - } - - test('Should draw centered radial gradient.', () async { - Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300); - await _testGradient( - 'radial_gradient_centered', - Gradient.radial( - Offset((shaderRect.left + shaderRect.right) / 2, - (shaderRect.top + shaderRect.bottom) / 2), - shaderRect.width / 2, - [ - const Color.fromARGB(255, 0, 0, 0), - const Color.fromARGB(255, 0, 0, 255) - ]), - shaderRect: shaderRect); - }); - - test('Should draw right bottom centered radial gradient.', () async { - Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300); - await _testGradient( - 'radial_gradient_right_bottom', - Gradient.radial( - Offset(shaderRect.right, shaderRect.bottom), shaderRect.width / 2, [ - const Color.fromARGB(255, 0, 0, 0), - const Color.fromARGB(255, 0, 0, 255) - ]), - shaderRect: shaderRect); - }); - - test('Should draw with radial gradient with TileMode.clamp.', () async { - Rect shaderRect = const Rect.fromLTRB(50, 50, 100, 100); - await _testGradient( - 'radial_gradient_tilemode_clamp', - Gradient.radial( - Offset((shaderRect.left + shaderRect.right) / 2, - (shaderRect.top + shaderRect.bottom) / 2), - shaderRect.width / 2, - [ - const Color.fromARGB(255, 0, 0, 0), - const Color.fromARGB(255, 0, 0, 255) - ], - [0.0, 1.0], - TileMode.clamp), - shaderRect: shaderRect); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/scuba.dart b/lib/web_ui/test/golden_tests/engine/scuba.dart deleted file mode 100644 index 53a0f49aa3650..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/scuba.dart +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:async'; -import 'dart:html' as html; - -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' as ui; -import 'package:test/test.dart'; - -import 'package:web_engine_tester/golden_tester.dart'; - -/// Class that controls some details of how screenshotting is made. -/// -/// (For Googlers: Not really related with internal Scuba anymore) -class EngineScubaTester { - /// The size of the browser window used in this scuba test. - final ui.Size viewportSize; - - EngineScubaTester(this.viewportSize); - - static Future initialize( - {ui.Size viewportSize = const ui.Size(2400, 1800)}) async { - assert(viewportSize != null); - - assert(() { - if (viewportSize.width.ceil() != viewportSize.width || - viewportSize.height.ceil() != viewportSize.height) { - throw Exception( - 'Scuba only supports integer screen sizes, but found: $viewportSize'); - } - if (viewportSize.width < 472) { - throw Exception('Scuba does not support screen width smaller than 472'); - } - return true; - }()); - - return EngineScubaTester(viewportSize); - } - - ui.Rect get viewportRegion => - ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height); - - Future diffScreenshot( - String fileName, { - ui.Rect region, - double maxDiffRatePercent, - bool write = false, - }) async { - await matchGoldenFile( - '$fileName.png', - region: region ?? viewportRegion, - maxDiffRatePercent: maxDiffRatePercent, - write: write, - ); - } - - /// Prepares the DOM and inserts all the necessary nodes, then invokes scuba's - /// screenshot diffing. - /// - /// It also cleans up the DOM after itself. - Future diffCanvasScreenshot( - EngineCanvas canvas, - String fileName, { - ui.Rect region, - double maxDiffRatePercent, - bool write = false, - }) async { - // Wrap in so that our CSS selectors kick in. - final html.Element sceneElement = html.Element.tag('flt-scene'); - try { - sceneElement.append(canvas.rootElement); - html.document.body.append(sceneElement); - String screenshotName = '${fileName}_${canvas.runtimeType}'; - if (WebExperiments.instance.useCanvasText) { - screenshotName += '+canvas_measurement'; - } - await diffScreenshot( - screenshotName, - region: region, - maxDiffRatePercent: maxDiffRatePercent, - write: write, - ); - } finally { - // The page is reused across tests, so remove the element after taking the - // Scuba screenshot. - sceneElement.remove(); - } - } -} - -typedef CanvasTest = FutureOr Function(EngineCanvas canvas); - -/// Runs the given test [body] with each type of canvas. -void testEachCanvas(String description, CanvasTest body, - {double maxDiffRate}) { - const ui.Rect bounds = ui.Rect.fromLTWH(0, 0, 600, 800); - test('$description (bitmap)', () { - try { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - WebExperiments.instance.useCanvasText = false; - return body(BitmapCanvas(bounds)); - } finally { - WebExperiments.instance.useCanvasText = null; - TextMeasurementService.clearCache(); - } - }); - test('$description (bitmap + canvas measurement)', () async { - try { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - WebExperiments.instance.useCanvasText = true; - await body(BitmapCanvas(bounds)); - } finally { - WebExperiments.instance.useCanvasText = null; - TextMeasurementService.clearCache(); - } - }); - test('$description (dom)', () { - try { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - WebExperiments.instance.useCanvasText = false; - return body(DomCanvas()); - } finally { - WebExperiments.instance.useCanvasText = null; - TextMeasurementService.clearCache(); - } - }); -} - -final ui.TextStyle _defaultTextStyle = ui.TextStyle( - color: const ui.Color(0xFF000000), - fontFamily: 'Roboto', - fontSize: 14, -); - -ui.Paragraph paragraph( - String text, { - ui.ParagraphStyle paragraphStyle, - ui.TextStyle textStyle, - double maxWidth = double.infinity, -}) { - final ui.ParagraphBuilder builder = - ui.ParagraphBuilder(paragraphStyle ?? ui.ParagraphStyle()); - builder.pushStyle(textStyle ?? _defaultTextStyle); - builder.addText(text); - builder.pop(); - return builder.build()..layout(ui.ParagraphConstraints(width: maxWidth)); -} - -/// Configures the test to use bundled Roboto and Ahem fonts to avoid golden -/// screenshot differences due to differences in the preinstalled system fonts. -void setUpStableTestFonts() { - setUp(() async { - await ui.webOnlyInitializePlatform(); - ui.webOnlyFontCollection.debugRegisterTestFonts(); - await ui.webOnlyFontCollection.ensureFontsLoaded(); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart deleted file mode 100644 index dc243b2ab711d..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'dart:async'; - -import 'package:test/bootstrap/browser.dart'; -import 'package:ui/ui.dart' hide window; -import 'package:ui/src/engine.dart'; - -import 'scuba.dart'; - -typedef CanvasTest = FutureOr Function(EngineCanvas canvas); - -const String threeLines = 'First\nSecond\nThird'; -const String veryLongWithShortPrefix = - 'Lorem ipsum dolor\nsit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; -const String veryLongWithShortSuffix = - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et\ndolore magna aliqua.'; -const String veryLong = - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; -const String longUnbreakable = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final EngineScubaTester scuba = await EngineScubaTester.initialize( - viewportSize: const Size(800, 800), - ); - - final TextStyle warningStyle = TextStyle( - color: const Color(0xFFFF0000), - fontFamily: 'Roboto', - fontSize: 10, - ); - - setUpStableTestFonts(); - - Paragraph warning(String text) { - return paragraph(text, textStyle: warningStyle); - } - - testEachCanvas('maxLines clipping', (EngineCanvas canvas) { - Offset offset = Offset.zero; - Paragraph p; - - // All three lines are rendered. - p = paragraph(threeLines); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // Only the first two lines are rendered. - p = paragraph(threeLines, paragraphStyle: ParagraphStyle(maxLines: 2)); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // The whole text is rendered. - p = paragraph(veryLong, maxWidth: 200); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // Only the first two lines are rendered. - p = paragraph(veryLong, - paragraphStyle: ParagraphStyle(maxLines: 2), maxWidth: 200); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - return scuba.diffCanvasScreenshot(canvas, 'text_max_lines'); - }); - - testEachCanvas('maxLines with overflow', (EngineCanvas canvas) { - Offset offset = Offset.zero; - Paragraph p; - - // Only the first line is rendered with no ellipsis because the first line - // doesn't overflow. - p = paragraph( - threeLines, - paragraphStyle: ParagraphStyle(ellipsis: '...'), - ); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // The first two lines are rendered with an ellipsis on the 2nd line. - p = paragraph( - veryLongWithShortPrefix, - paragraphStyle: ParagraphStyle(ellipsis: '...'), - maxWidth: 200, - ); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // Only the first line is rendered with an ellipsis. - if (!WebExperiments.instance.useCanvasText) { - // This is now correct with the canvas-based measurement, so we shouldn't - // print the "(wrong)" warning. - p = warning('(wrong)'); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height); - } - p = paragraph( - veryLongWithShortSuffix, - paragraphStyle: ParagraphStyle(ellipsis: '...'), - maxWidth: 200, - ); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // Only the first two lines are rendered and the ellipsis appears on the 2nd - // line. - if (!WebExperiments.instance.useCanvasText) { - // This is now correct with the canvas-based measurement, so we shouldn't - // print the "(wrong)" warning. - p = warning('(wrong)'); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height); - } - p = paragraph( - veryLong, - paragraphStyle: ParagraphStyle(maxLines: 2, ellipsis: '...'), - maxWidth: 200, - ); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - return scuba.diffCanvasScreenshot(canvas, 'text_max_lines_with_ellipsis'); - }); - - testEachCanvas('long unbreakable text', (EngineCanvas canvas) { - Offset offset = Offset.zero; - Paragraph p; - - // The whole line is rendered unbroken when there are no constraints. - p = paragraph(longUnbreakable); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // The whole line is rendered with an ellipsis. - p = paragraph( - longUnbreakable, - paragraphStyle: ParagraphStyle(ellipsis: '...'), - maxWidth: 200, - ); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // The text is broken into multiple lines. - p = paragraph(longUnbreakable, maxWidth: 200); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - // Very narrow constraint (less than one character's width). - p = paragraph('AA', maxWidth: 7); - canvas.drawParagraph(p, offset); - offset = offset.translate(0, p.height + 10); - - return scuba.diffCanvasScreenshot(canvas, 'text_long_unbreakable'); - }); -} diff --git a/lib/web_ui/test/golden_tests/engine/text_placeholders_test.dart b/lib/web_ui/test/golden_tests/engine/text_placeholders_test.dart deleted file mode 100644 index 8b46adcb3846f..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/text_placeholders_test.dart +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'package:test/bootstrap/browser.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; - -import 'scuba.dart'; - -typedef PaintTest = void Function(RecordingCanvas recordingCanvas); - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -/// Whether we are running on iOS Safari. -// TODO: https://github.com/flutter/flutter/issues/66656 -bool get isIosSafari => browserEngine == BrowserEngine.webkit && - operatingSystem == OperatingSystem.iOs; - -void testMain() async { - final EngineScubaTester scuba = await EngineScubaTester.initialize( - viewportSize: const Size(600, 600), - ); - - - setUpStableTestFonts(); - - testEachCanvas('draws paragraphs with placeholders', (EngineCanvas canvas) { - final Rect screenRect = const Rect.fromLTWH(0, 0, 600, 600); - final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); - - Offset offset = Offset.zero; - for (PlaceholderAlignment placeholderAlignment - in PlaceholderAlignment.values) { - _paintTextWithPlaceholder( - recordingCanvas, - offset, - before: 'Lorem ipsum', - after: 'dolor sit amet, consectetur.', - placeholderAlignment: placeholderAlignment, - ); - offset = offset.translate(0.0, 80.0); - } - recordingCanvas.endRecording(); - recordingCanvas.apply(canvas, screenRect); - if (!isIosSafari) { - return scuba.diffCanvasScreenshot(canvas, 'text_with_placeholders'); - } - }); - - testEachCanvas('text alignment and placeholders', (EngineCanvas canvas) { - final Rect screenRect = const Rect.fromLTWH(0, 0, 600, 600); - final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); - - Offset offset = Offset.zero; - _paintTextWithPlaceholder( - recordingCanvas, - offset, - before: 'Lorem', - after: 'ipsum.', - textAlignment: TextAlign.start, - ); - offset = offset.translate(0.0, 80.0); - _paintTextWithPlaceholder( - recordingCanvas, - offset, - before: 'Lorem', - after: 'ipsum.', - textAlignment: TextAlign.center, - ); - offset = offset.translate(0.0, 80.0); - _paintTextWithPlaceholder( - recordingCanvas, - offset, - before: 'Lorem', - after: 'ipsum.', - textAlignment: TextAlign.end, - ); - recordingCanvas.endRecording(); - recordingCanvas.apply(canvas, screenRect); - return scuba.diffCanvasScreenshot(canvas, 'text_align_with_placeholders'); - }); -} - -const Color black = Color(0xFF000000); -const Color blue = Color(0xFF0000FF); -const Color red = Color(0xFFFF0000); - -const Size placeholderSize = Size(80.0, 50.0); - -void _paintTextWithPlaceholder( - RecordingCanvas canvas, - Offset offset, { - String before, - String after, - PlaceholderAlignment placeholderAlignment = PlaceholderAlignment.baseline, - TextAlign textAlignment = TextAlign.left, -}) { - // First let's draw the paragraph. - final Paragraph paragraph = _createParagraphWithPlaceholder( - before, - after, - placeholderAlignment, - textAlignment, - ); - canvas.drawParagraph(paragraph, offset); - - // Then fill the placeholders. - final TextBox placeholderBox = paragraph.getBoxesForPlaceholders().single; - canvas.drawRect( - placeholderBox.toRect().shift(offset), - Paint()..color = red, - ); -} - -Paragraph _createParagraphWithPlaceholder( - String before, - String after, - PlaceholderAlignment placeholderAlignment, - TextAlign textAlignment, -) { - final ParagraphBuilder builder = - ParagraphBuilder(ParagraphStyle(textAlign: textAlignment)); - builder - .pushStyle(TextStyle(color: black, fontFamily: 'Roboto', fontSize: 14)); - builder.addText(before); - builder.addPlaceholder( - placeholderSize.width, - placeholderSize.height, - placeholderAlignment, - baselineOffset: 40.0, - baseline: TextBaseline.alphabetic, - ); - builder.pushStyle(TextStyle(color: blue, fontFamily: 'Roboto', fontSize: 14)); - builder.addText(after); - return builder.build()..layout(ParagraphConstraints(width: 200.0)); -} diff --git a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart deleted file mode 100644 index 5f31ca6ada828..0000000000000 --- a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'package:test/bootstrap/browser.dart'; -import 'package:ui/ui.dart'; -import 'package:ui/src/engine.dart'; - -import 'scuba.dart'; - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - final EngineScubaTester scuba = await EngineScubaTester.initialize( - viewportSize: const Size(800, 800), - ); - - setUpStableTestFonts(); - - void drawLetterAndWordSpacing(EngineCanvas canvas) { - Offset offset = Offset.zero; - - for (double spacing = 0; spacing < 15; spacing += 5) { - canvas.drawParagraph( - paragraph('HelloWorld', - textStyle: TextStyle( - color: const Color(0xFF000000), - decoration: TextDecoration.none, - fontFamily: 'Roboto', - fontSize: 30, - letterSpacing: spacing)), - offset, - ); - offset = offset.translate(0, 40); - } - for (double spacing = 0; spacing < 30; spacing += 10) { - final TextStyle textStyle = TextStyle( - color: const Color(0xFF00FF00), - decoration: TextDecoration.none, - fontFamily: 'Roboto', - fontSize: 30, - wordSpacing: spacing); - canvas.drawParagraph( - paragraph('Hello World', textStyle: textStyle, maxWidth: 600), - offset, - ); - offset = offset.translate(0, 40); - } - } - - testEachCanvas('draws text with letter/word spacing', (EngineCanvas canvas) { - drawLetterAndWordSpacing(canvas); - return scuba.diffCanvasScreenshot( - canvas, 'paint_bounds_for_text_style_letter_spacing'); - }); - - void drawTextDecorationStyle(EngineCanvas canvas) { - final List decorationStyles = [ - TextDecorationStyle.solid, - TextDecorationStyle.double, - TextDecorationStyle.dotted, - TextDecorationStyle.dashed, - TextDecorationStyle.wavy, - ]; - - Offset offset = Offset.zero; - - for (TextDecorationStyle decorationStyle in decorationStyles) { - final TextStyle textStyle = TextStyle( - color: const Color.fromRGBO(50, 50, 255, 1.0), - decoration: TextDecoration.underline, - decorationStyle: decorationStyle, - decorationColor: const Color.fromRGBO(50, 50, 50, 1.0), - fontFamily: 'Roboto', - fontSize: 30, - ); - canvas.drawParagraph( - paragraph('Hello World', textStyle: textStyle, maxWidth: 600), - offset, - ); - offset = offset.translate(0, 40); - } - } - - testEachCanvas('draws text decoration style', (EngineCanvas canvas) { - drawTextDecorationStyle(canvas); - return scuba.diffCanvasScreenshot( - canvas, 'paint_bounds_for_text_decorationStyle'); - }); - - void drawTextDecoration(EngineCanvas canvas) { - final List decorations = [ - TextDecoration.overline, - TextDecoration.underline, - TextDecoration.combine([ - TextDecoration.underline, - TextDecoration.lineThrough - ]), - TextDecoration.combine( - [TextDecoration.underline, TextDecoration.overline]), - TextDecoration.combine( - [TextDecoration.overline, TextDecoration.lineThrough]) - ]; - - Offset offset = Offset.zero; - - for (TextDecoration decoration in decorations) { - final TextStyle textStyle = TextStyle( - color: const Color.fromRGBO(50, 50, 255, 1.0), - decoration: decoration, - decorationStyle: TextDecorationStyle.solid, - decorationColor: const Color.fromRGBO(255, 160, 0, 1.0), - fontFamily: 'Roboto', - fontSize: 20, - ); - canvas.drawParagraph( - paragraph( - 'Hello World $decoration', - textStyle: textStyle, - maxWidth: 600, - ), - offset, - ); - offset = offset.translate(0, 40); - } - } - - testEachCanvas('draws text decoration', (EngineCanvas canvas) { - drawTextDecoration(canvas); - return scuba.diffCanvasScreenshot( - canvas, 'paint_bounds_for_text_decoration'); - }); - - void drawTextWithBackground(EngineCanvas canvas) { - // Single-line text. - canvas.drawParagraph( - paragraph( - 'Hello World', - maxWidth: 600, - textStyle: TextStyle( - color: const Color.fromRGBO(0, 0, 0, 1.0), - background: Paint()..color = const Color.fromRGBO(255, 50, 50, 1.0), - fontFamily: 'Roboto', - fontSize: 30, - ), - ), - Offset.zero, - ); - - // Multi-line text. - canvas.drawParagraph( - paragraph( - 'Multi line Hello World paragraph', - maxWidth: 200, - textStyle: TextStyle( - color: const Color.fromRGBO(0, 0, 0, 1.0), - background: Paint()..color = const Color.fromRGBO(50, 50, 255, 1.0), - fontFamily: 'Roboto', - fontSize: 30, - ), - ), - const Offset(0, 40), - ); - } - - void drawTextWithShadow(EngineCanvas canvas) { - // Single-line text. - canvas.drawParagraph( - paragraph( - 'Hello World', - maxWidth: 600, - textStyle: TextStyle( - color: const Color.fromRGBO(0, 0, 0, 1.0), - background: Paint()..color = const Color.fromRGBO(255, 50, 50, 1.0), - fontFamily: 'Roboto', - fontSize: 30, - shadows: [ - Shadow( - blurRadius: 0, - color: const Color.fromRGBO(255, 0, 255, 1.0), - offset: Offset(10, 5), - ), - ], - ), - ), - Offset.zero, - ); - - // Multi-line text. - canvas.drawParagraph( - paragraph( - 'Multi line Hello World paragraph', - maxWidth: 200, - textStyle: TextStyle( - color: const Color.fromRGBO(0, 0, 0, 1.0), - background: Paint()..color = const Color.fromRGBO(50, 50, 255, 1.0), - fontFamily: 'Roboto', - fontSize: 30, - shadows: [ - Shadow( - blurRadius: 0, - color: const Color.fromRGBO(255, 0, 255, 1.0), - offset: Offset(10, 5), - ), - Shadow( - blurRadius: 0, - color: const Color.fromRGBO(0, 255, 255, 1.0), - offset: Offset(-10, -5), - ), - ], - ), - ), - const Offset(0, 40), - ); - } - - testEachCanvas('draws text with a background', (EngineCanvas canvas) { - drawTextWithBackground(canvas); - return scuba.diffCanvasScreenshot(canvas, 'text_background'); - }); - - testEachCanvas('draws text with a shadow', (EngineCanvas canvas) { - drawTextWithShadow(canvas); - return scuba.diffCanvasScreenshot(canvas, 'text_shadow', maxDiffRatePercent: 0.2); - }); - - testEachCanvas('Handles disabled strut style', (EngineCanvas canvas) { - // Flutter uses [StrutStyle.disabled] for the [SelectableText] widget. This - // translates into a strut style with a [height] of 0, which wasn't being - // handled correctly by the web engine. - final StrutStyle disabled = StrutStyle(height: 0, leading: 0); - canvas.drawParagraph( - paragraph( - 'Hello\nWorld', - paragraphStyle: ParagraphStyle(strutStyle: disabled), - ), - Offset.zero, - ); - return scuba.diffCanvasScreenshot( - canvas, - 'text_strut_style_disabled', - region: Rect.fromLTRB(0, 0, 100, 100), - maxDiffRatePercent: 0.0, - ); - }); -} diff --git a/lib/web_ui/test/golden_tests/golden_failure_smoke_test.dart b/lib/web_ui/test/golden_tests/golden_failure_smoke_test.dart index 7a0cfc7c621e0..85297e5de567b 100644 --- a/lib/web_ui/test/golden_tests/golden_failure_smoke_test.dart +++ b/lib/web_ui/test/golden_tests/golden_failure_smoke_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; @@ -16,7 +15,7 @@ void main() { void testMain() { test('screenshot test reports failure', () async { - html.document.body.innerHtml = 'Text that does not appear on the screenshot!'; - await matchGoldenFile('__local__/smoke_test.png', region: Rect.fromLTWH(0, 0, 320, 200)); + html.document.body!.innerHtml = 'Text that does not appear on the screenshot!'; + await matchGoldenFile('__local__/smoke_test.png', region: const Rect.fromLTWH(0, 0, 320, 200)); }); } diff --git a/lib/web_ui/test/golden_tests/golden_success_smoke_test.dart b/lib/web_ui/test/golden_tests/golden_success_smoke_test.dart index 563e3e92c7269..b6ce8db86e82b 100644 --- a/lib/web_ui/test/golden_tests/golden_success_smoke_test.dart +++ b/lib/web_ui/test/golden_tests/golden_success_smoke_test.dart @@ -2,26 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { +Future testMain() async { debugEmulateFlutterTesterEnvironment = true; await webOnlyInitializePlatform(assetManager: WebOnlyMockAssetManager()); test('screenshot test reports success', () async { - html.document.body.style.fontFamily = 'Roboto'; - html.document.body.innerHtml = 'Hello world!'; - await matchGoldenFile('__local__/smoke_test.png', region: Rect.fromLTWH(0, 0, 320, 200)); + html.document.body!.style.fontFamily = 'Roboto'; + html.document.body!.innerHtml = 'Hello world!'; + // TODO(yjbanov): https://github.com/flutter/flutter/issues/74702 , reduce webkit percentage. + await matchGoldenFile('__local__/smoke_test.png', region: const Rect.fromLTWH(0, 0, 320, 200), + maxDiffRatePercent: browserEngine == BrowserEngine.webkit ? 3.0 : 0.28); }); } diff --git a/lib/web_ui/test/gradient_test.dart b/lib/web_ui/test/gradient_test.dart index b4335dd353228..5cf6edacf94e0 100644 --- a/lib/web_ui/test/gradient_test.dart +++ b/lib/web_ui/test/gradient_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -17,7 +16,7 @@ void testMain() { expect( Gradient.radial( Offset.zero, - null, + 5.0, [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], [0.0, 1.0], TileMode.mirror), diff --git a/lib/web_ui/test/hash_codes_test.dart b/lib/web_ui/test/hash_codes_test.dart index cca96671b711a..7f56a727b6811 100644 --- a/lib/web_ui/test/hash_codes_test.dart +++ b/lib/web_ui/test/hash_codes_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; diff --git a/lib/web_ui/test/html/bitmap_canvas_golden_test.dart b/lib/web_ui/test/html/bitmap_canvas_golden_test.dart new file mode 100644 index 0000000000000..c78e491848b7b --- /dev/null +++ b/lib/web_ui/test/html/bitmap_canvas_golden_test.dart @@ -0,0 +1,260 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; +import 'screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 500, 100); + + late BitmapCanvas canvas; + + void appendToScene() { + // Create a element to make sure our CSS reset applies correctly. + final html.Element testScene = html.Element.tag('flt-scene'); + testScene.append(canvas.rootElement); + domRenderer.glassPaneShadow!.querySelector('flt-scene-host')!.append(testScene); + } + + setUpStableTestFonts(); + + tearDown(() { + domRenderer.glassPaneShadow?.querySelector('flt-scene')?.remove(); + }); + + /// Draws several lines, some aligned precisely with the pixel grid, and some + /// that are offset by 0.5 vertically or horizontally. + /// + /// The produced picture stresses the antialiasing generated by the browser + /// when positioning and rasterizing `` tags. Aliasing artifacts can + /// be seen depending on pixel alignment and whether antialiasing happens + /// before or after rasterization. + void drawMisalignedLines(BitmapCanvas canvas) { + final SurfacePaintData linePaint = (SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1) + .paintData; + + final SurfacePaintData fillPaint = + (SurfacePaint()..style = PaintingStyle.fill).paintData; + + canvas.translate(10, 10); + + canvas.drawRect( + const Rect.fromLTWH(0, 0, 40, 40), + linePaint, + ); + + canvas.drawLine( + const Offset(10, 0), + const Offset(10, 40), + linePaint, + ); + + canvas.drawLine( + const Offset(20.5, 0), + const Offset(20, 40), + linePaint, + ); + + canvas.drawCircle(const Offset(30, 10), 3, fillPaint); + canvas.drawCircle(const Offset(30.5, 30), 3, fillPaint); + } + + test('renders pixels that are not aligned inside the canvas', () async { + canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 60, 60), + RenderStrategy()); + + drawMisalignedLines(canvas); + + appendToScene(); + + await matchGoldenFile('misaligned_pixels_in_canvas_test.png', region: region); + }); + + test('compensates for misalignment of the canvas', () async { + // Notice the 0.5 offset in the bounds rectangle. It's what causes the + // misalignment of canvas relative to the pixel grid. BitmapCanvas will + // shift its position back to 0.0 and at the same time it will it will + // compensate by shifting the contents of the canvas in the opposite + // direction. + canvas = BitmapCanvas(const Rect.fromLTWH(0.5, 0.5, 60, 60), + RenderStrategy()); + canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); + drawMisalignedLines(canvas); + + appendToScene(); + + await matchGoldenFile('misaligned_canvas_test.png', region: region, + maxDiffRatePercent: 1.0); + }); + + test('fill the whole canvas with color even when transformed', () async { + canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50), + RenderStrategy()); + canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); + canvas.translate(25, 25); + canvas.drawColor(const Color.fromRGBO(0, 255, 0, 1.0), BlendMode.src); + + appendToScene(); + + await matchGoldenFile('bitmap_canvas_fills_color_when_transformed.png', + region: region, + maxDiffRatePercent: 5.0); + }); + + test('fill the whole canvas with paint even when transformed', () async { + canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50), + RenderStrategy()); + canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); + canvas.translate(25, 25); + canvas.drawPaint(SurfacePaintData() + ..color = const Color.fromRGBO(0, 255, 0, 1.0) + ..style = PaintingStyle.fill); + + appendToScene(); + + await matchGoldenFile('bitmap_canvas_fills_paint_when_transformed.png', + region: region, + maxDiffRatePercent: 5.0); + }); + + // This test reproduces text blurriness when two pieces of text appear inside + // two nested clips: + // + // ┌───────────────────────┐ + // │ text in outer clip │ + // │ ┌────────────────────┐│ + // │ │ text in inner clip ││ + // │ └────────────────────┘│ + // └───────────────────────┘ + // + // This test clips using canvas. See a similar test in `compositing_golden_test.dart`, + // which clips using layers. + // + // More details: https://github.com/flutter/flutter/issues/32274 + test('renders clipped DOM text with high quality', () async { + final EngineParagraph paragraph = + (ParagraphBuilder(ParagraphStyle(fontFamily: 'Roboto')) + ..addText('Am I blurry?')).build() as EngineParagraph; + paragraph.layout(const ParagraphConstraints(width: 1000)); + + final Rect canvasSize = Rect.fromLTRB( + 0, + 0, + paragraph.maxIntrinsicWidth + 16, + 2 * paragraph.height + 32, + ); + final Rect outerClip = + Rect.fromLTRB(0.5, 0.5, canvasSize.right, canvasSize.bottom); + final Rect innerClip = Rect.fromLTRB(0.5, canvasSize.bottom / 2 + 0.5, + canvasSize.right, canvasSize.bottom); + + canvas = BitmapCanvas(canvasSize, RenderStrategy()); + canvas.debugChildOverdraw = true; + canvas.clipRect(outerClip, ClipOp.intersect); + canvas.drawParagraph(paragraph, const Offset(8.5, 8.5)); + canvas.clipRect(innerClip, ClipOp.intersect); + canvas.drawParagraph(paragraph, Offset(8.5, 8.5 + innerClip.top)); + + expect( + canvas.rootElement.querySelectorAll('p').map((html.Element e) => e.innerText).toList(), + ['Am I blurry?', 'Am I blurry?'], + reason: 'Expected to render text using HTML', + ); + + appendToScene(); + + await matchGoldenFile( + 'bitmap_canvas_draws_high_quality_text.png', + region: canvasSize, + maxDiffRatePercent: 0.0, + pixelComparison: PixelComparison.precise, + ); + }, testOn: 'chrome'); + + // NOTE: Chrome in --headless mode does not reproduce the bug that this test + // attempts to reproduce. However, it's still good to have this test + // for potential future regressions related to paint order. + test('draws text on top of canvas when transformed and clipped', () async { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontSize: 18, + )); + + const String text = 'This text is intentionally very long to make sure that it ' + 'breaks into multiple lines.'; + builder.addText(text); + + final EngineParagraph paragraph = builder.build() as EngineParagraph; + paragraph.layout(const ParagraphConstraints(width: 100)); + + final Rect canvasSize = Offset.zero & const Size(500, 500); + + canvas = BitmapCanvas(canvasSize, RenderStrategy()); + canvas.debugChildOverdraw = true; + + final SurfacePaintData pathPaint = SurfacePaintData() + ..color = const Color(0xFF7F7F7F) + ..style = PaintingStyle.fill; + + const double r = 200.0; + const double l = 50.0; + + final Path path = (Path() + ..moveTo(-l, -l) + ..lineTo(0, -r) + ..lineTo(l, -l) + ..lineTo(r, 0) + ..lineTo(l, l) + ..lineTo(0, r) + ..lineTo(-l, l) + ..lineTo(-r, 0) + ..close()).shift(const Offset(250, 250)); + + canvas.drawPath(path, pathPaint); + canvas.drawParagraph(paragraph, const Offset(180, 50)); + + expect( + canvas.rootElement.querySelectorAll('p').map((html.Element e) => e.text).toList(), + [text], + reason: 'Expected to render text using HTML', + ); + + final SceneBuilder sb = SceneBuilder(); + sb.pushTransform(Matrix4.diagonal3Values(EnginePlatformDispatcher.browserDevicePixelRatio, + EnginePlatformDispatcher.browserDevicePixelRatio, 1.0).toFloat64()); + sb.pushTransform(Matrix4.rotationZ(math.pi / 2).toFloat64()); + sb.pushOffset(0, -500); + sb.pushClipRect(canvasSize); + sb.pop(); + sb.pop(); + sb.pop(); + sb.pop(); + final SurfaceScene scene = sb.build() as SurfaceScene; + final html.Element sceneElement = scene.webOnlyRootElement!; + + sceneElement.querySelector('flt-clip')!.append(canvas.rootElement); + domRenderer.glassPaneShadow!.querySelector('flt-scene-host')!.append(sceneElement); + + await matchGoldenFile( + 'bitmap_canvas_draws_text_on_top_of_canvas.png', + region: canvasSize, + maxDiffRatePercent: 1.0, + pixelComparison: PixelComparison.precise, + ); + }); +} diff --git a/lib/web_ui/test/html/canvas_clip_path_golden_test.dart b/lib/web_ui/test/html/canvas_clip_path_golden_test.dart new file mode 100644 index 0000000000000..fc8ce74b0facd --- /dev/null +++ b/lib/web_ui/test/html/canvas_clip_path_golden_test.dart @@ -0,0 +1,145 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart' as engine; +import 'package:ui/ui.dart' hide TextStyle; + +import 'screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 500.0; + const double screenHeight = 500.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + // Regression test for https://github.com/flutter/flutter/issues/48683 + // Should clip image with oval. + test('Clips image with oval clip path', () async { + final engine.RecordingCanvas rc = + engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + final Path path = Path(); + path.addOval(Rect.fromLTWH(100, 30, testWidth, testHeight)); + rc.clipPath(path); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTWH(100, 30, testWidth, testHeight), engine.SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'image_clipped_by_oval', + region: screenRect); + }); + + // Regression test for https://github.com/flutter/flutter/issues/48683 + test('Clips triangle with oval clip path', () async { + final engine.RecordingCanvas rc = + engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + rc.save(); + const double testWidth = 200; + const double testHeight = 150; + final Path path = Path(); + path.addOval(const Rect.fromLTWH(100, 30, testWidth, testHeight)); + rc.clipPath(path); + final Path paintPath = Path(); + paintPath.moveTo(testWidth / 2, 0); + paintPath.lineTo(testWidth, testHeight); + paintPath.lineTo(0, testHeight); + paintPath.close(); + rc.drawPath( + paintPath, + engine.SurfacePaint() + ..color = const Color(0xFF00FF00) + ..style = PaintingStyle.fill); + rc.restore(); + await canvasScreenshot(rc, 'triangle_clipped_by_oval', + region: screenRect); + }); + + // Regression test for https://github.com/flutter/flutter/issues/78782 + test('Clips on Safari when clip bounds off screen', () async { + final engine.RecordingCanvas rc = + engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + rc.save(); + const double testWidth = 200; + const double testHeight = 150; + + final Path paintPath = Path(); + paintPath.addRect(const Rect.fromLTWH(-50, 0, testWidth, testHeight)); + paintPath.close(); + rc.drawPath(paintPath, + engine.SurfacePaint() + ..color = const Color(0xFF000000) + ..style = PaintingStyle.stroke); + + final Path path = Path(); + path.moveTo(-200, 0); + path.lineTo(100, 75); + path.lineTo(-200, 150); + path.close(); + rc.clipPath(path); + rc.drawImageRect(createTestImage(), const Rect.fromLTRB(0, 0, testWidth, testHeight), + const Rect.fromLTWH(-50, 0, testWidth, testHeight), engine.SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'image_clipped_by_triangle_off_screen'); + }); + + // Tests oval clipping using border radius 50%. + test('Clips against oval', () async { + final engine.RecordingCanvas rc = + engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + rc.save(); + const double testWidth = 200; + const double testHeight = 150; + + final Path paintPath = Path(); + paintPath.addRect(const Rect.fromLTWH(-50, 0, testWidth, testHeight)); + paintPath.close(); + rc.drawPath(paintPath, + engine.SurfacePaint() + ..color = const Color(0xFF000000) + ..style = PaintingStyle.stroke); + + final Path path = Path(); + path.addOval(const Rect.fromLTRB(-200, 0, 100, 150)); + rc.clipPath(path); + rc.drawImageRect(createTestImage(), const Rect.fromLTRB(0, 0, testWidth, testHeight), + const Rect.fromLTWH(-50, 0, testWidth, testHeight), engine.SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'image_clipped_by_oval_path'); + }); +} + +engine.HtmlImage createTestImage({int width = 200, int height = 150}) { + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, width / 3, height); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(width / 3, 0, width / 3, height); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(2 * width / 3, 0, width / 3, height); + ctx.fill(); + final html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; + return engine.HtmlImage(imageElement, width, height); +} diff --git a/lib/web_ui/test/html/canvas_context_golden_test.dart b/lib/web_ui/test/html/canvas_context_golden_test.dart new file mode 100644 index 0000000000000..975cc3822a860 --- /dev/null +++ b/lib/web_ui/test/html/canvas_context_golden_test.dart @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart' as engine; +import 'package:ui/ui.dart' hide TextStyle; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +/// Tests context save/restore. +Future testMain() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(engine.RecordingCanvas rc, String fileName, + {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { + final engine.EngineCanvas engineCanvas = engine.BitmapCanvas(screenRect, + engine.RenderStrategy()); + + rc.endRecording(); + rc.apply(engineCanvas, screenRect); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + // TODO(yjbanov): 10% diff rate is excessive. Update goldens. + await matchGoldenFile('$fileName.png', region: region); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + // Regression test for https://github.com/flutter/flutter/issues/49429 + // Should clip with correct transform. + test('Clips image with oval clip path', () async { + final engine.RecordingCanvas rc = + engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + final engine.SurfacePaint paint = Paint() as engine.SurfacePaint + ..color = const Color(0xFF00FF00) + ..style = PaintingStyle.fill; + rc.save(); + final Path ovalPath = Path(); + ovalPath.addOval(const Rect.fromLTWH(100, 30, 200, 100)); + rc.clipPath(ovalPath); + rc.translate(-500, -500); + rc.save(); + rc.translate(500, 500); + rc.drawPath(ovalPath, paint); + // The line below was causing SaveClipStack to incorrectly set + // transform before path painting. + rc.translate(-1000, -1000); + rc.save(); + rc.restore(); + rc.restore(); + rc.restore(); + // The rectangle should paint without clipping since we restored + // context. + rc.drawRect(const Rect.fromLTWH(0, 0, 4, 200), paint); + await _checkScreenshot(rc, 'context_save_restore_transform'); + }); + + test('Should restore clip path', () async { + final engine.RecordingCanvas rc = + engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + final Paint goodPaint = Paint() + ..color = const Color(0x8000FF00) + ..style = PaintingStyle.fill; + final Paint badPaint = Paint() + ..color = const Color(0xFFFF0000) + ..style = PaintingStyle.fill; + rc.save(); + final Path ovalPath = Path(); + ovalPath.addOval(const Rect.fromLTWH(100, 30, 200, 100)); + rc.clipPath(ovalPath); + rc.translate(-500, -500); + rc.save(); + rc.restore(); + // The rectangle should be clipped against oval. + rc.drawRect(const Rect.fromLTWH(0, 0, 300, 300), badPaint as engine.SurfacePaint); + rc.restore(); + // The rectangle should paint without clipping since we restored + // context. + rc.drawRect(const Rect.fromLTWH(0, 0, 200, 200), goodPaint as engine.SurfacePaint); + await _checkScreenshot(rc, 'context_save_restore_clip'); + }); +} diff --git a/lib/web_ui/test/html/canvas_reuse_golden_test.dart b/lib/web_ui/test/html/canvas_reuse_golden_test.dart new file mode 100644 index 0000000000000..5fb44af23a4cb --- /dev/null +++ b/lib/web_ui/test/html/canvas_reuse_golden_test.dart @@ -0,0 +1,105 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + final SurfacePaint testPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFFFF00FF); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + // Regression test for https://github.com/flutter/flutter/issues/51514 + test('Canvas is reused and z-index doesn\'t leak across paints', () async { + final EngineCanvas engineCanvas = BitmapCanvas(screenRect, + RenderStrategy()); + const Rect region = Rect.fromLTWH(0, 0, 500, 500); + + // Draw first frame into engine canvas. + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); + final Path path = Path() + ..moveTo(3, 0) + ..lineTo(100, 97); + rc.drawPath(path, testPaint); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); + engineCanvas.endOfPaint(); + + html.Element sceneElement = html.Element.tag('flt-scene'); + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + + final html.CanvasElement canvas = html.document.querySelector('canvas')! as html.CanvasElement; + // ! Since canvas is first element, it should have zIndex = -1 for correct + // paint order. + expect(canvas.style.zIndex , '-1'); + + // Add id to canvas element to test for reuse. + const String kTestId = 'test-id-5698'; + canvas.id = kTestId; + + sceneElement.remove(); + // Clear so resources are marked for reuse. + + engineCanvas.clear(); + + // Now paint a second scene to same [BitmapCanvas] but paint an image + // before the path to move canvas element into second position. + final RecordingCanvas rc2 = + RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); + final Path path2 = Path() + ..moveTo(3, 0) + ..quadraticBezierTo(100, 0, 100, 100); + rc2.drawImage(_createRealTestImage(), const Offset(0, 0), SurfacePaint()); + rc2.drawPath(path2, testPaint); + rc2.endRecording(); + rc2.apply(engineCanvas, screenRect); + + sceneElement = html.Element.tag('flt-scene'); + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + + final html.CanvasElement canvas2 = html.document.querySelector('canvas')! as html.CanvasElement; + // ZIndex should have been cleared since we have image element preceding + // canvas. + expect(canvas.style.zIndex != '-1', isTrue); + expect(canvas2.id, kTestId); + await matchGoldenFile('bitmap_canvas_reuse_zindex.png', region: region); + }); +} + +const String _base64Encoded20x20TestImage = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' + 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' + 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; + +HtmlImage _createRealTestImage() { + return HtmlImage( + html.ImageElement() + ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + 20, + 20, + ); +} diff --git a/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart b/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart new file mode 100644 index 0000000000000..7dd6ec7f9edf5 --- /dev/null +++ b/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart @@ -0,0 +1,76 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 500, 500); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws paths using nonzero and evenodd winding rules', () async { + paintPaths(canvas); + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_path_winding.png', region: region); + }); + +} + +void paintPaths(BitmapCanvas canvas) { + canvas.drawRect(const Rect.fromLTRB(0, 0, 500, 500), + SurfacePaintData() + ..color = const Color(0xFFFFFFFF) + ..style = PaintingStyle.fill); // white + + final SurfacePaint paintFill = SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF00B0FF); + final SurfacePaint paintStroke = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2 + ..color = const Color(0xFFE00000); + final Path path1 = Path() + ..fillType = PathFillType.evenOdd + ..moveTo(50, 0) + ..lineTo(21, 90) + ..lineTo(98, 35) + ..lineTo(2, 35) + ..lineTo(79, 90) + ..close() + ..addRect(const Rect.fromLTWH(20, 100, 200, 50)) + ..addRect(const Rect.fromLTWH(40, 120, 160, 10)); + final Path path2 = Path() + ..fillType = PathFillType.nonZero + ..moveTo(50, 200) + ..lineTo(21, 290) + ..lineTo(98, 235) + ..lineTo(2, 235) + ..lineTo(79, 290) + ..close() + ..addRect(const Rect.fromLTWH(20, 300, 200, 50)) + ..addRect(const Rect.fromLTWH(40, 320, 160, 10)); + canvas.drawPath(path1, paintFill.paintData); + canvas.drawPath(path2, paintFill.paintData); + canvas.drawPath(path1, paintStroke.paintData); + canvas.drawPath(path2, paintStroke.paintData); +} diff --git a/lib/web_ui/test/html/clip_op_golden_test.dart b/lib/web_ui/test/html/clip_op_golden_test.dart new file mode 100644 index 0000000000000..2d2b34535dc61 --- /dev/null +++ b/lib/web_ui/test/html/clip_op_golden_test.dart @@ -0,0 +1,88 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'paragraph/helper.dart'; +import 'screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpStableTestFonts(); + + /// Regression test for https://github.com/flutter/flutter/issues/64734. + test('Clips using difference', () async { + const Offset shift = Offset(8, 8); + const Rect region = Rect.fromLTRB(0, 0, 400, 300); + final RecordingCanvas canvas = RecordingCanvas(region); + final Rect titleRect = const Rect.fromLTWH(20, 0, 50, 20).shift(shift); + final SurfacePaint paint = SurfacePaint() + ..style = PaintingStyle.stroke + ..color = const Color(0xff000000) + ..strokeWidth = 1; + canvas.save(); + try { + final Rect borderRect = Rect.fromLTRB(0, 10, region.width, region.height).shift(shift); + canvas.clipRect(titleRect, ClipOp.difference); + canvas.drawRect(borderRect, paint); + } finally { + canvas.restore(); + } + canvas.drawRect(titleRect, paint); + await canvasScreenshot(canvas, 'clip_op_difference', + region: const Rect.fromLTRB(0, 0, 420, 360)); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/86345 + test('Clips with zero width or height', () async { + const Rect region = Rect.fromLTRB(0, 0, 400, 300); + final RecordingCanvas canvas = RecordingCanvas(region); + + final SurfacePaint paint = SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xff00ff00); + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..color = const Color(0xffff0000) + ..strokeWidth = 1; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + final double x = 10 + i * 70; + final double y = 10 + j * 70; + canvas.save(); + // Clip. + canvas.clipRect( + Rect.fromLTWH(x, y, i * 25, j * 25), + ClipOp.intersect, + ); + // Draw the blue (clipped) rect. + canvas.drawRect( + Rect.fromLTWH(x, y, 50, 50), + paint, + ); + final Paragraph p = plain( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 34), + '23', + textStyle: EngineTextStyle.only(color: const Color(0xff0000ff)), + ); + p.layout(const ParagraphConstraints(width: double.infinity)); + canvas.drawParagraph(p, Offset(x, y)); + canvas.restore(); + // Draw the red border. + canvas.drawRect( + Rect.fromLTWH(x, y, 50, 50), + borderPaint, + ); + } + } + await canvasScreenshot(canvas, 'clip_zero_width_height', region: region); + }); +} diff --git a/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart b/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart new file mode 100644 index 0000000000000..e26dfccffb8eb --- /dev/null +++ b/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart @@ -0,0 +1,186 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart' hide ClipRectEngineLayer, BackdropFilterEngineLayer; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUp(() async { + debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + for (final html.Node scene in html.document.querySelectorAll('flt-scene')) { + scene.remove(); + } + + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + // The black circle on the left should not be blurred since it is outside + // the clip boundary around backdrop filter. However there should be only + // one red dot since the other one should be blurred by filter. + test('Background should only blur at ancestor clip boundary', () async { + const Rect region = Rect.fromLTWH(0, 0, 190, 130); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture backgroundPicture = _drawBackground(region); + builder.addPicture(Offset.zero, backgroundPicture); + + builder.pushClipRect( + const Rect.fromLTRB(10, 10, 180, 120), + ); + final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); + builder.addPicture(Offset.zero, circles1); + + builder.pushClipRect( + const Rect.fromLTRB(60, 10, 180, 120), + ); + builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + oldLayer: null); + final Picture circles2 = _drawTestPictureWithCircles(region, 90, 30); + builder.addPicture(Offset.zero, circles2); + builder.pop(); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder + .build() + .webOnlyRootElement!); + + await matchGoldenFile('backdrop_filter_clip.png', region: region); + }); + + test('Background should only blur at ancestor clip boundary after move', () async { + const Rect region = Rect.fromLTWH(0, 0, 190, 130); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture backgroundPicture = _drawBackground(region); + builder.addPicture(Offset.zero, backgroundPicture); + final ClipRectEngineLayer clipEngineLayer = builder.pushClipRect( + const Rect.fromLTRB(10, 10, 180, 120), + ); + final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); + builder.addPicture(Offset.zero, circles1); + final ClipRectEngineLayer clipEngineLayer2 = builder.pushClipRect( + const Rect.fromLTRB(60, 10, 180, 120), + ); + final BackdropFilterEngineLayer oldBackdropFilterLayer = + builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + oldLayer: null); + final Picture circles2 = _drawTestPictureWithCircles(region, 90, 30); + builder.addPicture(Offset.zero, circles2); + builder.pop(); + builder.pop(); + builder.pop(); + builder.build(); + + // Now reparent filter layer in next scene. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.addPicture(Offset.zero, backgroundPicture); + builder2.pushClipRect( + const Rect.fromLTRB(10, 10, 180, 120), + oldLayer: clipEngineLayer + ); + builder2.addPicture(Offset.zero, circles1); + builder2.pushClipRect( + const Rect.fromLTRB(10, 75, 180, 120), + oldLayer: clipEngineLayer2 + ); + builder2.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + oldLayer: oldBackdropFilterLayer); + builder2.addPicture(Offset.zero, circles2); + builder2.pop(); + builder2.pop(); + builder2.pop(); + + html.document.body!.append(builder2 + .build() + .webOnlyRootElement!); + + await matchGoldenFile('backdrop_filter_clip_moved.png', region: region); + }); + + // The blur filter should be applied to the background inside the clip even + // though there are no children of the backdrop filter. + test('Background should blur even if child does not paint', () async { + const Rect region = Rect.fromLTWH(0, 0, 190, 130); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture backgroundPicture = _drawBackground(region); + builder.addPicture(Offset.zero, backgroundPicture); + + builder.pushClipRect( + const Rect.fromLTRB(10, 10, 180, 120), + ); + final Picture circles1 = _drawTestPictureWithCircles(region, 30, 30); + builder.addPicture(Offset.zero, circles1); + + builder.pushClipRect( + const Rect.fromLTRB(60, 10, 180, 120), + ); + builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + oldLayer: null); + builder.pop(); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder + .build() + .webOnlyRootElement!); + + await matchGoldenFile('backdrop_filter_no_child_rendering.png', + region: region); + }); +} + +Picture _drawTestPictureWithCircles(Rect region, double offsetX, double offsetY) { + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(region); + canvas.drawCircle( + Offset(offsetX + 10, offsetY + 10), 10, SurfacePaint()..style = PaintingStyle.fill); + canvas.drawCircle( + Offset(offsetX + 60, offsetY + 10), + 10, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(255, 0, 0, 1)); + canvas.drawCircle( + Offset(offsetX + 10, offsetY + 60), + 10, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(0, 255, 0, 1)); + canvas.drawCircle( + Offset(offsetX + 60, offsetY + 60), + 10, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(0, 0, 255, 1)); + return recorder.endRecording(); +} + +Picture _drawBackground(Rect region) { + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(region); + canvas.drawRect( + region.deflate(8.0), + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFFE0FFE0) + ); + return recorder.endRecording(); +} diff --git a/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart new file mode 100644 index 0000000000000..f873ff491a6af --- /dev/null +++ b/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart @@ -0,0 +1,125 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; + +import '../screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Blend circles with difference and color', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.drawRect( + const Rect.fromLTRB(0, 0, 400, 400), + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(255, 255, 255, 255)); + rc.drawCircle( + const Offset(100, 100), + 80.0, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0) + ..blendMode = BlendMode.difference); + + rc.drawCircle( + const Offset(170, 100), + 80.0, + SurfacePaint() + ..style = PaintingStyle.fill + ..blendMode = BlendMode.color + ..color = const Color.fromARGB(128, 0, 255, 0)); + + rc.drawCircle( + const Offset(135, 170), + 80.0, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0)); + rc.restore(); + + await canvasScreenshot(rc, 'canvas_blend_circle_diff_color', + region: const Rect.fromLTWH(0, 0, 500, 500), + maxDiffRatePercent: operatingSystem == OperatingSystem.macOs ? 2.95 : + operatingSystem == OperatingSystem.iOs ? 1.0 : 0); + }); + + test('Blend circle and text with multiply', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.drawRect( + const Rect.fromLTRB(0, 0, 400, 400), + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(255, 255, 255, 255)); + rc.drawCircle( + const Offset(100, 100), + 80.0, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0) + ..blendMode = BlendMode.difference); + rc.drawCircle( + const Offset(170, 100), + 80.0, + SurfacePaint() + ..style = PaintingStyle.fill + ..blendMode = BlendMode.color + ..color = const Color.fromARGB(128, 0, 255, 0)); + + rc.drawCircle( + const Offset(135, 170), + 80.0, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color.fromARGB(128, 255, 0, 0)); + rc.drawImage(createTestImage(), const Offset(135.0, 130.0), + SurfacePaint()..blendMode = BlendMode.multiply); + rc.restore(); + await canvasScreenshot(rc, 'canvas_blend_image_multiply', + region: const Rect.fromLTWH(0, 0, 500, 500), + maxDiffRatePercent: operatingSystem == OperatingSystem.macOs ? 2.95 : + operatingSystem == OperatingSystem.iOs ? 2.0 : 0); + }); +} + +HtmlImage createTestImage() { + const int width = 100; + const int height = 50; + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(33, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(66, 0, 33, 50); + ctx.fill(); + final html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; + return HtmlImage(imageElement, width, height); +} diff --git a/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart new file mode 100644 index 0000000000000..527bb01337706 --- /dev/null +++ b/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart @@ -0,0 +1,132 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; + +import '../screenshot.dart'; +import '../testimage.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +SurfacePaint makePaint() => Paint() as SurfacePaint; + +Future testMain() async { + const double screenWidth = 500.0; + const double screenHeight = 500.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + const Color red = Color(0xFFFF0000); + const Color green = Color(0xFF00FF00); + const Color blue = Color(0xFF2196F3); + const Color white = Color(0xFFFFFFFF); + const Color grey = Color(0xFF808080); + const Color black = Color(0xFF000000); + + final List> modes = >[ + [BlendMode.clear, BlendMode.src, BlendMode.dst, + BlendMode.srcOver, BlendMode.dstOver, BlendMode.srcIn, BlendMode.dstIn, + BlendMode.srcOut], + [BlendMode.dstOut, BlendMode.srcATop, BlendMode.dstATop, + BlendMode.xor, BlendMode.plus, BlendMode.modulate, BlendMode.screen, + BlendMode.overlay], + [BlendMode.darken, BlendMode.lighten, BlendMode.colorDodge, + BlendMode.hardLight, BlendMode.softLight, BlendMode.difference, + BlendMode.exclusion, BlendMode.multiply], + [BlendMode.hue, BlendMode.saturation, BlendMode.color, + BlendMode.luminosity], + ]; + + for (int blendGroup = 0; blendGroup < 4; ++blendGroup) { + test('Draw image with Group$blendGroup blend modes', () async { + final RecordingCanvas rc = RecordingCanvas( + const Rect.fromLTRB(0, 0, 400, 400)); + rc.save(); + final List blendModes = modes[blendGroup]; + for (int row = 0; row < blendModes.length; row++) { + // draw white background for first 4, black for next 4 blends. + final double top = row * 50.0; + rc.drawRect(Rect.fromLTWH(0, top, 200, 50), makePaint() + ..color = white); + rc.drawRect(Rect.fromLTWH(200, top, 200, 50), makePaint() + ..color = grey); + final BlendMode blendMode = blendModes[row]; + rc.drawImage(createFlutterLogoTestImage(), Offset(0, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(red, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(50, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(green, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(100, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(blue, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(150, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(black, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(200, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(red, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(250, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(green, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(300, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(blue, blendMode)); + rc.drawImage(createFlutterLogoTestImage(), Offset(350, top), + makePaint() + ..colorFilter = EngineColorFilter.mode(black, blendMode)); + } + rc.restore(); + await canvasScreenshot(rc, 'canvas_image_blend_group$blendGroup', + maxDiffRatePercent: 8.0, region: screenRect); + }, + skip: browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs); + } + + // Regression test for https://github.com/flutter/flutter/issues/56971 + test('Draws image and paragraph at same vertical position', () async { + final RecordingCanvas rc = RecordingCanvas( + const Rect.fromLTRB(0, 0, 400, 400)); + rc.save(); + rc.drawRect(const Rect.fromLTWH(0, 50, 200, 50), makePaint() + ..color = white); + rc.drawImage(createFlutterLogoTestImage(), const Offset(0, 50), + makePaint() + ..colorFilter = const EngineColorFilter.mode(red, BlendMode.srcIn)); + + final Paragraph paragraph = createTestParagraph(); + const double textLeft = 80.0; + const double textTop = 50.0; + const double widthConstraint = 300.0; + paragraph.layout(const ParagraphConstraints(width: widthConstraint)); + rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); + + rc.restore(); + await canvasScreenshot(rc, 'canvas_image_blend_and_text', + maxDiffRatePercent: 8.0, region: screenRect); + }); +} + +Paragraph createTestParagraph() { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 14.0, + )); + builder.addText('FOO'); + return builder.build(); +} diff --git a/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart new file mode 100644 index 0000000000000..f1302dee9e5d8 --- /dev/null +++ b/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; + +import '../screenshot.dart'; +import '../testimage.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +SurfacePaint makePaint() => Paint() as SurfacePaint; + +Future testMain() async { + const double screenWidth = 100.0; + const double screenHeight = 100.0; + const Rect region = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + // Regression test for https://github.com/flutter/flutter/issues/76966 + test('Draws image with dstATop color filter', () async { + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawImage( + createFlutterLogoTestImage(), + const Offset(10, 10), + makePaint() + ..colorFilter = + const EngineColorFilter.mode(Color(0x40000000), BlendMode.dstATop)); + await canvasScreenshot(canvas, 'image_color_fiter_dstatop', region: region); + }); + + test('Draws image with matrix color filter', () async { + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawImage( + createFlutterLogoTestImage(), + const Offset(10, 10), + makePaint() + ..colorFilter = const EngineColorFilter.matrix([ + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0, 0, 0, 1, 0, // + ])); + await canvasScreenshot(canvas, 'image_matrix_color_fiter', region: region); + }); +} diff --git a/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart new file mode 100644 index 0000000000000..548db3be499d8 --- /dev/null +++ b/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart @@ -0,0 +1,182 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import '../screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUp(() async { + ui.debugEmulateFlutterTesterEnvironment = true; + await ui.webOnlyInitializePlatform(); + ui.webOnlyFontCollection.debugRegisterTestFonts(); + await ui.webOnlyFontCollection.ensureFontsLoaded(); + }); + + tearDown(() { + ContextStateHandle.debugEmulateWebKitMaskFilter = false; + }); + + // Regression test for https://github.com/flutter/flutter/issues/55930 + void testMaskFilterBlur({bool isWebkit = false}) { + final String browser = isWebkit ? 'Safari' : 'Chrome'; + + test('renders MaskFilter.blur in $browser', () async { + const double screenWidth = 800.0; + const double screenHeight = 150.0; + const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + ContextStateHandle.debugEmulateWebKitMaskFilter = isWebkit; + final RecordingCanvas rc = RecordingCanvas(screenRect); + rc.translate(0, 75); + + final SurfacePaint paint = SurfacePaint() + ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + + rc.translate(50, 0); + rc.drawRect( + ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), + paint, + ); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFF00FF00); + rc.drawRRect( + ui.RRect.fromRectAndRadius( + ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), + const ui.Radius.circular(20), + ), + paint, + ); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFF0000FF); + rc.drawCircle(ui.Offset.zero, 30, paint); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFF00FFFF); + rc.drawPath( + SurfacePath() + ..moveTo(-20, 0) + ..lineTo(0, -50) + ..lineTo(20, 0) + ..lineTo(0, 50) + ..close(), + paint, + ); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFFFF00FF); + rc.drawOval( + ui.Rect.fromCenter(center: ui.Offset.zero, width: 40, height: 100), + paint, + ); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFF888800); + paint.strokeWidth = 5; + rc.drawLine( + const ui.Offset(-20, -50), + const ui.Offset(20, 50), + paint, + ); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFF888888); + rc.drawDRRect( + ui.RRect.fromRectAndRadius( + ui.Rect.fromCircle(center: ui.Offset.zero, radius: 35), + const ui.Radius.circular(20), + ), + ui.RRect.fromRectAndRadius( + ui.Rect.fromCircle(center: ui.Offset.zero, radius: 15), + const ui.Radius.circular(7), + ), + paint, + ); + + rc.translate(100, 0); + paint.color = const ui.Color(0xFF6500C9); + rc.drawRawPoints( + ui.PointMode.points, + Float32List.fromList([-10, -10, -10, 10, 10, -10, 10, 10]), + paint, + ); + + await canvasScreenshot(rc, 'mask_filter_$browser', region: screenRect); + }); + + test('renders transformed MaskFilter.blur in $browser', () async { + const double screenWidth = 300.0; + const double screenHeight = 300.0; + const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + ContextStateHandle.debugEmulateWebKitMaskFilter = isWebkit; + final RecordingCanvas rc = RecordingCanvas(screenRect); + rc.translate(150, 150); + + final SurfacePaint paint = SurfacePaint() + ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + + const List colors = [ + ui.Color(0xFF000000), + ui.Color(0xFF00FF00), + ui.Color(0xFF0000FF), + ui.Color(0xFF00FFFF), + ui.Color(0xFFFF00FF), + ui.Color(0xFF888800), + ui.Color(0xFF888888), + ui.Color(0xFF6500C9), + ]; + + for (final ui.Color color in colors) { + paint.color = color; + rc.rotate(math.pi / 4); + rc.drawRect( + ui.Rect.fromCircle(center: const ui.Offset(90, 0), radius: 20), + paint, + ); + } + + await canvasScreenshot(rc, 'mask_filter_transformed_$browser', + region: screenRect); + }); + } + + testMaskFilterBlur(isWebkit: false); + testMaskFilterBlur(isWebkit: true); + + for (final int testDpr in [1, 2, 4]) { + test('MaskFilter.blur blurs correctly for device-pixel ratio $testDpr', () async { + window.debugOverrideDevicePixelRatio(testDpr.toDouble()); + const ui.Rect screenRect = ui.Rect.fromLTWH(0, 0, 150, 150); + + final RecordingCanvas rc = RecordingCanvas(screenRect); + rc.translate(0, 75); + + final SurfacePaint paint = SurfacePaint() + ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + + rc.translate(75, 0); + rc.drawRect( + ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), + paint, + ); + + await canvasScreenshot(rc, 'mask_filter_blur_dpr_$testDpr', + region: screenRect); + window.debugOverrideDevicePixelRatio(1.0); + }); + } +} diff --git a/lib/web_ui/test/html/compositing/color_filter_golden_test.dart b/lib/web_ui/test/html/compositing/color_filter_golden_test.dart new file mode 100644 index 0000000000000..6a3a5097432f6 --- /dev/null +++ b/lib/web_ui/test/html/compositing/color_filter_golden_test.dart @@ -0,0 +1,194 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart' hide PhysicalShapeEngineLayer; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +const Rect region = Rect.fromLTWH(0, 0, 500, 500); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUp(() async { + debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + for (final html.Node scene in html.document.querySelectorAll('flt-scene')) { + scene.remove(); + } + + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Should apply color filter to image', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture backgroundPicture = _drawBackground(); + builder.addPicture(Offset.zero, backgroundPicture); + builder.pushColorFilter( + const EngineColorFilter.mode(Color(0xF0000080), BlendMode.color)); + final Picture circles1 = _drawTestPictureWithCircles(30, 30); + builder.addPicture(Offset.zero, circles1); + builder.pop(); + html.document.body!.append(builder.build().webOnlyRootElement!); + + // TODO(ferhat): update golden for this test after canvas sandwich detection is + // added to RecordingCanvas. + await matchGoldenFile('color_filter_blendMode_color.png', region: region, + maxDiffRatePercent: 12.0); + }); + + test('Should apply matrix color filter to image', () async { + final List colorMatrix = [ + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0.2126, 0.7152, 0.0722, 0, 0, // + 0, 0, 0, 1, 0, // + ]; + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture backgroundPicture = _drawBackground(); + builder.addPicture(Offset.zero, backgroundPicture); + builder.pushColorFilter( + EngineColorFilter.matrix(colorMatrix)); + final Picture circles1 = _drawTestPictureWithCircles(30, 30); + builder.addPicture(Offset.zero, circles1); + builder.pop(); + html.document.body!.append(builder.build().webOnlyRootElement!); + await matchGoldenFile('color_filter_matrix.png', region: region, + maxDiffRatePercent: 12.0); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/85733 + test('Should apply mode color filter to circles', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture backgroundPicture = _drawBackground(); + builder.addPicture(Offset.zero, backgroundPicture); + builder.pushColorFilter( + const ColorFilter.mode( + Color(0xFFFF0000), + BlendMode.srcIn, + )); + final Picture circles1 = _drawTestPictureWithCircles(30, 30); + builder.addPicture(Offset.zero, circles1); + builder.pop(); + html.document.body!.append(builder.build().webOnlyRootElement!); + await matchGoldenFile('color_filter_mode.png', region: region, + maxDiffRatePercent: 12.0); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/59451. + /// + /// Picture with overlay blend inside a physical shape. Should show image + /// at 0,0. In the filed issue it was leaving a gap on top. + test('Should render image with color filter without gap', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Path path = Path(); + path.addRRect(RRect.fromRectAndRadius( + const Rect.fromLTRB(0, 0, 400, 400), const Radius.circular(2))); + final PhysicalShapeEngineLayer oldLayer = builder.pushPhysicalShape( + path: path, color: const Color(0xFFFFFFFF), elevation: 0); + final Picture circles1 = _drawTestPictureWithImage( + const ColorFilter.mode(Color(0x3C4043), BlendMode.overlay)); + builder.addPicture(const Offset(10, 0), circles1); + builder.pop(); + builder.build(); + + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushPhysicalShape( + path: path, color: const Color(0xFFFFFFFF), elevation: 0, oldLayer: oldLayer); + builder2.addPicture(const Offset(10, 0), circles1); + builder2.pop(); + + html.document.body!.append(builder2.build().webOnlyRootElement!); + + await matchGoldenFile('color_filter_blendMode_overlay.png', + region: region, + maxDiffRatePercent: 12.0); + }); +} + +Picture _drawTestPictureWithCircles(double offsetX, double offsetY) { + final EnginePictureRecorder recorder = + PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); + canvas.drawCircle(Offset(offsetX + 10, offsetY + 10), 10, + (Paint()..style = PaintingStyle.fill) as SurfacePaint); + canvas.drawCircle( + Offset(offsetX + 60, offsetY + 10), + 10, + (Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(255, 0, 0, 1)) as SurfacePaint); + canvas.drawCircle( + Offset(offsetX + 10, offsetY + 60), + 10, + (Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(0, 255, 0, 1)) as SurfacePaint); + canvas.drawCircle( + Offset(offsetX + 60, offsetY + 60), + 10, + (Paint() + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(0, 0, 255, 1)) as SurfacePaint); + return recorder.endRecording(); +} + +Picture _drawTestPictureWithImage(ColorFilter filter) { + final EnginePictureRecorder recorder = + PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); + final Image testImage = createTestImage(); + canvas.drawImageRect( + testImage, + const Rect.fromLTWH(0, 0, 200, 150), + const Rect.fromLTWH(0, 0, 300, 300), + (Paint() + ..style = PaintingStyle.fill + ..colorFilter = filter + ..color = const Color.fromRGBO(0, 0, 255, 1)) as SurfacePaint); + return recorder.endRecording(); +} + +Picture _drawBackground() { + final EnginePictureRecorder recorder = + PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); + canvas.drawRect( + const Rect.fromLTWH(8, 8, 400.0 - 16, 400.0 - 16), + (Paint() + ..style = PaintingStyle.fill + ..color = const Color(0xFFE0FFE0)) as SurfacePaint); + return recorder.endRecording(); +} + +HtmlImage createTestImage({int width = 200, int height = 150}) { + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, width / 3, height); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(width / 3, 0, width / 3, height); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(2 * width / 3, 0, width / 3, height); + ctx.fill(); + final html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; + return HtmlImage(imageElement, width, height); +} diff --git a/lib/web_ui/test/html/compositing/compositing_golden_test.dart b/lib/web_ui/test/html/compositing/compositing_golden_test.dart new file mode 100644 index 0000000000000..4cfb4f4c2be13 --- /dev/null +++ b/lib/web_ui/test/html/compositing/compositing_golden_test.dart @@ -0,0 +1,941 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:web_engine_tester/golden_tester.dart'; + +import '../../matchers.dart'; + +const ui.Rect region = ui.Rect.fromLTWH(0, 0, 500, 100); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUp(() async { + // To debug test failures uncomment the following to visualize clipping + // layers: + // debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + for (final html.Node scene in html.document.querySelectorAll('flt-scene')) { + scene.remove(); + } + + await ui.webOnlyInitializePlatform(); + ui.webOnlyFontCollection.debugRegisterTestFonts(); + await ui.webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('pushClipRect', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 60, 60), + ); + _drawTestPicture(builder); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_shifted_clip_rect.png', region: region); + }); + + test('pushClipRect with offset and transform', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + + builder.pushOffset(0, 60); + builder.pushTransform( + Matrix4.diagonal3Values(1, -1, 1).toFloat64(), + ); + builder.pushClipRect( + const ui.Rect.fromLTRB(10, 10, 60, 60), + ); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_clip_rect_with_offset_and_transform.png', + region: region); + }); + + test('pushClipRect with offset and transform ClipOp none should not clip', + () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + + builder.pushOffset(0, 80); + builder.pushTransform( + Matrix4.diagonal3Values(1, -1, 1).toFloat64(), + ); + builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 60, 60), + clipBehavior: ui.Clip.none); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_clip_rect_clipop_none.png', + region: region); + }); + + test('pushClipRRect with offset and transform ClipOp none should not clip', + () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + + builder.pushOffset(0, 80); + builder.pushTransform( + Matrix4.diagonal3Values(1, -1, 1).toFloat64(), + ); + builder.pushClipRRect( + ui.RRect.fromRectAndRadius( + const ui.Rect.fromLTRB(10, 10, 60, 60), + const ui.Radius.circular(1), + ), + clipBehavior: ui.Clip.none); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_clip_rrect_clipop_none.png', + region: region); + }); + + test('pushClipRRect', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRRect( + ui.RRect.fromLTRBR(10, 10, 60, 60, const ui.Radius.circular(5)), + ); + _drawTestPicture(builder); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_shifted_clip_rrect.png', region: region); + }); + + test('pushPhysicalShape', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushPhysicalShape( + path: ui.Path()..addRect(const ui.Rect.fromLTRB(10, 10, 60, 60)), + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color.fromRGBO(0, 0, 0, 0.3), + elevation: 0, + ); + _drawTestPicture(builder); + builder.pop(); + + builder.pushOffset(70, 0); + builder.pushPhysicalShape( + path: ui.Path() + ..addRRect(ui.RRect.fromLTRBR(10, 10, 60, 60, const ui.Radius.circular(5))), + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color.fromRGBO(0, 0, 0, 0.3), + elevation: 0, + ); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_shifted_physical_shape_clip.png', + region: region); + }); + + test('pushPhysicalShape clipOp.none', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushPhysicalShape( + path: ui.Path()..addRect(const ui.Rect.fromLTRB(10, 10, 60, 60)), + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color.fromRGBO(0, 0, 0, 0.3), + elevation: 0, + ); + _drawTestPicture(builder); + builder.pop(); + + builder.pushOffset(70, 0); + builder.pushPhysicalShape( + path: ui.Path() + ..addRRect(ui.RRect.fromLTRBR(10, 10, 60, 60, const ui.Radius.circular(5))), + clipBehavior: ui.Clip.none, + color: const ui.Color.fromRGBO(0, 0, 0, 0.3), + elevation: 0, + ); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_shifted_physical_shape_clipnone.png', + region: region); + }); + + test('pushPhysicalShape with path and elevation', () async { + final ui.Path cutCornersButton = ui.Path() + ..moveTo(15, 10) + ..lineTo(60, 10) + ..lineTo(60, 60) + ..lineTo(15, 60) + ..lineTo(10, 55) + ..lineTo(10, 15); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFFA0FFFF), + elevation: 2, + ); + _drawTestPicture(builder); + builder.pop(); + + builder.pushOffset(70, 0); + builder.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFFA0FFFF), + elevation: 8, + ); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + + builder.pushOffset(140, 0); + builder.pushPhysicalShape( + path: ui.Path()..addOval(const ui.Rect.fromLTRB(10, 10, 60, 60)), + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFFA0FFFF), + elevation: 4, + ); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + + builder.pushOffset(210, 0); + builder.pushPhysicalShape( + path: ui.Path() + ..addRRect(ui.RRect.fromRectAndRadius( + const ui.Rect.fromLTRB(10, 10, 60, 60), const ui.Radius.circular(10.0))), + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFFA0FFFF), + elevation: 4, + ); + _drawTestPicture(builder); + builder.pop(); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_physical_shape_path.png', + region: region); + }); + + test('pushPhysicalShape should update across frames', () async { + final ui.Path cutCornersButton = ui.Path() + ..moveTo(15, 10) + ..lineTo(60, 10) + ..lineTo(60, 60) + ..lineTo(15, 60) + ..lineTo(10, 55) + ..lineTo(10, 15); + + /// Start with shape that has elevation and red color. + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final ui.PhysicalShapeEngineLayer oldShapeLayer = builder.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFFFF0000), + elevation: 2, + ); + _drawTestPicture(builder); + builder.pop(); + + final html.Element viewElement = builder.build().webOnlyRootElement!; + html.document.body!.append(viewElement); + await matchGoldenFile('compositing_physical_update_1.png', region: region); + viewElement.remove(); + + /// Update color to green. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + final ui.PhysicalShapeEngineLayer oldShapeLayer2 = builder2.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFF00FF00), + elevation: 2, + oldLayer: oldShapeLayer, + ); + _drawTestPicture(builder2); + builder2.pop(); + + final html.Element viewElement2 = builder2.build().webOnlyRootElement!; + html.document.body!.append(viewElement2); + await matchGoldenFile('compositing_physical_update_2.png', region: region); + viewElement2.remove(); + + /// Update elevation. + final SurfaceSceneBuilder builder3 = SurfaceSceneBuilder(); + final ui.PhysicalShapeEngineLayer oldShapeLayer3 = builder3.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFF00FF00), + elevation: 6, + oldLayer: oldShapeLayer2, + ); + _drawTestPicture(builder3); + builder3.pop(); + + final html.Element viewElement3 = builder3.build().webOnlyRootElement!; + html.document.body!.append(viewElement3); + await matchGoldenFile('compositing_physical_update_3.png', + region: region, maxDiffRatePercent: 0.8); + viewElement3.remove(); + + /// Update shape from arbitrary path to rect. + final SurfaceSceneBuilder builder4 = SurfaceSceneBuilder(); + final ui.PhysicalShapeEngineLayer oldShapeLayer4 = builder4.pushPhysicalShape( + path: ui.Path()..addOval(const ui.Rect.fromLTRB(10, 10, 60, 60)), + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFF00FF00), + elevation: 6, + oldLayer: oldShapeLayer3, + ); + _drawTestPicture(builder4); + builder4.pop(); + + final html.Element viewElement4 = builder4.build().webOnlyRootElement!; + html.document.body!.append(viewElement4); + await matchGoldenFile('compositing_physical_update_4.png', region: region); + viewElement4.remove(); + + /// Update shape back to arbitrary path. + final SurfaceSceneBuilder builder5 = SurfaceSceneBuilder(); + final ui.PhysicalShapeEngineLayer oldShapeLayer5 = builder5.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFF00FF00), + elevation: 6, + oldLayer: oldShapeLayer4, + ); + _drawTestPicture(builder5); + builder5.pop(); + + final html.Element viewElement5 = builder5.build().webOnlyRootElement!; + html.document.body!.append(viewElement5); + await matchGoldenFile('compositing_physical_update_3.png', + region: region, + maxDiffRatePercent: browserEngine == BrowserEngine.webkit ? 0.6 : 0.4); + viewElement5.remove(); + + /// Update shadow color. + final SurfaceSceneBuilder builder6 = SurfaceSceneBuilder(); + builder6.pushPhysicalShape( + path: cutCornersButton, + clipBehavior: ui.Clip.hardEdge, + color: const ui.Color(0xFF00FF00), + shadowColor: const ui.Color(0xFFFF0000), + elevation: 6, + oldLayer: oldShapeLayer5, + ); + _drawTestPicture(builder6); + builder6.pop(); + + final html.Element viewElement6 = builder6.build().webOnlyRootElement!; + html.document.body!.append(viewElement6); + await matchGoldenFile('compositing_physical_update_5.png', region: region); + viewElement6.remove(); + }); + + test('pushImageFilter blur', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushImageFilter( + ui.ImageFilter.blur(sigmaX: 1, sigmaY: 3), + ); + _drawTestPicture(builder); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_image_filter.png', region: region); + }); + + test('pushImageFilter matrix', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushImageFilter( + ui.ImageFilter.matrix( + ( + Matrix4.identity() + ..translate(40, 10) + ..rotateZ(math.pi / 6) + ..scale(0.75, 0.75) + ).toFloat64()), + ); + _drawTestPicture(builder); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_image_filter_matrix.png', region: region); + }); + + group('Cull rect computation', () { + _testCullRectComputation(); + }); +} + +void _testCullRectComputation() { + // Draw a picture larger that screen. Verify that cull rect is equal to screen + // bounds. + test('fills screen bounds', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle( + ui.Offset.zero, 10000, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(0, 0, 500, 100)); + }, skip: ''' + TODO(https://github.com/flutter/flutter/issues/40395) + Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''); + + // Draw a picture that overflows the screen. Verify that cull rect is the + // intersection of screen bounds and paint bounds. + test('intersects with screen bounds', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(0, 0, 20, 20)); + }); + + // Draw a picture that's fully outside the screen bounds. Verify the cull rect + // is zero. + test('fully outside screen bounds', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle( + const ui.Offset(-100, -100), 20, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, ui.Rect.zero); + expect(picture.debugExactGlobalCullRect, ui.Rect.zero); + }); + + // Draw a picture that's fully inside the screen. Verify that cull rect is + // equal to the paint bounds. + test('limits to paint bounds if no clip layers', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle( + const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(40, 40, 60, 60)); + }); + + // Draw a picture smaller than the screen. Offset it such that it remains + // fully inside the screen bounds. Verify that cull rect is still just the + // paint bounds. + test('offset does not affect paint bounds', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + + builder.pushOffset(10, 10); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle( + const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.pop(); + + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(40, 40, 60, 60)); + }); + + // Draw a picture smaller than the screen. Offset it such that the picture + // overflows screen bounds. Verify that the cull rect is the intersection + // between screen bounds and paint bounds. + test('offset overflows paint bounds', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + + builder.pushOffset(0, 90); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.pop(); + + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect( + picture.debugExactGlobalCullRect, const ui.Rect.fromLTRB(0, 70, 20, 100)); + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(0, -20, 20, 10)); + }, skip: ''' + TODO(https://github.com/flutter/flutter/issues/40395) + Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''); + + // Draw a picture inside a layer clip but fill all available space inside it. + // Verify that the cull rect is equal to the layer clip. + test('fills layer clip rect', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const ui.Rect.fromLTWH(10, 10, 60, 60), + ); + + builder.pushClipRect( + const ui.Rect.fromLTWH(40, 40, 60, 60), + ); + + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle( + ui.Offset.zero, 10000, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + + builder.pop(); // pushClipRect + builder.pop(); // pushClipRect + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_cull_rect_fills_layer_clip.png', + region: region); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(40, 40, 70, 70)); + }); + + // Draw a picture inside a layer clip but position the picture such that its + // paint bounds overflow the layer clip. Verify that the cull rect is the + // intersection between the layer clip and paint bounds. + test('intersects layer clip rect and paint bounds', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const ui.Rect.fromLTWH(10, 10, 60, 60), + ); + + builder.pushClipRect( + const ui.Rect.fromLTWH(40, 40, 60, 60), + ); + + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle( + const ui.Offset(80, 55), 30, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + + builder.pop(); // pushClipRect + builder.pop(); // pushClipRect + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile( + 'compositing_cull_rect_intersects_clip_and_paint_bounds.png', + region: region); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, const ui.Rect.fromLTRB(50, 40, 70, 70)); + }); + + // Draw a picture inside a layer clip that's positioned inside the clip using + // an offset layer. Verify that the cull rect is the intersection between the + // layer clip and the offset paint bounds. + test('offsets picture inside layer clip rect', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const ui.Rect.fromLTWH(10, 10, 60, 60), + ); + + builder.pushClipRect( + const ui.Rect.fromLTWH(40, 40, 60, 60), + ); + + builder.pushOffset(55, 70); + + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + + builder.pop(); // pushOffset + builder.pop(); // pushClipRect + builder.pop(); // pushClipRect + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_cull_rect_offset_inside_layer_clip.png', + region: region); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, + const ui.Rect.fromLTRB(-15.0, -20.0, 15.0, 0.0)); + }); + + // Draw a picture inside a layer clip that's positioned an offset layer such + // that the picture is push completely outside the clip area. Verify that the + // cull rect is zero. + test('zero intersection with clip', () async { + final ui.SceneBuilder builder = ui.SceneBuilder(); + builder.pushClipRect( + const ui.Rect.fromLTWH(10, 10, 60, 60), + ); + + builder.pushClipRect( + const ui.Rect.fromLTWH(40, 40, 60, 60), + ); + + builder.pushOffset(100, 50); + + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + + builder.pop(); // pushOffset + builder.pop(); // pushClipRect + builder.pop(); // pushClipRect + + builder.build(); + + final PersistedPicture picture = enumeratePictures().single; + expect(picture.optimalLocalCullRect, ui.Rect.zero); + expect(picture.debugExactGlobalCullRect, ui.Rect.zero); + }); + + // Draw a picture inside a rotated clip. Verify that the cull rect is big + // enough to fit the rotated clip. + test('rotates clip and the picture', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushOffset(80, 50); + + builder.pushTransform( + Matrix4.rotationZ(-math.pi / 4).toFloat64(), + ); + + builder.pushClipRect( + const ui.Rect.fromLTRB(-10, -10, 10, 10), + ); + + builder.pushTransform( + Matrix4.rotationZ(math.pi / 4).toFloat64(), + ); + + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawPaint(SurfacePaint() + ..color = const ui.Color.fromRGBO(0, 0, 255, 0.6) + ..style = ui.PaintingStyle.fill); + canvas.drawRect( + const ui.Rect.fromLTRB(-5, -5, 5, 5), + SurfacePaint() + ..color = const ui.Color.fromRGBO(0, 255, 0, 1.0) + ..style = ui.PaintingStyle.fill, + ); + }); + + builder.pop(); // pushTransform + builder.pop(); // pushClipRect + builder.pop(); // pushTransform + builder.pop(); // pushOffset + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_cull_rect_rotated.png', region: region); + + final PersistedPicture picture = enumeratePictures().single; + expect( + picture.optimalLocalCullRect, + within( + distance: 0.05, from: const ui.Rect.fromLTRB(-14.1, -14.1, 14.1, 14.1)), + ); + }); + + test('pushClipPath', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final ui.Path path = ui.Path(); + path.addRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); + builder.pushClipPath( + path, + ); + _drawTestPicture(builder); + builder.pop(); + + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_clip_path.png', region: region); + }); + + // Draw a picture inside a rotated clip. Verify that the cull rect is big + // enough to fit the rotated clip. + test('clips correctly when using 3d transforms', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + + builder.pushTransform(Matrix4.diagonal3Values( + EnginePlatformDispatcher.browserDevicePixelRatio, + EnginePlatformDispatcher.browserDevicePixelRatio, + 1.0) + .toFloat64()); + + // TODO(yjbanov): see the TODO below. + // final double screenWidth = html.window.innerWidth.toDouble(); + // final double screenHeight = html.window.innerHeight.toDouble(); + + final Matrix4 scaleTransform = Matrix4.identity().scaled(0.5, 0.2); + builder.pushTransform( + scaleTransform.toFloat64(), + ); + + builder.pushOffset(400, 200); + + builder.pushClipRect( + const ui.Rect.fromLTRB(-200, -200, 200, 200), + ); + + builder + .pushTransform(Matrix4.rotationY(45.0 * math.pi / 180.0).toFloat64()); + + builder.pushClipRect( + const ui.Rect.fromLTRB(-140, -140, 140, 140), + ); + + builder.pushTransform(Matrix4.translationValues(0, 0, -50).toFloat64()); + + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawPaint(SurfacePaint() + ..color = const ui.Color.fromRGBO(0, 0, 255, 0.6) + ..style = ui.PaintingStyle.fill); + // ui.Rect will be clipped. + canvas.drawRect( + const ui.Rect.fromLTRB(-150, -150, 150, 150), + SurfacePaint() + ..color = const ui.Color.fromRGBO(0, 255, 0, 1.0) + ..style = ui.PaintingStyle.fill, + ); + // Should be outside the clip range. + canvas.drawRect( + const ui.Rect.fromLTRB(-150, -150, -140, -140), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) + ..style = ui.PaintingStyle.fill, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(140, -150, 150, -140), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) + ..style = ui.PaintingStyle.fill, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(-150, 140, -140, 150), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) + ..style = ui.PaintingStyle.fill, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(140, 140, 150, 150), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 255, 0, 0) + ..style = ui.PaintingStyle.fill, + ); + // Should be inside clip range + canvas.drawRect( + const ui.Rect.fromLTRB(-100, -100, -90, -90), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) + ..style = ui.PaintingStyle.fill, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(90, -100, 100, -90), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) + ..style = ui.PaintingStyle.fill, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(-100, 90, -90, 100), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) + ..style = ui.PaintingStyle.fill, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(90, 90, 100, 100), + SurfacePaint() + ..color = const ui.Color.fromARGB(0xE0, 0, 0, 0x80) + ..style = ui.PaintingStyle.fill, + ); + }); + + builder.pop(); // pushTransform Z-50 + builder.pop(); // pushClipRect + builder.pop(); // pushTransform 3D rotate + builder.pop(); // pushClipRect + builder.pop(); // pushOffset + builder.pop(); // pushTransform scale + builder.pop(); // pushTransform scale devicepixelratio + html.document.body!.append(builder.build().webOnlyRootElement!); + + await matchGoldenFile('compositing_3d_rotate1.png', region: region); + + // ignore: unused_local_variable + final PersistedPicture picture = enumeratePictures().single; + // TODO(yjbanov): https://github.com/flutter/flutter/issues/40395) + // Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500. + // expect( + // picture.optimalLocalCullRect, + // within( + // distance: 0.05, + // from: ui.Rect.fromLTRB( + // -140, -140, screenWidth - 360.0, screenHeight + 40.0)), + // ); + }); + + // This test reproduces text blurriness when two pieces of text appear inside + // two nested clips: + // + // ┌───────────────────────┐ + // │ text in outer clip │ + // │ ┌────────────────────┐│ + // │ │ text in inner clip ││ + // │ └────────────────────┘│ + // └───────────────────────┘ + // + // This test clips using layers. See a similar test in `bitmap_canvas_golden_test.dart`, + // which clips using canvas. + // + // More details: https://github.com/flutter/flutter/issues/32274 + test( + 'renders clipped text with high quality', + () async { + // To reproduce blurriness we need real clipping. + final CanvasParagraph paragraph = + (ui.ParagraphBuilder(ui.ParagraphStyle(fontFamily: 'Roboto')) + // Use a decoration to force rendering in DOM mode. + ..pushStyle(ui.TextStyle(decoration: ui.TextDecoration.lineThrough, decorationColor: const ui.Color(0x00000000))) + ..addText('Am I blurry?')) + .build() as CanvasParagraph; + paragraph.layout(const ui.ParagraphConstraints(width: 1000)); + + final ui.Rect canvasSize = ui.Rect.fromLTRB( + 0, + 0, + paragraph.maxIntrinsicWidth + 16, + 2 * paragraph.height + 32, + ); + final ui.Rect outerClip = + ui.Rect.fromLTRB(0.5, 0.5, canvasSize.right, canvasSize.bottom); + final ui.Rect innerClip = ui.Rect.fromLTRB(0.5, canvasSize.bottom / 2 + 0.5, + canvasSize.right, canvasSize.bottom); + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + + builder.pushClipRect(outerClip); + + { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(outerClip); + canvas.drawParagraph(paragraph, const ui.Offset(8.5, 8.5)); + final ui.Picture picture = recorder.endRecording(); + expect(paragraph.drawOnCanvas, isFalse); + + builder.addPicture( + ui.Offset.zero, + picture, + ); + } + + builder.pushClipRect(innerClip); + { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(innerClip); + canvas.drawParagraph(paragraph, ui.Offset(8.5, 8.5 + innerClip.top)); + final ui.Picture picture = recorder.endRecording(); + expect(paragraph.drawOnCanvas, isFalse); + + builder.addPicture( + ui.Offset.zero, + picture, + ); + } + builder.pop(); // inner clip + builder.pop(); // outer clip + + final html.Element sceneElement = builder.build().webOnlyRootElement!; + expect( + sceneElement + .querySelectorAll('p') + .map((html.Element e) => e.innerText) + .toList(), + ['Am I blurry?', 'Am I blurry?'], + reason: 'Expected to render text using HTML', + ); + html.document.body!.append(sceneElement); + + await matchGoldenFile( + 'compositing_draw_high_quality_text.png', + region: canvasSize, + maxDiffRatePercent: 0.0, + pixelComparison: PixelComparison.precise, + ); + }, + testOn: 'chrome', + ); +} + +void _drawTestPicture(ui.SceneBuilder builder) { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = + recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 100, 100)); + canvas.drawCircle( + const ui.Offset(10, 10), 10, SurfacePaint()..style = ui.PaintingStyle.fill); + canvas.drawCircle( + const ui.Offset(60, 10), + 10, + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(255, 0, 0, 1)); + canvas.drawCircle( + const ui.Offset(10, 60), + 10, + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(0, 255, 0, 1)); + canvas.drawCircle( + const ui.Offset(60, 60), + 10, + SurfacePaint() + ..style = ui.PaintingStyle.fill + ..color = const ui.Color.fromRGBO(0, 0, 255, 1)); + final ui.Picture picture = recorder.endRecording(); + + builder.addPicture( + ui.Offset.zero, + picture, + ); +} + +typedef PaintCallback = void Function(RecordingCanvas canvas); + +void drawWithBitmapCanvas(ui.SceneBuilder builder, PaintCallback callback, + {ui.Rect bounds = ui.Rect.largest}) { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(bounds); + + canvas.debugEnforceArbitraryPaint(); + callback(canvas); + final ui.Picture picture = recorder.endRecording(); + + builder.addPicture( + ui.Offset.zero, + picture, + ); +} diff --git a/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart b/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart new file mode 100644 index 0000000000000..37baeeb01baa8 --- /dev/null +++ b/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; +import '../screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 500.0; + const double screenHeight = 500.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Should blur rectangles based on sigma.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + for (int blurSigma = 1; blurSigma < 10; blurSigma += 2) { + final SurfacePaint paint = SurfacePaint() + ..color = const Color(0xFF2fdfd2) + ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma.toDouble()); + rc.drawRect(Rect.fromLTWH(15.0, 15.0 + blurSigma * 40, 200, 20), paint); + } + await canvasScreenshot(rc, 'dom_mask_filter_blur', + region: screenRect, + maxDiffRatePercent: 0.01); + }); +} diff --git a/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart new file mode 100644 index 0000000000000..5454e1a6be83d --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart @@ -0,0 +1,114 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 400, 600); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws arcs with largeArc , anticlockwise variations', () async { + paintArc(canvas, const Offset(0, 0), + largeArc: false, clockwise: false, distance: 20); + paintArc(canvas, const Offset(200, 0), + largeArc: true, clockwise: false, distance: 20); + paintArc(canvas, const Offset(0, 150), + largeArc: false, clockwise: true, distance: 20); + paintArc(canvas, const Offset(200, 150), + largeArc: true, clockwise: true, distance: 20); + paintArc(canvas, const Offset(0, 300), + largeArc: false, clockwise: false, distance: -20); + paintArc(canvas, const Offset(200, 300), + largeArc: true, clockwise: false, distance: -20); + paintArc(canvas, const Offset(0, 400), + largeArc: false, clockwise: true, distance: -20); + paintArc(canvas, const Offset(200, 400), + largeArc: true, clockwise: true, distance: -20); + + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_arc_to_point.png', region: region); + }); + + test('Path.addArc that starts new path has correct start point', () async { + const Rect rect = Rect.fromLTWH(20, 20, 200, 200); + final Path p = Path() + ..fillType = PathFillType.evenOdd + ..addRect(rect) + ..addArc(Rect.fromCircle(center: rect.center, + radius: rect.size.shortestSide / 2), 0.25 * math.pi, 1.5 * math.pi); + canvas.drawPath(p, SurfacePaintData() + ..color = const Color(0xFFFF9800) // orange + ..style = PaintingStyle.fill); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_addarc.png', region: region); + }); + + test('Should render counter clockwise arcs', () async { + final Path path = Path(); + path.moveTo(149.999999999999997, 50); + path.lineTo(149.999999999999997, 20); + path.arcTo(const Rect.fromLTRB(20, 20, 280, 280), 4.71238898038469, + 5.759586531581287 - 4.71238898038469, true); + path.lineTo(236.60254037844385, 99.99999999999999); + path.arcTo(const Rect.fromLTRB(50, 50, 250, 250), 5.759586531581287, + 4.71238898038469 - 5.759586531581287, true); + path.lineTo(149.999999999999997, 20); + canvas.drawPath(path, SurfacePaintData() + ..color = const Color(0xFFFF9800) // orange + ..style = PaintingStyle.fill); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_addarc_ccw.png', region: region); + }); +} + +void paintArc(BitmapCanvas canvas, Offset offset, + {bool largeArc = false, bool clockwise = false, double distance = 0}) { + + final Offset startP = + Offset(75 - distance + offset.dx, 75 - distance + offset.dy); + final Offset endP = + Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); + canvas.drawRect( + Rect.fromLTRB(startP.dx, startP.dy, endP.dx, endP.dy), + SurfacePaintData() + ..strokeWidth = 1 + ..color = const Color(0xFFFF9800) // orange + ..style = PaintingStyle.stroke); + final Path path = Path(); + path.moveTo(startP.dx, startP.dy); + path.arcToPoint(endP, + rotation: 45, + radius: const Radius.elliptical(40, 60), + largeArc: largeArc, + clockwise: clockwise); + canvas.drawPath( + path, + SurfacePaintData() + ..strokeWidth = 2 + ..color = const Color(0x61000000) // black38 + ..style = PaintingStyle.stroke); +} diff --git a/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart new file mode 100644 index 0000000000000..012930f118263 --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart @@ -0,0 +1,81 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import '../screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUp(() async { + debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + tearDown(() { + for (final html.Node scene in html.document.querySelectorAll('flt-scene')) { + scene.remove(); + } + }); + + test('drawColor should cover entire viewport', () async { + const Rect region = Rect.fromLTWH(0, 0, 400, 400); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture testPicture = _drawTestPicture(region, useColor: true); + builder.addPicture(Offset.zero, testPicture); + await sceneScreenshot(builder, 'canvas_draw_color', region: region); + }, skip: true); // TODO(ferhat): matchGolden fails when a div covers viewport. + + test('drawPaint should cover entire viewport', () async { + const Rect region = Rect.fromLTWH(0, 0, 400, 400); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture testPicture = _drawTestPicture(region, useColor: false); + builder.addPicture(Offset.zero, testPicture); + await sceneScreenshot(builder, 'canvas_draw_paint', region: region); + }, skip: true); // TODO(ferhat): matchGolden fails when a div covers viewport.); +} + +Picture _drawTestPicture(Rect region, {bool useColor = false}) { + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + const Rect r = Rect.fromLTWH(0, 0, 200, 200); + final RecordingCanvas canvas = recorder.beginRecording(r); + + canvas.drawRect( + region.deflate(8.0), + Paint() as SurfacePaint + ..style = PaintingStyle.fill + ..color = const Color(0xFFE0E0E0) + ); + + canvas.transform(Matrix4.translationValues(50, 50, 0).storage); + + if (useColor) { + canvas.drawColor(const Color.fromRGBO(0, 255, 0, 1), BlendMode.srcOver); + } else { + canvas.drawPaint(Paint() as SurfacePaint + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(0, 0, 255, 1)); + } + + canvas.drawCircle( + Offset(r.width/2, r.height/2), r.width/2, + Paint() as SurfacePaint + ..style = PaintingStyle.fill + ..color = const Color.fromRGBO(255, 0, 0, 1)); + + return recorder.endRecording(); +} diff --git a/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart new file mode 100644 index 0000000000000..8c5808cf283bc --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart @@ -0,0 +1,752 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +import '../screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + }); + + setUpStableTestFonts(); + + test('Paints image', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.drawImage(createTestImage(), const Offset(0, 0), SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + test('Paints image with transform', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 4.0); + rc.drawImage(createTestImage(), const Offset(0, 0), SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image_with_transform', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + test('Paints image with transform and offset', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 4.0); + rc.drawImage(createTestImage(), const Offset(30, 20), SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image_with_transform_and_offset', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + test('Paints image with transform using destination', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 4.0); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image_rect_with_transform', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + test('Paints image with source and destination', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.drawImageRect( + testImage, + Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), + SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image_rect_with_source', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + test('Paints image with source and destination and round clip', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.save(); + rc.clipRRect(RRect.fromLTRBR( + 100, 30, 2 * testWidth, 2 * testHeight, const Radius.circular(16))); + rc.drawImageRect( + testImage, + Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), + SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image_rect_with_source_and_clip', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + test('Paints image with transform using source and destination', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + rc.translate(50.0, 100.0); + rc.rotate(math.pi / 6.0); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.drawImageRect( + testImage, + Rect.fromLTRB(testWidth / 2, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), + SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_image_rect_with_transform_source', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should draw on top of image not below. + test('Paints on top of image', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_on_image', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should below image not on top. + test('Paints below image', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_below_image', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should draw on top of image with clip rect. + test('Paints on top of image with clip rect', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.clipRect(const Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_on_image_clip_rect', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should draw on top of image with clip rect and transform. + test('Paints on top of image with clip rect with transform', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + // Rotate around center of circle. + rc.translate(100, 100); + rc.rotate(math.pi / 4.0); + rc.translate(-100, -100); + rc.clipRect(const Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_on_image_clip_rect_with_transform', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should draw on top of image with stack of clip rect and transforms. + test('Paints on top of image with clip rect with stack', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + // Rotate around center of circle. + rc.translate(100, 100); + rc.rotate(-math.pi / 4.0); + rc.save(); + rc.translate(-100, -100); + rc.clipRect(const Rect.fromLTRB(75, 75, 160, 160), ClipOp.intersect); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.restore(); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_on_image_clip_rect_with_stack', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should draw on top of image with clip rrect. + test('Paints on top of image with clip rrect', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + rc.clipRRect(RRect.fromLTRBR(75, 75, 160, 160, const Radius.circular(5))); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_on_image_clip_rrect', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/44845 + // Circle should draw on top of image with clip rrect. + test('Paints on top of image with clip path', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + final Path path = Path(); + // Triangle. + path.moveTo(118, 57); + path.lineTo(75, 160); + path.lineTo(160, 160); + rc.clipPath(path); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 30, 2 * testWidth, 2 * testHeight), SurfacePaint()); + rc.drawCircle( + const Offset(100, 100), + 50.0, + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color.fromARGB(128, 0, 0, 0)); + rc.restore(); + await canvasScreenshot(rc, 'draw_circle_on_image_clip_path', + region: const Rect.fromLTWH(0, 0, 500, 500)); + }); + + // Regression test for https://github.com/flutter/flutter/issues/53078 + // Verified that Text+Image+Text+Rect+Text composites correctly. + // Yellow text should be behind image and rectangle. + // Cyan text should be above everything. + test('Paints text above and below image', () async { + // Use a non-Ahem font so that text is visible. + debugEmulateFlutterTesterEnvironment = false; + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + final Image testImage = createTestImage(); + final double testWidth = testImage.width.toDouble(); + final double testHeight = testImage.height.toDouble(); + const Color orange = Color(0xFFFF9800); + final Paragraph paragraph1 = createTestParagraph( + 'Should be below below below below below', + color: orange); + paragraph1.layout(const ParagraphConstraints(width: 400.0)); + rc.drawParagraph(paragraph1, const Offset(20, 100)); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + const Rect.fromLTRB(100, 100, 200, 200), SurfacePaint()); + rc.drawRect( + const Rect.fromLTWH(50, 50, 100, 200), + SurfacePaint() + ..strokeWidth = 3 + ..color = const Color(0xA0000000)); + const Color cyan = Color(0xFF0097A7); + final Paragraph paragraph2 = createTestParagraph( + 'Should be above above above above above', + color: cyan); + paragraph2.layout(const ParagraphConstraints(width: 400.0)); + rc.drawParagraph(paragraph2, const Offset(20, 150)); + rc.restore(); + await canvasScreenshot( + rc, + 'draw_text_composite_order_below', + maxDiffRatePercent: 1.1, + region: const Rect.fromLTWH(0, 0, 350, 300), + ); + }); + + // Creates a picture + test('Paints nine slice image', () async { + const Rect region = Rect.fromLTWH(0, 0, 500, 500); + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + final Image testImage = createNineSliceImage(); + canvas.clipRect(const Rect.fromLTWH(0, 0, 420, 200)); + canvas.drawImageNine(testImage, const Rect.fromLTWH(20, 20, 20, 20), + const Rect.fromLTWH(20, 20, 400, 400), SurfacePaint()); + final Picture picture = recorder.endRecording(); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.addPicture(const Offset(0, 0), picture); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(builder.build().webOnlyRootElement!); + html.document.body!.append(sceneElement); + await matchGoldenFile('draw_nine_slice.png', + region: region, maxDiffRatePercent: 0); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + }); + + // Regression test for https://github.com/flutter/flutter/issues/78068 + // Tests for correct behavior when using drawImageNine with a destination + // size that is too small to render the center portion of the original image. + test('Paints nine slice image', () async { + const Rect region = Rect.fromLTWH(0, 0, 100, 100); + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final Canvas canvas = Canvas(recorder, region); + final Image testImage = createNineSliceImage(); + canvas.clipRect(const Rect.fromLTWH(0, 0, 100, 100)); + // The testImage is 60x60 and the center slice is 20x20 so the edges + // of the image are 40x40. Drawing into a destination that is smaller + // than that will not provide enough room to draw the center portion. + canvas.drawImageNine(testImage, const Rect.fromLTWH(20, 20, 20, 20), + const Rect.fromLTWH(20, 20, 36, 36), SurfacePaint()); + final Picture picture = recorder.endRecording(); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.addPicture(const Offset(0, 0), picture); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(builder.build().webOnlyRootElement!); + html.document.body!.append(sceneElement); + await matchGoldenFile('draw_nine_slice_empty_center.png', + region: region, maxDiffRatePercent: 0); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + }); + + // Regression test for https://github.com/flutter/flutter/issues/61691 + // + // The bug in bitmap_canvas.dart was that when we transformed and clipped + // the image we did not apply `transform-origin: 0 0 0` to the clipping + // element which resulted in an undesirable offset. + test('Paints clipped and transformed image', () async { + const Rect region = Rect.fromLTRB(0, 0, 60, 70); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.translate(10, 10); + canvas.transform(Matrix4.rotationZ(0.4).storage); + canvas.clipPath(Path() + ..moveTo(10, 10) + ..lineTo(50, 10) + ..lineTo(50, 30) + ..lineTo(10, 30) + ..close()); + canvas.drawImage(createNineSliceImage(), Offset.zero, SurfacePaint()); + await canvasScreenshot(canvas, 'draw_clipped_and_transformed_image', + region: region, maxDiffRatePercent: 1.0); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/61245 + test('Should render image with perspective', () async { + const Rect region = Rect.fromLTRB(0, 0, 200, 200); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.translate(10, 10); + canvas.drawImage(createTestImage(), const Offset(0, 0), SurfacePaint()); + final Matrix4 transform = Matrix4.identity() + ..setRotationY(0.8) + ..setEntry(3, 2, 0.0005); // perspective + canvas.transform(transform.storage); + canvas.drawImage(createTestImage(), const Offset(0, 100), SurfacePaint()); + await canvasScreenshot(canvas, 'draw_3d_image', + region: region, + maxDiffRatePercent: 6.0, + setupPerspective: true); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/61245 + test('Should render image with perspective inside clip area', () async { + const Rect region = Rect.fromLTRB(0, 0, 200, 200); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFE0E0E0)); + canvas.translate(10, 10); + canvas.drawImage(createTestImage(), const Offset(0, 0), SurfacePaint()); + final Matrix4 transform = Matrix4.identity() + ..setRotationY(0.8) + ..setEntry(3, 2, 0.0005); // perspective + canvas.transform(transform.storage); + canvas.clipRect(region, ClipOp.intersect); + canvas.drawRect(const Rect.fromLTWH(0, 0, 100, 200), SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawImage(createTestImage(), const Offset(0, 100), SurfacePaint()); + canvas.drawRect(const Rect.fromLTWH(50, 150, 50, 20), SurfacePaint()..color = const Color(0x80000000)); + await canvasScreenshot(canvas, 'draw_3d_image_clipped', + region: region, + maxDiffRatePercent: 5.0, + setupPerspective: true); + }); + + test('Should render rect with perspective transform', () async { + const Rect region = Rect.fromLTRB(0, 0, 400, 400); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFE0E0E0)); + canvas.translate(20, 20); + canvas.drawRect(const Rect.fromLTWH(0, 0, 100, 40), + SurfacePaint()..color = const Color(0xFF000000)); + final Matrix4 transform = Matrix4.identity() + ..setRotationY(0.8) + ..setEntry(3, 2, 0.001); // perspective + canvas.transform(transform.storage); + canvas.clipRect(region, ClipOp.intersect); + canvas.drawRect(const Rect.fromLTWH(0, 60, 120, 40), SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawRect(const Rect.fromLTWH(300, 250, 120, 40), SurfacePaint()..color = const Color(0x80E010E0)); + canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(0, 120, 160, 40), const Radius.circular(5)), + SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(300, 320, 90, 40), const Radius.circular(20)), + SurfacePaint()..color = const Color(0x80E010E0)); + await canvasScreenshot(canvas, 'draw_3d_rect_clipped', + region: region, + maxDiffRatePercent: 1.0, + setupPerspective: true); + }); + + test('Should render color and ovals with perspective transform', () async { + const Rect region = Rect.fromLTRB(0, 0, 400, 400); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFFF0000)); + canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); + canvas.translate(20, 20); + canvas.drawRect(const Rect.fromLTWH(0, 0, 100, 40), + SurfacePaint()..color = const Color(0xFF000000)); + final Matrix4 transform = Matrix4.identity() + ..setRotationY(0.8) + ..setEntry(3, 2, 0.001); // perspective + canvas.transform(transform.storage); + canvas.clipRect(region, ClipOp.intersect); + canvas.drawOval(const Rect.fromLTWH(0, 120, 130, 40), + SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawOval(const Rect.fromLTWH(300, 290, 90, 40), + SurfacePaint()..color = const Color(0x80E010E0)); + canvas.drawCircle(const Offset(60, 240), 50, SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawCircle(const Offset(360, 370), 30, SurfacePaint()..color = const Color(0x80E010E0)); + await canvasScreenshot(canvas, 'draw_3d_oval_clipped', + region: region, + maxDiffRatePercent: 1.0, + setupPerspective: true); + }); + + test('Should render path with perspective transform', () async { + const Rect region = Rect.fromLTRB(0, 0, 400, 400); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFFF0000)); + canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); + canvas.translate(20, 20); + canvas.drawRect(const Rect.fromLTWH(0, 0, 100, 20), + SurfacePaint()..color = const Color(0xFF000000)); + final Matrix4 transform = Matrix4.identity() + ..setRotationY(0.8) + ..setEntry(3, 2, 0.001); // perspective + canvas.transform(transform.storage); + canvas.drawRect(const Rect.fromLTWH(0, 120, 130, 40), + SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawOval(const Rect.fromLTWH(300, 290, 90, 40), + SurfacePaint()..color = const Color(0x80E010E0)); + final Path path = Path(); + path.moveTo(50, 50); + path.lineTo(100, 50); + path.lineTo(100, 100); + path.close(); + canvas.drawPath(path, SurfacePaint()..color = const Color(0x801080E0)); + + canvas.drawCircle(const Offset(50, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); + canvas.drawCircle(const Offset(100, 100), 4, SurfacePaint()..color = const Color(0xFF000000)); + canvas.drawCircle(const Offset(100, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); + await canvasScreenshot(canvas, 'draw_3d_path', + region: region, + maxDiffRatePercent: 1.0, + setupPerspective: true); + }); + + test('Should render path with perspective transform', () async { + const Rect region = Rect.fromLTRB(0, 0, 400, 400); + final RecordingCanvas canvas = RecordingCanvas(region); + canvas.drawRect(region, SurfacePaint()..color = const Color(0xFFFF0000)); + canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); + canvas.translate(20, 20); + canvas.drawRect(const Rect.fromLTWH(0, 0, 100, 20), + SurfacePaint()..color = const Color(0xFF000000)); + final Matrix4 transform = Matrix4.identity() + ..setRotationY(0.8) + ..setEntry(3, 2, 0.001); // perspective + canvas.transform(transform.storage); + //canvas.clipRect(region, ClipOp.intersect); + canvas.drawRect(const Rect.fromLTWH(0, 120, 130, 40), + SurfacePaint()..color = const Color(0x801080E0)); + canvas.drawOval(const Rect.fromLTWH(300, 290, 90, 40), + SurfacePaint()..color = const Color(0x80E010E0)); + final Path path = Path(); + path.moveTo(50, 50); + path.lineTo(100, 50); + path.lineTo(100, 100); + path.close(); + canvas.drawPath(path, SurfacePaint()..color = const Color(0x801080E0)); + + canvas.drawCircle(const Offset(50, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); + canvas.drawCircle(const Offset(100, 100), 4, SurfacePaint()..color = const Color(0xFF000000)); + canvas.drawCircle(const Offset(100, 50), 4, SurfacePaint()..color = const Color(0xFF000000)); + await canvasScreenshot(canvas, 'draw_3d_path_clipped', + region: region, + maxDiffRatePercent: 1.0, + setupPerspective: true); + }); +} + +// 9 slice test image that has a shiny/glass look. +const String base64ImageData = 'data:image/png;base64,iVBORw0KGgoAAAANSUh' + 'EUgAAADwAAAA8CAYAAAA6/NlyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPo' + 'AAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAApGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQA' + 'AARoABQAAAAEAAABKARsABQAAAAEAAABSATEAAgAAACAAAABah2kABAAAAAEAAAB6AAAAAAAA' + 'AEgAAAABAAAASAAAAAFBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpAAADoAEAAwAA' + 'AAEAAQAAoAIABAAAAAEAAAA8oAMABAAAAAEAAAA8AAAAAKgRPeEAAAAJcEhZcwAACxMAAAs' + 'TAQCanBgAAATqaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOn' + 'g9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+CiAgIDxyZGY6Uk' + 'RGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW' + '5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAg' + 'IHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICA' + 'gICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1J' + 'lc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlL' + 'mNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2' + 'JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmF' + 'kb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAua' + 'WlkOjMxRTc0MTc5ODQwQTExRUE5OEU4QUI4OTRCMjhDRUE3PC94bXBNTTpJbnN0YW5jZUl' + 'EPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjMxRTc0MTdBODQwQTExR' + 'UE5OEU4QUI4OTRCMjhDRUE3PC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU0' + '6RGVyaXZlZEZyb20gcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICA8c' + '3RSZWY6aW5zdGFuY2VJRD54bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4MjJBQUI1NDhBQTA' + 'zMDNBPC9zdFJlZjppbnN0YW5jZUlEPgogICAgICAgICAgICA8c3RSZWY6ZG9jdW1lbnRJR' + 'D54bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4MjJBQUI1NDhBQTAzMDNBPC9zdFJlZjpkb2N' + '1bWVudElEPgogICAgICAgICA8L3htcE1NOkRlcml2ZWRGcm9tPgogICAgICAgICA8ZXhpZ' + 'jpQaXhlbFlEaW1lbnNpb24+NjA8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8' + 'ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl' + '4ZWxYRGltZW5zaW9uPjYwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPHhtcD' + 'pDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpPC94bXA6Q3Jl' + 'YXRvclRvb2w+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YX' + 'Rpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZ' + 'XRhPgpq1fpCAAAUDUlEQVRoBd1beYxd11n/3fvu2+fNm9VbPB6PHS+ldt0kNKWJk0YkaVJS' + 'UtRQhFCqSggRCVWgSKUKEgi1FNSKlvIH/1dVoWqatBKkUYqgAVRaNSTN0jg2buIkE4/XmbF' + 'ne/O2u/D7ffedN2/GM9hJCRAf+76z3LN8+/nuOd94jUYjwRWmJFnp6nkeVI+jCJ7vw+Mc9l' + 'Z9+M7qLK+MYHPPOmrvjulp17yunxvr+inXWmvh6Bl+2WJw2R7qwJUFehLHiIUEH58LWxIAa' + 'RerOoBi1Qi8S71AujaXW69O314k3XvXprURhrZ+ijyJ45HYIlLPWm7cevkVIUw+GieThFzN' + 'pAt0EUj0LiYQRNFBxnISs2L/00YRa6NkwHYAdsCLoBqpx7V5WjuTWZEMrat5faIvBgjxjRb' + 'ptG+IsKjJ6ToU5UTZLFdPENaXUL+4gNqFi1iuMT8/jcb8AtqkfBxHQNREVG8CzTaLbbaJGJ' + 'KKlCApQCkRCCHXELycvyMx2VyWawUI8jnEhQIJnEEuk0U2F6AyOopCdQil/ipKQwMsV+GrH' + '9UqabdJjAAZzuWLABukdREWgB5hSjSRR4py0fq5aUwdfxFTzzyDsz9+FrOPfR9NLIIoot2Z' + 'XMsIAeUSeL1zybWpvaMMxr2QdY1RUh89SkYI5qobl5ln+JDsKO89hNFbb8Tm91+P7e+9Hps' + 'm9qBQLiNutZAE0vFUCtn1kuStZ7SEcGgUyyBi/tpTT+HFb3wTk1/7KuqcQgBnc2UEWzfD6+' + '9HJiBlCZokQhSWCIbkaIbzrKRU3AwBa+6gwQaSl91Y55iI3JFeKgkOgo6oIyVeo4XW0hzCU1' + 'MkdkqUYQxj3xc+jet+5R4M7tlt3M6SST6ZtF5ajTAXSAhs1A4RZTy0KarP/O3f4Sd/+Gksc3' + 'Rx/FrkByrItglio4FQFK0vI9PiuM4/P6KeCxjqcIa8T/wM5yTYFAOhLNSka8SG/YigyRgBZ' + 'LNPmfBi9ucEIl7skf8Udb2LcszzeVI6i7DAvEBRZvvykRfQYr7pPdfjji98Hrs+eJidfWS' + 'yGseRtiA7dNIqhEXRiIaACoX6wgU8+eW/xgtf+kuUt4wjPzyIzMISsFgzg5TJS9cyCKg3YC' + '6xFzdowmwNQ81LBVPlkDoiQiipn9W4XtBtVRvnYJtgpDLxl/NJ9yU1VK+wSQvdjii6DUQt2g' + 'zCkAz2o03k54++aCL/kUe+hQN3302KUZcFX8c2aF2lVQgL2TBu09408fRX/gY//LPPoXLgAI' + 'pL5OTcIjIUt6BYQEA9aS/MIbk4Y3oqJkmAhJ7AdA+LloSA3qdcTHVbZSVxx6UURVdL+7ua7I' + 'HGxPz1N29DQm5HFHEZSEljuGkAzZ+d5HzL+NgTj2P/bXcgCVuENTAVc/N0ERZ3k4hL+hGe' + 'feQRPPHJ30bfwQMoTC8gaDSJaAl+u4Vw5hS5QvHGZhQPvxulrVuR3TSKbLmIbDGPDAHJUI' + 'yzfLiaGT0ZvtinFSWnZJGFmCVxU1RSYqNHNUgS8pY6G4qjXC+hpW8vN9BcmEfz7HnUJ09h6' + 'cWnTIdt3NZxtGln2st1tLaOoH7iZTNsn3j+WWzet5/UpS0hHC51EY6ikJIc4OzRo/jmDdcDW' + '8ZRlvI3G0SmBO/kKzbRtt/8HYzdfhjl8TEUaLBy+QK8XJ6IkPJEJvYFuf6rLN6yxP3bF/9FVN' + 'HUkDahVsVgERH06Fc7hDp6FEuVI4q1CBHRZoRUqcbsBUwdewlnHv4Ozr3wY2B4K9qUunB+Ccn' + 'EGM4fO4J9n/wEPvqlv0Kxr2xwS02VDGFxty0LWW/gH//i8zjy5a9g9F0H4J2aRXagD94bL2P' + 'gF2/FdZ96AJsOXY+gr4iWWBNSwORgaPNn0jwCPwV8bSFFTP0um9h1ZR4irYp++Lj9Wos0Z8/' + 'jlSe+h6N//lkkpVE0Kjk0l+po7tiGaSJ936OP4roP30N1Ipwdq20Ia+OOyd3Xf/IMvn34ZpR' + '370d2oYY8N/XM1AmM3H433vfQZ9A3MU7Pjtok4yEgmDLCziCy6srPem0rby9T4qRdqnW6SjS' + '6RXJcHCvSIaERO/kvT+KpT/0eGpu3o91oo1XKYe7MSQzdchPu//o3UN60CVnBw8f4LP5EyzW' + '89Njjphs5ikeWRPDZVkIfbvj9P0Dfrp2o1SgyNAQJrV9Cj8jn9uDTG5LeasvQk2GbPTkSa9WT' + 'RcAxAdvWe1b37dgCN5fNW7C1wPF+Nk8YfLTqNdQpmdtvvxPv+tPPwj83hWy1j/ksqvt/Aad/' + '8COcePbZVNU6BAu0X3o0MLOnT+O1L34RA9vGgLMzFOV+RJOvYNdDf4zKnmvRpEUsFqgPpKwM' + 'SkxDIXeuVadehU3zcvw2jY30je9lLELqnSRfap0ktBHGto5odLglQybeaf+XcZMPrZ3EV5kup' + 'lW05Yl4lDgvTycnyCMfpMRLWnRhucbE7bfh1D/cjOnnfojc5jE6KEvgbo3nHvt77D98C/J9VE' + '3CRjOq7d/HGz990RzFUb7IzNW57yU2YHTvPhLEQ5YTty7OYuH0edROT2HxzHksT0/TanNrWp' + 'xDe47cX2zS11623FukJCR8QPE357PWQXF1JvRTYSUHUWaN+zslIdNHJ7JShkfxDCpFeCznRk' + 'eQHxlFfvMI+rdsQ9+27SiMDFm/fHUE2266CTNEOEvCtJeX0Ld1DCe/+jVMPfAA9hy6zvyHQK' + 'Itakz++w8ovtRJcjJDKnr1OnJEeenlE4j4YbA4OYmZ53+Kucef4t57qrt/Sie0RyoJcO23ZA' + 'vLFD0CH5M7Cbnho2rt9lqdzNCZL9SxwtSvkO5YXIe45l2gpFxIZ' + '+6VCdNBDtdGk9/3Pgx/4L0YPXgQxf4BxPQM5WuH3MpydNKSoQriM8Dkc89j97sPmsGjfPhYmj' + 'mLcw8/TiM1iICdNalPDicDI3j16w8jnJqhDz1tiGWE0vZxUrFIv5nOIMXEo5RojMRX24dhTgd' + 'eX1zypyW06cceO61JtLv2jjuQ9dIuZS3K9dCpIOrpO9vCKJE0nK1aDc3jT2ORz0nOWdq6D+0' + 'cR1a2GNPkoVFfDa43/uMZ1D92H8qVCrzlZjM5+q//hu/e9SEM7NqNwlwNOQJugAj2ptx0cmKE' + 'nfWJSD2FfGhZa+k/ERK3TCyJtRx/NbHRxqm0cXJ9lKcIC71VqbdK5ZauewFdWT6g6CbcqCOq' + 'UXxhiW2UzCLbOEEzDtEqF7AwfQFLYQ2/+9JRXLN7F4KQYnTu1RO2TBDkKJ+LhDoVnFgISI8' + 'oBUmDulhr2GQihv6TiUysSWytlKKZtkpuV5J7v9KikuvjciG9Ura+a6pqk1GM6SiBHqDsix' + 'm3kQF4VAUdPEgyJH0+9+T87u04f/w4zk9NYsvuCaoNnY25o/9psi+vxp1U2MRCih/1ssgxRV' + 'WeU8JHhDAR5Hvxtlvu1CWAaut9tG/31n+uMtcXHInEXfPSxoDISuJc0geKT8Low1X6fvZnL5' + 'sP4dcWFzFNdpO39JWlv5yB/7splc9utbcgz8rkd02flWV7e7/9ZVu3A7syfiSa/y+Ep0+8h' + 'naL3wR1OuW1J/8J5YBWtNmyTdph3MV7DUIOdHsv+VHq9LncmLTz2/TrQOnAI//eq9Vt95k5' + 'dgx17kZ+/eK8MdTbOgyPCK/i7tsE19s9rSO6T5H36AbnCv1Y+NHT/MZfhL8wP297aoZfPT6' + '3pKspybhm6GsHg1V+Zc3y0HERwdJSymFRQ6ZW1s1R6J2MvFxWJV9+An1xmrVUpBsLC7YRyD' + 'lQuhqQNTy0XxtGxEkWneXaInUYy/R9WdnALnWGvDOzlIVyQ7lFEYUGz+T8Fo9GVPG43zqKv' + 'DPRuxRq4SM+K5f/0tYu1KYVU0VOR/d8ifWrIUmP0zPu9CRVx1h+yA96S5JpfYhepcmJN7+3' + '13FWr0Kknbr6Olq1REvmDuOuJnx1iZd+oBIrougnvJW7WpPEWB8yxNl2oozO4YKgkH6B6' + 'VqMMSZedj6HDRL/EMK1Xoq9VgEV2e8gm7YqmEIMtjD6WYp4q6W9WxTIo6aSPyXElyJtD1de' + 'PWtrv3byV3c77JsYYTz6o1PF/q5+VfP48vWYm4KWeJsB3h2P6Vyv2Vzt8Lj3ltbOhtu9J5Nu' + 'r3VmhnBxHEqT27wKNbHsaXijzbrvJCm6skPPtJdA6s9D8JaTrj/8mvpDWiUQ7jeeQOvAdFHl' + 'f55SJv8m+5mZfbPMsyhFNavhmKOvr0jlH553lEITf+rVLL4kt4gC/Xqn9iAkXdgOapyKXde' + 'wIVmfIzqZJHHqTXaeSYi7vHu2oredfeqTKFjvJ1CE/H+4Lb/rpGaf5V63XA4cdMwlQfQ7qU' + 'LZlsIkQhqriI6VEK6OjdNg8bYiy7uh4vgOzLOR0Qr8Hu6cMffI9XqUXk0OrdKTCJCS/XbKPW' + '+vqNg7f+9J7aWD2ZP/hapbTwRSBEEcMHwjatshXmnnNt5pMToox7vd/p3j4AE974dChDwICH' + 'gIL0vtPqJF3bVxVu72sBcAfYAouXEa48rpmyv/dWM1pYU89Qx163SbiK04re8iJ2G6IIh5R' + 'h2dImaVKoZ4NROQ2zzSDVBlCEFmUxWtmQWGD+iMORUNi9USsppZTb2Prdbb4Lb3NE/dOQL' + 'AwQY0+7u2NBeQnXcu5wLuncRT45TU1ru4DuN7m9z8UhuVI74nrS0sohW3UPrQHRgYHGa8V6' + 'B7Yg+VkWEUb72LFwoXKAZB96xZYuGe9HaPQAgQW9Atqjxti7WQTheUs9mdReuGSO9c3eVS' + 'IYmry1172ldzpHOZlLCsddJHc6fv3Fpaz82j4xydnbfZh2E4GDnIOJUKr1HpSfoBbxWK1' + 'KKNxwy6oQy4zzUtkGUnbjzRMwveQRshygmSoZAR3/Y7g7bDZGefqZfHOsQdHnaznFr+ro1' + 'uvkaWLowchxjewymNnfYBu+PtdFu2stwK7qVijgKsgzry+eKGL12L86igIhnXHGO1lp3uy' + 'Y3HLFhkiymfpm68j+XdD/M1eCSvXAV5j39LxnX02394trJ0l5SPr0JeXqjWK7WuTMofeTDG' + 'LyG16q6jKf08nM4QIk36iOMxqnc/xsI52bQYrBKm8FmERUw1UHd/W3wEFpxSAtJPJVbvdN' + '9d62dfpfMs712SgnLPKOHWwar3JIfrYTRg82Y7R40SZV2sywh/7BIV6g826F6qbDeeR4E5e' + 'rVjB00wcs5KHNOK2Yoq29zMSL3I50t0R0bPJVeSqC6bv/rbIIm65lcNH6RXrYpliz9kAJjT' + 'cmkf2lG7HlwCEUc4ot40U7CcCditsQvZH+fBmje/bi9Md/HfVHHkXmGsY/zTUQkHLupNr7f' + '3bo1dk/bEvSNiaDGRLGNiP0xN2xe+/F8PAoSgqmoxcp48eQKSJM2S4WixjkCf3YnXebwVI8' + 'FDWd5l2UpMUU9Yh8Wk7F14mSxOmyj8Tuv3s2msPGCIbV4yPqjcGinC8l4gpQi4YraJycRPH' + 'u7Cd3O1jjFmRCGfMi5RUE2FVioxXLPNrYnRiJzY9+CAWF2YQ9pWIPENEOaFy3YFbmYuEsUSe' + 'Wxj3git5Qo3Z4Iloztefg3rKdwqUSccShs4cEY1lyMjUFoNR236W8doeWoMlLE/xI4jE2Xnf' + 'r6I6NESEKwz8YfgFtyRjLt9ZIS+xrvRjqbKI8cO3YplUmnv0O8iN74Q/v8xbt4YFbsu8amB' + 'qxVlTiM6VpMt16xj71VOlQmsxWRIhCarrR5ui9sjur+maDJQZ2dvkzf8CJj73JxjZuxvV/g' + 'rKjKPuPai070EhoEi1EsW6f6CKBm/Wd37013Bsjlep//x9ZHdsZ1QNY6Pma3bJbArCMRrH' + '7T81zbIITGpJk9OwtIVbZ' + '/dNp0M3036d0iP91QvNopo9Ukg3P8v2wWAv6CuXaIz4TR9OMoaaXtXYZx7EjusY7FIYRJkI' + 'Z/XBQJV1qYuwsZsvyuU+9FWXMRBuw+77fwuvV4ex8O1vGY4Bg1kUkp8wfkLy7dH39hW84hE' + 'KKrhCfB2atgDFz074VaFYbpgkMcJI/V1im7Z4kyb9rQPjtuRTh5QoT1sMjVDCLTVcpN/w2qt' + 'kvI+df/QQdrz/RnJ2AGXaozIZmMsytkvi3Jm3i7omli6X+PU0PDBkccnJNWMIPn4fzk3swpn' + 'vfRf1I0eM4hokAvtFXqLn6aRkeDhGZ0VzWFwIraVO/HVzJ7ETB21AZ9G1meGr+WR9mDS3mC' + 'pLpQhbyi2LJDJvDqKFOkMw0vsw9VaYUumee3HNbb/M6NkJDPDIapB/DzHAs7oU2RWZ05Tda' + 'Fr3nRuRS8u8YJtnZMDsLAPReJ1aUxzI1CnMv/o65l9/A0tnX0Vy/gKi4ycMOIePANAjgJ' + 'cu8pO9VTWe0dxV1/bpnb10TjldirDPO4roHDttciMbEFpxziqu8YxvHMC5aFhcpTMoqEaH' + 'BxEf9+KsZLBcqmLsBqEtE4JZN6XGZi2xNjKixcY9sNQgTrDl5o892rTMLQajHHk+5CE8di' + 'fDKCVVXgvDwLptJiOcT7N5XTXglLdqmtQ1l+h6EqTYmFjJR0KZc5QlBWhY5F5BjP70bgqnD' + 'nPHSVH9zHPrbNEH6LET0HZn37+xUu5xG8DSqp0V7D0ItwVacEikdSjjjJgoqwuGvXNXGOg' + 'Z2x0yEXi0PeqFO87AiFGCkemvOKYkSF/6yS1vnLOWTaHN/VcmnSWt0eHThELBHBiCGisGra' + 'SI5l6R3qD0q05ZR4TNVHES7bnltEgcg6JF3uVlyFsBpdB+lzgdRTMHeejneZR0H1BrnKECH' + '7GyVy1BAmcsr17WxYs78QNqQF4bppFXqX9BBqIrzmcExwueASjAFzlaWncpqEpJCXVc7wv' + 'Nj7eT/BbztCaofk+k0AAAAAElFTkSuQmCC'; + +HtmlImage createNineSliceImage() { + return HtmlImage( + html.ImageElement()..src = base64ImageData, + 60, + 60, + ); +} + +HtmlImage createTestImage({int width = 100, int height = 50}) { + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(33, 0, 33, 50); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(66, 0, 33, 50); + ctx.fill(); + final html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; + return HtmlImage(imageElement, width, height); +} + +Paragraph createTestParagraph(String text, + {Color color = const Color(0xFF000000)}) { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Roboto', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 14.0, + )); + builder.pushStyle(TextStyle(color: color)); + builder.addText(text); + return builder.build(); +} diff --git a/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart new file mode 100644 index 0000000000000..ea43649b33397 --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart @@ -0,0 +1,149 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import '../screenshot.dart'; + +const Rect region = Rect.fromLTWH(0, 0, 500, 100); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +SurfacePaint makePaint() => Paint() as SurfacePaint; + +Future testMain() async { + setUp(() async { + debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + group('Add picture to scene', () { + test('draw growing picture across frames', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + + _drawTestPicture(builder, 100, false); + builder.pop(); + + final html.Element elm1 = builder + .build() + .webOnlyRootElement!; + html.document.body!.append(elm1); + + // Now draw picture again but at larger size. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + // Now draw the picture at original target size, which will use a + // different code path that should normally not have width/height set + // on image element. + _drawTestPicture(builder2, 20, false); + builder2.pop(); + + elm1.remove(); + await sceneScreenshot(builder2, 'canvas_draw_picture_acrossframes', + region: region); + }); + + test('draw growing picture across frames clipped', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + + _drawTestPicture(builder, 100, true); + builder.pop(); + + final html.Element elm1 = builder + .build() + .webOnlyRootElement!; + html.document.body!.append(elm1); + + // Now draw picture again but at larger size. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + _drawTestPicture(builder2, 20, true); + builder2.pop(); + + elm1.remove(); + await sceneScreenshot(builder2, 'canvas_draw_picture_acrossframes_clipped', + region: region); + }); + + test('PictureInPicture', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture greenRectPicture = _drawGreenRectIntoPicture(); + + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); + canvas.drawPicture(greenRectPicture); + builder.addPicture(const Offset(10, 10), recorder.endRecording()); + + await sceneScreenshot(builder, 'canvas_draw_picture_in_picture_rect', + region: region); + }); + }); +} + +HtmlImage? sharedImage; + +void _drawTestPicture(SceneBuilder builder, double targetSize, bool clipped) { + sharedImage ??= _createRealTestImage(); + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); + canvas.debugEnforceArbitraryPaint(); + if (clipped) { + canvas.clipRRect( + RRect.fromLTRBR(0, 0, targetSize, targetSize, const Radius.circular(4))); + } + canvas.drawImageRect(sharedImage!, const Rect.fromLTWH(0, 0, 20, 20), + Rect.fromLTWH(0, 0, targetSize, targetSize), makePaint()); + final Picture picture = recorder.endRecording(); + builder.addPicture( + Offset.zero, + picture, + ); +} + +Picture _drawGreenRectIntoPicture() { + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); + canvas.drawRect(const Rect.fromLTWH(20, 20, 50, 50), + makePaint()..color = const Color(0xFF00FF00)); + return recorder.endRecording(); +} + +typedef PaintCallback = void Function(RecordingCanvas canvas); + +const String _base64Encoded20x20TestImage = + 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' + 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' + 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; + +HtmlImage _createRealTestImage() { + return HtmlImage( + html.ImageElement() + ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + 20, + 20, + ); +} diff --git a/lib/web_ui/test/html/drawing/canvas_draw_points_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_points_golden_test.dart new file mode 100644 index 0000000000000..f871e389cf752 --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_draw_points_golden_test.dart @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 400, 600); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws points in all 3 modes', () async { + final SurfacePaintData paint = SurfacePaintData(); + paint.strokeWidth = 2.0; + paint.color = const Color(0xFF0000FF); + final Float32List points = offsetListToFloat32List(const [ + Offset(10, 10), + Offset(50, 10), + Offset(70, 70), + Offset(170, 70) + ]); + canvas.drawPoints(PointMode.points, points, paint); + final Float32List points2 = offsetListToFloat32List(const [ + Offset(10, 110), + Offset(50, 110), + Offset(70, 170), + Offset(170, 170) + ]); + canvas.drawPoints(PointMode.lines, points2, paint); + final Float32List points3 = offsetListToFloat32List(const [ + Offset(10, 210), + Offset(50, 210), + Offset(70, 270), + Offset(170, 270) + ]); + canvas.drawPoints(PointMode.polygon, points3, paint); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_draw_points.png', region: region); + }); + + test('Should draw points with strokeWidth', () async { + final SurfacePaintData nullStrokePaint = + SurfacePaintData()..color = const Color(0xffff0000); + canvas.drawPoints(PointMode.lines, Float32List.fromList([ + 30.0, 20.0, 200.0, 20.0]), nullStrokePaint); + final SurfacePaintData strokePaint1 = SurfacePaintData() + ..strokeWidth = 1.0 + ..color = const Color(0xff0000ff); + canvas.drawPoints(PointMode.lines, Float32List.fromList([ + 30.0, 30.0, 200.0, 30.0]), strokePaint1); + final SurfacePaintData strokePaint3 = SurfacePaintData() + ..strokeWidth = 3.0 + ..color = const Color(0xff00a000); + canvas.drawPoints(PointMode.lines, Float32List.fromList([ + 30.0, 40.0, 200.0, 40.0]), strokePaint3); + canvas.drawPoints(PointMode.points, Float32List.fromList([ + 30.0, 50.0, 40.0, 50.0, 50.0, 50.0]), strokePaint3); + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_draw_points_stroke.png', region: region); + }); +} diff --git a/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart new file mode 100644 index 0000000000000..349b83c65f8f9 --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 300, 300); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws lines with varying strokeWidth', () async { + + paintLines(canvas); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_lines_thickness.png', region: region); + }); +} + +void paintLines(BitmapCanvas canvas) { + final SurfacePaintData nullPaint = SurfacePaintData() + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + final SurfacePaintData paint1 = SurfacePaintData() + ..color = const Color(0xFF9E9E9E) // Colors.grey + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + final SurfacePaintData paint2 = SurfacePaintData() + ..color = const Color(0x7fff0000) + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + final SurfacePaintData paint3 = SurfacePaintData() + ..color = const Color(0xFF4CAF50) //Colors.green + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + // Draw markers around 100x100 box + canvas.drawLine(const Offset(50, 40), const Offset(148, 40), nullPaint); + canvas.drawLine(const Offset(50, 50), const Offset(52, 50), paint1); + canvas.drawLine(const Offset(150, 50), const Offset(148, 50), paint1); + canvas.drawLine(const Offset(50, 150), const Offset(52, 150), paint1); + canvas.drawLine(const Offset(150, 150), const Offset(148, 150), paint1); + // Draw diagonal + canvas.drawLine(const Offset(50, 50), const Offset(150, 150), paint2); + // Draw horizontal + paint3.strokeWidth = 1.0; + paint3.color = const Color(0xFFFF0000); + canvas.drawLine(const Offset(50, 55), const Offset(150, 55), paint3); + paint3.strokeWidth = 2.0; + paint3.color = const Color(0xFF2196F3); // Colors.blue; + canvas.drawLine(const Offset(50, 60), const Offset(150, 60), paint3); + paint3.strokeWidth = 4.0; + paint3.color = const Color(0xFFFF9800); // Colors.orange; + canvas.drawLine(const Offset(50, 70), const Offset(150, 70), paint3); +} diff --git a/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart new file mode 100644 index 0000000000000..fb81bfd6fa36f --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 150, 420); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws rect with flipped coordinates L > R, T > B', () async { + + paintRects(canvas); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_rect_flipped.png', region: region); + }); +} + +void paintRects(BitmapCanvas canvas) { + + canvas.drawRect(const Rect.fromLTRB(30, 40, 100, 50), + SurfacePaintData() + ..color = const Color(0xFF4CAF50) //Colors.green + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke); + + // swap left and right. + canvas.drawRect(const Rect.fromLTRB(100, 150, 30, 140), + SurfacePaintData() + ..color = const Color(0xFFF44336) //Colors.red + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke); + + // Repeat above for fill + canvas.drawRect(const Rect.fromLTRB(30, 240, 100, 250), + SurfacePaintData() + ..color = const Color(0xFF4CAF50) //Colors.green + ..style = PaintingStyle.fill); + + // swap left and right. + canvas.drawRect(const Rect.fromLTRB(100, 350, 30, 340), + SurfacePaintData() + ..color = const Color(0xFFF44336) //Colors.red + ..style = PaintingStyle.fill); +} diff --git a/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart new file mode 100644 index 0000000000000..f7d6d437a1daf --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart @@ -0,0 +1,106 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(8, 8, 500, 100); // Compensate for old scuba tester padding + + late BitmapCanvas canvas; + + final SurfacePaintData niceRRectPaint = SurfacePaintData() + ..color = const Color.fromRGBO(250, 186, 218, 1.0) // #fabada + ..style = PaintingStyle.fill; + + // Some values to see how the algo behaves as radius get absurdly large + const List rRectRadii = [0, 10, 20, 80, 8000]; + + const Radius someFixedRadius = Radius.circular(10); + + setUp(() { + canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 500, 100), + RenderStrategy()); + canvas.translate(10, 10); // Center + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('round square with big (equal) radius ends up as a circle', () async { + for (int i = 0; i < 5; i++) { + canvas.drawRRect( + RRect.fromRectAndRadius(Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), + Radius.circular(rRectRadii[i])), + niceRRectPaint); + } + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_rrect_round_square.png', region: region); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/62631 + test('round square with flipped left/right coordinates', () async { + canvas.translate(35, 320); + canvas.drawRRect( + RRect.fromRectAndRadius( + const Rect.fromLTRB(-30, -100, 30, -300), + const Radius.circular(30)), + niceRRectPaint); + canvas.drawPath(Path()..moveTo(0, 0)..lineTo(20, 0), niceRRectPaint); + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_rrect_flipped.png', + region: const Rect.fromLTWH(0, 0, 100, 200)); + }); + + test('round rect with big radius scale down smaller radius', () async { + for (int i = 0; i < 5; i++) { + final Radius growingRadius = Radius.circular(rRectRadii[i]); + final RRect rrect = RRect.fromRectAndCorners( + Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), + bottomRight: someFixedRadius, + topRight: growingRadius, + bottomLeft: growingRadius); + + canvas.drawRRect(rrect, niceRRectPaint); + } + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_rrect_overlapping_radius.png', region: region); + }); + + test('diff round rect with big radius scale down smaller radius', () async { + for (int i = 0; i < 5; i++) { + final Radius growingRadius = Radius.circular(rRectRadii[i]); + final RRect outerRRect = RRect.fromRectAndCorners( + Rect.fromLTWH(100 * i.toDouble(), 0, 80, 80), + bottomRight: someFixedRadius, + topRight: growingRadius, + bottomLeft: growingRadius); + + // Inner is half of outer, but offset a little so it looks nicer + final RRect innerRRect = RRect.fromRectAndCorners( + Rect.fromLTWH(100 * i.toDouble() + 5, 5, 40, 40), + bottomRight: someFixedRadius / 2, + topRight: growingRadius / 2, + bottomLeft: growingRadius / 2); + + canvas.drawDRRect(outerRRect, innerRRect, niceRRectPaint); + } + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_drrect_overlapping_radius.png', region: region); + }); +} diff --git a/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart new file mode 100644 index 0000000000000..6eff9c39923b5 --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart @@ -0,0 +1,76 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 300, 300); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws stroke joins', () async { + + paintStrokeJoins(canvas); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_stroke_joins.png', region: region); + }); + +} + +void paintStrokeJoins(BitmapCanvas canvas) { + canvas.drawRect(const Rect.fromLTRB(0, 0, 300, 300), + SurfacePaintData() + ..color = const Color(0xFFFFFFFF) + ..style = PaintingStyle.fill); // white + + Offset start = const Offset(20, 10); + Offset mid = const Offset(120, 10); + Offset end = const Offset(120, 20); + + final List strokeCaps = [StrokeCap.butt, StrokeCap.round, StrokeCap.square]; + for (final StrokeCap cap in strokeCaps) { + final List joints = [StrokeJoin.miter, StrokeJoin.bevel, StrokeJoin.round]; + const List colors = [ + Color(0xFFF44336), Color(0xFF4CAF50), Color(0xFF2196F3)]; // red, green, blue + for (int i = 0; i < joints.length; i++) { + final StrokeJoin join = joints[i]; + final Color color = colors[i % colors.length]; + + final Path path = Path(); + path.moveTo(start.dx, start.dy); + path.lineTo(mid.dx, mid.dy); + path.lineTo(end.dx, end.dy); + canvas.drawPath(path, SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 4 + ..color = color + ..strokeJoin = join + ..strokeCap = cap); + + start = start.translate(0, 20); + mid = mid.translate(0, 20); + end = end.translate(0, 20); + } + } +} diff --git a/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart new file mode 100644 index 0000000000000..9339bcadba636 --- /dev/null +++ b/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 300, 300); + + late BitmapCanvas canvas; + + setUp(() { + canvas = BitmapCanvas(region, RenderStrategy()); + }); + + tearDown(() { + canvas.rootElement.remove(); + }); + + test('draws rects side by side with fill and stroke', () async { + + paintSideBySideRects(canvas); + + html.document.body!.append(canvas.rootElement); + await matchGoldenFile('canvas_stroke_rects.png', region: region); + }); + +} + +void paintSideBySideRects(BitmapCanvas canvas) { + canvas.drawRect(const Rect.fromLTRB(0, 0, 300, 300), + SurfacePaintData() + ..color = const Color(0xFFFFFFFF) + ..style = PaintingStyle.fill); // white + + canvas.drawRect(const Rect.fromLTRB(0, 20, 40, 60), + SurfacePaintData() + ..style = PaintingStyle.fill + ..color = const Color(0x7f0000ff)); + canvas.drawRect(const Rect.fromLTRB(40, 20, 80, 60), + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 4 + ..color = const Color(0x7fff0000)); + + // Rotate 30 degrees (in rad: deg*pi/180) + canvas.transform(Matrix4.rotationZ(30.0 * math.pi / 180.0).storage); + + canvas.drawRect(const Rect.fromLTRB(100, 60, 140, 100), + SurfacePaintData() + ..style = PaintingStyle.fill + ..color = const Color(0x7fff00ff)); + canvas.drawRect(const Rect.fromLTRB(140, 60, 180, 100), + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 4 + ..color = const Color(0x7fffff00)); +} diff --git a/lib/web_ui/test/golden_tests/engine/conic_golden_test.dart b/lib/web_ui/test/html/drawing/conic_golden_test.dart similarity index 89% rename from lib/web_ui/test/golden_tests/engine/conic_golden_test.dart rename to lib/web_ui/test/html/drawing/conic_golden_test.dart index 9362cccebd3b9..ce028b64dd2fb 100644 --- a/lib/web_ui/test/golden_tests/engine/conic_golden_test.dart +++ b/lib/web_ui/test/html/drawing/conic_golden_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; @@ -16,21 +15,22 @@ void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { - final Rect region = Rect.fromLTWH(8, 8, 600, 800); // Compensate for old scuba tester padding +Future testMain() async { + const Rect region = Rect.fromLTWH(8, 8, 600, 800); // Compensate for old scuba tester padding Future testPath(Path path, String scubaFileName) async { const Rect canvasBounds = Rect.fromLTWH(0, 0, 600, 800); - final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds); + final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds, + RenderStrategy()); final RecordingCanvas canvas = RecordingCanvas(canvasBounds); - Paint paint = Paint() + SurfacePaint paint = SurfacePaint() ..color = const Color(0x7F7F7F7F) ..style = PaintingStyle.fill; canvas.drawPath(path, paint); - paint = Paint() + paint = SurfacePaint() ..strokeWidth = 2.0 ..color = const Color(0xFF7F007F) ..style = PaintingStyle.stroke; @@ -38,7 +38,7 @@ void testMain() async { canvas.drawPath(path, paint); canvas.endRecording(); - html.document.body.append(bitmapCanvas.rootElement); + html.document.body!.append(bitmapCanvas.rootElement); canvas.apply(bitmapCanvas, canvasBounds); await matchGoldenFile('$scubaFileName.png', region: region); bitmapCanvas.rootElement.remove(); diff --git a/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart b/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart new file mode 100644 index 0000000000000..6e7faae60c2cc --- /dev/null +++ b/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart @@ -0,0 +1,429 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:js_util' as js_util; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle, ImageShader; + +import 'package:web_engine_tester/golden_tester.dart'; + +import '../../common.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(RecordingCanvas rc, String fileName, + {Rect region = const Rect.fromLTWH(0, 0, 500, 500), + double maxDiffRatePercent = 0.0, + bool write = false}) async { + final EngineCanvas engineCanvas = + BitmapCanvas(screenRect, RenderStrategy()); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + await matchGoldenFile( + '$fileName.png', + region: region, + write: write, + maxDiffRatePercent: maxDiffRatePercent, + ); + } finally { + // The page is reused across tests, so remove the element after taking the + // golden screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + disposeWebGl(); + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + Future _testVertices( + String fileName, Vertices vertices, BlendMode blendMode, Paint paint, + {bool write = false}) async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + rc.drawVertices( + vertices as SurfaceVertices, blendMode, paint as SurfacePaint); + await _checkScreenshot(rc, fileName, write: write); + } + + test('Should draw green hairline triangles when colors array is null.', + () async { + final Vertices vertices = Vertices.raw( + VertexMode.triangles, + Float32List.fromList([ + 20.0, + 20.0, + 220.0, + 10.0, + 110.0, + 220.0, + 220.0, + 320.0, + 20.0, + 310.0, + 200.0, + 420.0 + ])); + await _testVertices('draw_vertices_hairline_triangle', vertices, + BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0)); + }); + + test( + 'Should draw black hairline triangles when colors array is null' + ' and Paint() has no color.', () async { + // ignore: unused_local_variable + final Int32List colors = Int32List.fromList([ + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF + ]); + final Vertices vertices = Vertices.raw( + VertexMode.triangles, + Float32List.fromList([ + 20.0, + 20.0, + 220.0, + 10.0, + 110.0, + 220.0, + 220.0, + 320.0, + 20.0, + 310.0, + 200.0, + 420.0 + ])); + await _testVertices('draw_vertices_hairline_triangle_black', vertices, + BlendMode.srcOver, Paint()); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/71442. + test( + 'Should draw filled triangles when colors array is null' + ' and Paint() has color.', () async { + // ignore: unused_local_variable + final Int32List colors = Int32List.fromList([ + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF + ]); + final Vertices vertices = Vertices.raw( + VertexMode.triangles, + Float32List.fromList([ + 20.0, + 20.0, + 220.0, + 10.0, + 110.0, + 220.0, + 220.0, + 320.0, + 20.0, + 310.0, + 200.0, + 420.0 + ])); + await _testVertices( + 'draw_vertices_triangle_green_filled', + vertices, + BlendMode.srcOver, + Paint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF00FF00)); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw hairline triangleFan.', () async { + final Vertices vertices = Vertices.raw( + VertexMode.triangleFan, + Float32List.fromList([ + 150.0, + 150.0, + 20.0, + 10.0, + 80.0, + 20.0, + 220.0, + 15.0, + 280.0, + 30.0, + 300.0, + 420.0 + ])); + + await _testVertices('draw_vertices_hairline_triangle_fan', vertices, + BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0)); + }); + + test('Should draw hairline triangleStrip.', () async { + final Vertices vertices = Vertices.raw( + VertexMode.triangleStrip, + Float32List.fromList([ + 20.0, + 20.0, + 220.0, + 10.0, + 110.0, + 220.0, + 220.0, + 320.0, + 20.0, + 310.0, + 200.0, + 420.0 + ])); + await _testVertices('draw_vertices_hairline_triangle_strip', vertices, + BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0)); + }); + + test('Should draw triangles with colors.', () async { + final Int32List colors = Int32List.fromList([ + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF + ]); + final Vertices vertices = Vertices.raw( + VertexMode.triangles, + Float32List.fromList([ + 150.0, + 150.0, + 20.0, + 10.0, + 80.0, + 20.0, + 220.0, + 15.0, + 280.0, + 30.0, + 300.0, + 420.0 + ]), + colors: colors); + + await _testVertices('draw_vertices_triangles', vertices, BlendMode.srcOver, + Paint()..color = const Color.fromARGB(255, 0, 128, 0)); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw triangles with colors and indices.', () async { + final Int32List colors = Int32List.fromList( + [0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFF0000, 0xFF0000FF]); + final Uint16List indices = Uint16List.fromList([0, 1, 2, 3, 4, 0]); + + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + + final Vertices vertices = Vertices.raw( + VertexMode.triangles, + Float32List.fromList([ + 210.0, + 150.0, + 30.0, + 110.0, + 80.0, + 30.0, + 220.0, + 15.0, + 280.0, + 30.0, + ]), + colors: colors, + indices: indices); + + rc.drawVertices( + vertices as SurfaceVertices, BlendMode.srcOver, SurfacePaint()); + + await _checkScreenshot(rc, 'draw_vertices_triangles_indexed'); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw triangleFan with colors.', () async { + final Int32List colors = Int32List.fromList([ + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF + ]); + final Vertices vertices = Vertices.raw( + VertexMode.triangleFan, + Float32List.fromList([ + 150.0, + 150.0, + 20.0, + 10.0, + 80.0, + 20.0, + 220.0, + 15.0, + 280.0, + 30.0, + 300.0, + 420.0 + ]), + colors: colors); + + await _testVertices('draw_vertices_triangle_fan', vertices, + BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0)); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw triangleStrip with colors.', () async { + final Int32List colors = Int32List.fromList([ + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF, + 0xFFFF0000, + 0xFF00FF00, + 0xFF0000FF + ]); + final Vertices vertices = Vertices.raw( + VertexMode.triangleStrip, + Float32List.fromList([ + 20.0, + 20.0, + 220.0, + 10.0, + 110.0, + 220.0, + 220.0, + 320.0, + 20.0, + 310.0, + 200.0, + 420.0 + ]), + colors: colors); + await _testVertices('draw_vertices_triangle_strip', vertices, + BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0)); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + Future testTexture(TileMode tileMode, String filename) async { + final Uint16List indices = Uint16List.fromList([0, 1, 2, 3, 4, 0]); + + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + + final Vertices vertices = Vertices.raw( + VertexMode.triangles, + Float32List.fromList([ + 210.0, + 150.0, + 0.0, + 0.0, + 80.0, + 30.0, + 220.0, + 15.0, + 280.0, + 30.0, + ]), + indices: indices); + + final Float32List matrix4 = Matrix4.identity().storage; + + final HtmlImage img = await createTestImage(); + final SurfacePaint paint = SurfacePaint(); + + final EngineImageShader imgShader = EngineImageShader(img, tileMode, tileMode, + Float64List.fromList(matrix4), FilterQuality.high); + + paint.shader = imgShader; + + rc.drawVertices(vertices as SurfaceVertices, BlendMode.srcOver, paint); + await _checkScreenshot(rc, filename, maxDiffRatePercent: 1.0); + } + + test('Should draw triangle with texture and indices', () async { + await testTexture(TileMode.clamp, 'draw_vertices_texture'); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw triangle with texture and indices', () async { + await testTexture(TileMode.mirror, 'draw_vertices_texture_mirror'); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw triangle with texture and indices', () async { + await testTexture(TileMode.repeated, 'draw_vertices_texture_repeated'); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); +} + +Future createTestImage({int width = 50, int height = 40}) { + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, width / 3, height); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(width / 3, 0, width / 3, height); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(2 * width / 3, 0, width / 3, height); + ctx.fill(); + final html.ImageElement imageElement = html.ImageElement(); + final Completer completer = Completer(); + imageElement.onLoad.listen((html.Event event) { + completer.complete(HtmlImage(imageElement, width, height)); + }); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; + return completer.future; +} diff --git a/lib/web_ui/test/html/paragraph/bidi_golden_test.dart b/lib/web_ui/test/html/paragraph/bidi_golden_test.dart new file mode 100644 index 0000000000000..49b5aca522908 --- /dev/null +++ b/lib/web_ui/test/html/paragraph/bidi_golden_test.dart @@ -0,0 +1,582 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +import 'helper.dart'; +import 'text_scuba.dart'; + +typedef CanvasTest = FutureOr Function(EngineCanvas canvas); + +const String _rtlWord1 = 'واحد'; +const String _rtlWord2 = 'اثنان'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpStableTestFonts(); + + void paintBasicBidiStartingWithLtr( + EngineCanvas canvas, + Rect bounds, + double y, + TextDirection textDirection, + TextAlign textAlign, + ) { + // The text starts with a left-to-right word. + const String text = 'One 12 $_rtlWord1 $_rtlWord2 34 two 56'; + + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'Roboto', + fontSize: 20.0, + textDirection: textDirection, + textAlign: textAlign, + ); + final CanvasParagraph paragraph = plain( + paragraphStyle, + text, + textStyle: EngineTextStyle.only(color: blue), + ); + final double maxWidth = bounds.width - 10; + paragraph.layout(constrain(maxWidth)); + canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5)); + } + + test('basic bidi starting with ltr', () { + const Rect bounds = Rect.fromLTWH(0, 0, 340, 600); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const double height = 40; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintBasicBidiStartingWithLtr(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintBasicBidiStartingWithLtr(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_ltr'); + }); + + test('basic bidi starting with ltr (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 340, 600); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + const double height = 40; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintBasicBidiStartingWithLtr(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintBasicBidiStartingWithLtr(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintBasicBidiStartingWithLtr(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintBasicBidiStartingWithLtr(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_ltr_dom'); + }); + + void paintBasicBidiStartingWithRtl( + EngineCanvas canvas, + Rect bounds, + double y, + TextDirection textDirection, + TextAlign textAlign, + ) { + // The text starts with a right-to-left word. + const String text = '$_rtlWord1 12 one 34 $_rtlWord2 56 two'; + + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'Roboto', + fontSize: 20.0, + textDirection: textDirection, + textAlign: textAlign, + ); + final CanvasParagraph paragraph = plain( + paragraphStyle, + text, + textStyle: EngineTextStyle.only(color: blue), + ); + final double maxWidth = bounds.width - 10; + paragraph.layout(constrain(maxWidth)); + canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5)); + } + + test('basic bidi starting with rtl', () { + const Rect bounds = Rect.fromLTWH(0, 0, 340, 600); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const double height = 40; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintBasicBidiStartingWithRtl(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintBasicBidiStartingWithRtl(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_rtl'); + }); + + test('basic bidi starting with rtl (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 340, 600); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + const double height = 40; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintBasicBidiStartingWithRtl(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintBasicBidiStartingWithRtl(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintBasicBidiStartingWithRtl(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintBasicBidiStartingWithRtl(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_rtl_dom'); + }); + + void paintMultilineBidi( + EngineCanvas canvas, + Rect bounds, + double y, + TextDirection textDirection, + TextAlign textAlign, + ) { + // ''' + // Lorem 12 $_rtlWord1 + // $_rtlWord2 34 ipsum + // dolor 56 + // ''' + const String text = 'Lorem 12 $_rtlWord1 $_rtlWord2 34 ipsum dolor 56'; + + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'Roboto', + fontSize: 20.0, + textDirection: textDirection, + textAlign: textAlign, + ); + final CanvasParagraph paragraph = plain( + paragraphStyle, + text, + textStyle: EngineTextStyle.only(color: blue), + ); + final double maxWidth = bounds.width - 10; + paragraph.layout(constrain(maxWidth)); + canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5)); + } + + test('multiline bidi', () { + const Rect bounds = Rect.fromLTWH(0, 0, 400, 500); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const double height = 95; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintMultilineBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintMultilineBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintMultilineBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintMultilineBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintMultilineBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintMultilineBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintMultilineBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintMultilineBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintMultilineBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintMultilineBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multiline'); + }); + + test('multiline bidi (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 400, 500); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + const double height = 95; + + final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintMultilineBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintMultilineBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintMultilineBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintMultilineBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintMultilineBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintMultilineBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintMultilineBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintMultilineBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintMultilineBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintMultilineBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multiline_dom'); + }); + + void paintMultSpanBidi( + EngineCanvas canvas, + Rect bounds, + double y, + TextDirection textDirection, + TextAlign textAlign, + ) { + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'Roboto', + fontSize: 20.0, + textDirection: textDirection, + textAlign: textAlign, + ); + // ''' + // Lorem 12 $_rtlWord1 + // $_rtlWord2 34 ipsum + // dolor 56 + // ''' + final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('12 '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('$_rtlWord1 '); + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('$_rtlWord2 '); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('34 ipsum '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('dolor 56 '); + }); + final double maxWidth = bounds.width - 10; + paragraph.layout(constrain(maxWidth)); + canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5)); + } + + test('multi span bidi', () { + const Rect bounds = Rect.fromLTWH(0, 0, 400, 900); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const double height = 95; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintMultSpanBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintMultSpanBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintMultSpanBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintMultSpanBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintMultSpanBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintMultSpanBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintMultSpanBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintMultSpanBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintMultSpanBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintMultSpanBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multispan'); + }); + + test('multi span bidi (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 400, 900); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + const double height = 95; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintMultSpanBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintMultSpanBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintMultSpanBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintMultSpanBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintMultSpanBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintMultSpanBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintMultSpanBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintMultSpanBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintMultSpanBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintMultSpanBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multispan_dom'); + }); + + void paintBidiWithSelection( + EngineCanvas canvas, + Rect bounds, + double y, + TextDirection textDirection, + TextAlign textAlign, + ) { + // ''' + // Lorem 12 $_rtlWord1 + // $_rtlWord2 34 ipsum + // dolor 56 + // ''' + const String text = 'Lorem 12 $_rtlWord1 $_rtlWord2 34 ipsum dolor 56'; + + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'Roboto', + fontSize: 20.0, + textDirection: textDirection, + textAlign: textAlign, + ); + final CanvasParagraph paragraph = plain( + paragraphStyle, + text, + textStyle: EngineTextStyle.only(color: blue), + ); + + final double maxWidth = bounds.width - 10; + paragraph.layout(constrain(maxWidth)); + + final Offset offset = Offset(bounds.left + 5, bounds.top + y + 5); + + // Range for "em 12 " and the first character of `_rtlWord1`. + paintBoxes(canvas, offset, paragraph.getBoxesForRange(3, 10), lightBlue); + // Range for the second half of `_rtlWord1` and all of `_rtlWord2` and " 3". + paintBoxes(canvas, offset, paragraph.getBoxesForRange(11, 21), lightPurple); + // Range for "psum dolo". + paintBoxes(canvas, offset, paragraph.getBoxesForRange(24, 33), green); + + canvas.drawParagraph(paragraph, offset); + } + + test('bidi with selection', () { + const Rect bounds = Rect.fromLTWH(0, 0, 400, 500); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const double height = 95; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintBidiWithSelection(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintBidiWithSelection(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintBidiWithSelection(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintBidiWithSelection(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintBidiWithSelection(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintBidiWithSelection(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintBidiWithSelection(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintBidiWithSelection(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintBidiWithSelection(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintBidiWithSelection(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_selection'); + }); + + test('bidi with selection (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 400, 500); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + const double height = 95; + + // Border for ltr paragraphs. + final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10); + canvas.drawRect( + ltrBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // LTR with different text align values: + paintBidiWithSelection(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left); + paintBidiWithSelection(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right); + paintBidiWithSelection(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center); + paintBidiWithSelection(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start); + paintBidiWithSelection(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end); + + // Border for rtl paragraphs. + final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0); + canvas.drawRect( + rtlBox, + SurfacePaintData() + ..color = black + ..style = PaintingStyle.stroke, + ); + // RTL with different text align values: + paintBidiWithSelection(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left); + paintBidiWithSelection(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right); + paintBidiWithSelection(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center); + paintBidiWithSelection(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start); + paintBidiWithSelection(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_selection_dom'); + }); +} + +void paintBoxes(EngineCanvas canvas, Offset offset, List boxes, Color color) { + for (final TextBox box in boxes) { + final Rect rect = box.toRect().shift(offset); + canvas.drawRect(rect, SurfacePaintData()..color = color); + } +} diff --git a/lib/web_ui/test/html/paragraph/general_golden_test.dart b/lib/web_ui/test/html/paragraph/general_golden_test.dart new file mode 100644 index 0000000000000..261c452a075ac --- /dev/null +++ b/lib/web_ui/test/html/paragraph/general_golden_test.dart @@ -0,0 +1,505 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +import 'helper.dart'; +import 'text_scuba.dart'; + +typedef CanvasTest = FutureOr Function(EngineCanvas canvas); + +const Rect bounds = Rect.fromLTWH(0, 0, 800, 600); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpStableTestFonts(); + + test('paints spans and lines correctly', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + Offset offset = Offset.zero; + CanvasParagraph paragraph; + + // Single-line multi-span. + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only( + color: green, + background: Paint()..color = red, + )); + builder.addText('ipsum '); + builder.pop(); + builder.addText('.'); + }) + ..layout(constrain(double.infinity)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + // Multi-line single-span. + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { + builder.addText('Lorem ipsum dolor sit'); + }) + ..layout(constrain(90.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + // Multi-line multi-span. + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem ipsum '); + builder.pushStyle(EngineTextStyle.only(background: Paint()..color = red)); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('dolor '); + builder.pop(); + builder.addText('sit'); + }) + ..layout(constrain(90.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_general'); + }); + + test('respects alignment', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + Offset offset = Offset.zero; + CanvasParagraph paragraph; + + void build(CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('ipsum '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('dolor '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('sit'); + } + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), + build, + )..layout(constrain(100.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), + build, + )..layout(constrain(100.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), + build, + )..layout(constrain(100.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_align'); + }); + + test('respects alignment in DOM mode', () { + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + Offset offset = Offset.zero; + CanvasParagraph paragraph; + + void build(CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('ipsum '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('dolor '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('sit'); + } + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), + build, + )..layout(constrain(100.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), + build, + )..layout(constrain(100.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), + build, + )..layout(constrain(100.0)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + 10); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_dom', maxDiffRatePercent: 0.3); + }); + + void testAlignAndTransform(EngineCanvas canvas) { + CanvasParagraph paragraph; + + void build(CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: white)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('ipsum\n'); + builder.pushStyle(EngineTextStyle.only(color: yellow)); + builder.addText('dolor'); + } + + void drawParagraphAt(Offset offset, TextAlign align) { + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: align), + build, + )..layout(constrain(150.0)); + canvas.save(); + canvas.translate(offset.dx, offset.dy); + canvas.rotate(math.pi / 4); + final Rect rect = Rect.fromLTRB(0.0, 0.0, 150.0, paragraph.height); + canvas.drawRect(rect, SurfacePaintData()..color = black); + canvas.drawParagraph(paragraph, Offset.zero); + canvas.restore(); + } + + drawParagraphAt(const Offset(50.0, 0.0), TextAlign.left); + drawParagraphAt(const Offset(150.0, 0.0), TextAlign.center); + drawParagraphAt(const Offset(250.0, 0.0), TextAlign.right); + } + + test('alignment and transform', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + testAlignAndTransform(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_transform'); + }); + + test('alignment and transform (DOM)', () { + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + testAlignAndTransform(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_transform_dom'); + }); + + void testGiantParagraphStyles(EngineCanvas canvas) { + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 80.0), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 32.0)); + builder.addText('ipsum'); + }, + )..layout(constrain(double.infinity)); + final Rect rect = Rect.fromLTRB(0.0, 0.0, paragraph.maxIntrinsicWidth, paragraph.height); + canvas.drawRect(rect, SurfacePaintData()..color = black); + canvas.drawParagraph(paragraph, Offset.zero); + } + + test('giant paragraph style', () { + const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + testGiantParagraphStyles(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_paragraph_style'); + }); + + test('giant paragraph style (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + testGiantParagraphStyles(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_paragraph_style_dom'); + }); + + test('giant font size on the body tag (DOM)', () async { + const Rect bounds = Rect.fromLTWH(0, 0, 600, 200); + + // Store the old font size value on the body, and set a gaint font size. + final String oldBodyFontSize = html.document.body!.style.fontSize; + html.document.body!.style.fontSize = '100px'; + + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + Offset offset = const Offset(10.0, 10.0); + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 48.0)); + builder.addText('ipsum'); + }, + )..layout(constrain(double.infinity)); + final Rect rect = Rect.fromLTWH(offset.dx, offset.dy, paragraph.maxIntrinsicWidth, paragraph.height); + canvas.drawRect(rect, SurfacePaintData()..color = black); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(paragraph.maxIntrinsicWidth, 0.0); + + // Add some extra padding between the two paragraphs. + offset = offset.translate(20.0, 0.0); + + // Use the same height as the previous paragraph so that the 2 paragraphs + // look nice in the screenshot. + final double placeholderHeight = paragraph.height; + final double placeholderWidth = paragraph.height * 2; + + final CanvasParagraph paragraph2 = rich( + EngineParagraphStyle(), + (CanvasParagraphBuilder builder) { + builder.addPlaceholder(placeholderWidth, placeholderHeight, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + }, + )..layout(constrain(double.infinity)); + final Rect rect2 = Rect.fromLTWH(offset.dx, offset.dy, paragraph2.maxIntrinsicWidth, paragraph2.height); + canvas.drawRect(rect2, SurfacePaintData()..color = black); + canvas.drawParagraph(paragraph2, offset); + // Draw a rect in the placeholder. + // Leave some padding around the placeholder to make the black paragraph + // background visible. + const double padding = 5; + final TextBox placeholderBox = paragraph2.getBoxesForPlaceholders().single; + canvas.drawRect( + placeholderBox.toRect().shift(offset).deflate(padding), + SurfacePaintData()..color = red, + ); + + await takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_body_font_size_dom'); + + // Restore the old font size value. + html.document.body!.style.fontSize = oldBodyFontSize; + }); + + test('paints spans with varying heights/baselines', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(fontSize: 20.0)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only( + fontSize: 40.0, + background: Paint()..color = green, + )); + builder.addText('ipsum '); + builder.pushStyle(EngineTextStyle.only( + fontSize: 10.0, + color: white, + background: Paint()..color = black, + )); + builder.addText('dolor '); + builder.pushStyle(EngineTextStyle.only(fontSize: 30.0)); + builder.addText('sit '); + builder.pop(); + builder.pop(); + builder.pushStyle(EngineTextStyle.only( + fontSize: 20.0, + background: Paint()..color = blue, + )); + builder.addText('amet'); + }, + )..layout(constrain(220.0)); + canvas.drawParagraph(paragraph, Offset.zero); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_varying_heights'); + }); + + test('respects letter-spacing', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: green, letterSpacing: 1)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: red, letterSpacing: 3)); + builder.addText('Lorem'); + }, + )..layout(constrain(double.infinity)); + canvas.drawParagraph(paragraph, Offset.zero); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing'); + }); + + test('draws text decorations', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + final List decorationStyles = [ + TextDecorationStyle.solid, + TextDecorationStyle.double, + TextDecorationStyle.dotted, + TextDecorationStyle.dashed, + TextDecorationStyle.wavy, + ]; + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto'), + (CanvasParagraphBuilder builder) { + for (final TextDecorationStyle decorationStyle in decorationStyles) { + builder.pushStyle(EngineTextStyle.only( + color: const Color.fromRGBO(50, 50, 255, 1.0), + decoration: TextDecoration.underline, + decorationStyle: decorationStyle, + decorationColor: red, + fontFamily: 'Roboto', + fontSize: 30, + )); + builder.addText('Hello World'); + builder.pop(); + builder.addText(' '); + } + }, + )..layout(constrain(double.infinity)); + + canvas.drawParagraph(paragraph, Offset.zero); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_decoration'); + }); + + void testFontFeatures(EngineCanvas canvas) { + const String text = 'Aa Bb Dd Ee Ff Difficult'; + const FontFeature enableSmallCaps = FontFeature('smcp'); + const FontFeature disableSmallCaps = FontFeature('smcp', 0); + + const String numeric = '123.4560'; + const FontFeature enableOnum = FontFeature('onum'); + + const FontFeature disableLigatures = FontFeature('liga', 0); + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto'), + (CanvasParagraphBuilder builder) { + // Small Caps + builder.pushStyle(EngineTextStyle.only( + height: 1.5, + color: black, + fontSize: 32.0, + )); + builder.pushStyle(EngineTextStyle.only( + color: blue, + fontFeatures: [enableSmallCaps], + )); + builder.addText(text); + // Make sure disabling a font feature also works. + builder.pushStyle(EngineTextStyle.only( + color: black, + fontFeatures: [disableSmallCaps], + )); + builder.addText(' (smcp)\n'); + builder.pop(); // disableSmallCaps + builder.pop(); // enableSmallCaps + + // No ligatures + builder.pushStyle(EngineTextStyle.only( + color: blue, + fontFeatures: [disableLigatures], + )); + builder.addText(text); + builder.pop(); // disableLigatures + builder.addText(' (no liga)\n'); + + // No font features + builder.pushStyle(EngineTextStyle.only( + color: blue, + )); + builder.addText(text); + builder.pop(); // color: blue + builder.addText(' (none)\n'); + + // Onum + builder.pushStyle(EngineTextStyle.only( + color: blue, + fontFeatures: [enableOnum], + )); + builder.addText(numeric); + builder.pop(); // enableOnum + builder.addText(' (onum)\n'); + + // No font features + builder.pushStyle(EngineTextStyle.only( + color: blue, + )); + builder.addText(numeric); + builder.pop(); // color: blue + builder.addText(' (none)\n\n'); + + // Multiple font features + builder.addText('Combined (smcp, onum):\n'); + builder.pushStyle(EngineTextStyle.only( + color: blue, + fontFeatures: [ + enableSmallCaps, + enableOnum, + ], + )); + builder.addText('$text - $numeric'); + builder.pop(); // enableSmallCaps, enableOnum + }, + )..layout(constrain(double.infinity)); + canvas.drawParagraph(paragraph, Offset.zero); + } + + test('font features', () { + const Rect bounds = Rect.fromLTWH(0, 0, 600, 500); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + testFontFeatures(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_font_features'); + }); + + test('font features (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 600, 500); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + testFontFeatures(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_font_features_dom'); + }); + + void testBackgroundStyle(EngineCanvas canvas) { + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 40.0), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.pushStyle(EngineTextStyle.only(background: Paint()..color = blue)); + builder.addText('Lor'); + builder.pushStyle(EngineTextStyle.only(background: Paint()..color = black, color: white)); + builder.addText('em '); + builder.pop(); + builder.pushStyle(EngineTextStyle.only(background: Paint()..color = green)); + builder.addText('ipsu'); + builder.pushStyle(EngineTextStyle.only(background: Paint()..color = yellow)); + builder.addText('m\ndo'); + builder.pushStyle(EngineTextStyle.only(background: Paint()..color = red)); + builder.addText('lor sit'); + }, + ); + paragraph.layout(constrain(double.infinity)); + canvas.drawParagraph(paragraph, Offset.zero); + } + + test('background style', () { + const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + testBackgroundStyle(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_background_style'); + }); + + test('background style (DOM)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + testBackgroundStyle(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_background_style_dom'); + }); +} diff --git a/lib/web_ui/test/html/paragraph/helper.dart b/lib/web_ui/test/html/paragraph/helper.dart new file mode 100644 index 0000000000000..7c913d3bab902 --- /dev/null +++ b/lib/web_ui/test/html/paragraph/helper.dart @@ -0,0 +1,69 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; +import 'package:web_engine_tester/golden_tester.dart'; + +const Color white = Color(0xFFFFFFFF); +const Color black = Color(0xFF000000); +const Color red = Color(0xFFFF0000); +const Color lightGreen = Color(0xFFDCEDC8); +const Color green = Color(0xFF00FF00); +const Color lightBlue = Color(0xFFB3E5FC); +const Color blue = Color(0xFF0000FF); +const Color yellow = Color(0xFFFFEB3B); +const Color lightPurple = Color(0xFFE1BEE7); + +ParagraphConstraints constrain(double width) { + return ParagraphConstraints(width: width); +} + +CanvasParagraph plain( + EngineParagraphStyle style, + String text, { + EngineTextStyle? textStyle, +}) { + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + if (textStyle != null) { + builder.pushStyle(textStyle); + } + builder.addText(text); + return builder.build(); +} + +CanvasParagraph rich( + EngineParagraphStyle style, + void Function(CanvasParagraphBuilder) callback, +) { + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + callback(builder); + return builder.build(); +} + +Future takeScreenshot( + EngineCanvas canvas, + Rect region, + String fileName, { + bool write = false, + double? maxDiffRatePercent, +}) async { + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(canvas.rootElement); + html.document.body!.append(sceneElement); + await matchGoldenFile( + '$fileName.png', + region: region, + maxDiffRatePercent: maxDiffRatePercent, + write: write, + ); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } +} diff --git a/lib/web_ui/test/html/paragraph/overflow_golden_test.dart b/lib/web_ui/test/html/paragraph/overflow_golden_test.dart new file mode 100644 index 0000000000000..a948f53a4b59e --- /dev/null +++ b/lib/web_ui/test/html/paragraph/overflow_golden_test.dart @@ -0,0 +1,122 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +import 'helper.dart'; +import 'text_scuba.dart'; + +typedef CanvasTest = FutureOr Function(EngineCanvas canvas); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpStableTestFonts(); + + void testEllipsis(EngineCanvas canvas) { + Offset offset = Offset.zero; + CanvasParagraph paragraph; + + const double fontSize = 22.0; + const double width = 126.0; + const double padding = 20.0; + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: fontSize, ellipsis: '...'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('ipsum'); + }, + )..layout(constrain(width)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + padding); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: fontSize, ellipsis: '...'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem\n'); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('ipsum '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('dolor sit'); + }, + )..layout(constrain(width)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + padding); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: fontSize, ellipsis: '...'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem\n'); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('ipsum '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('d'); + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('o'); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('l'); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('o'); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('r'); + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText(' '); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('s'); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('i'); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('t'); + }, + )..layout(constrain(width)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + padding); + + paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: fontSize, maxLines: 2, ellipsis: '...'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem'); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('ipsum'); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('dolor'); + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('sit'); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('amet'); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('consectetur'); + }, + )..layout(constrain(width)); + canvas.drawParagraph(paragraph, offset); + offset = offset.translate(0, paragraph.height + padding); + } + + test('ellipsis', () { + const Rect bounds = Rect.fromLTWH(0, 0, 300, 300); + final EngineCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + testEllipsis(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_ellipsis'); + }); + + test('ellipsis (dom)', () { + const Rect bounds = Rect.fromLTWH(0, 0, 300, 300); + final EngineCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + testEllipsis(canvas); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_ellipsis_dom'); + }); +} diff --git a/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart b/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart new file mode 100644 index 0000000000000..f8d1ae1c1a065 --- /dev/null +++ b/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart @@ -0,0 +1,205 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +import 'helper.dart'; +import 'text_scuba.dart'; + +typedef CanvasTest = FutureOr Function(EngineCanvas canvas); + +const Rect bounds = Rect.fromLTWH(0, 0, 800, 600); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpStableTestFonts(); + + test('draws paragraphs with placeholders', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + Offset offset = Offset.zero; + for (final PlaceholderAlignment placeholderAlignment in PlaceholderAlignment.values) { + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 14.0), + (CanvasParagraphBuilder builder) { + builder.pushStyle(TextStyle(color: black)); + builder.addText('Lorem ipsum'); + builder.addPlaceholder( + 80.0, + 50.0, + placeholderAlignment, + baselineOffset: 40.0, + baseline: TextBaseline.alphabetic, + ); + builder.pushStyle(TextStyle(color: blue)); + builder.addText('dolor sit amet, consecteur.'); + }, + )..layout(constrain(200.0)); + + // Draw the paragraph. + canvas.drawParagraph(paragraph, offset); + + // Then fill the placeholders. + fillPlaceholder(canvas, offset, paragraph); + + offset = offset.translate(0.0, paragraph.height + 30.0); + } + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_placeholders'); + }); + + test('draws paragraphs with placeholders and text align', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + const List aligns = [ + TextAlign.left, + TextAlign.center, + TextAlign.right, + ]; + + Offset offset = Offset.zero; + for (final TextAlign align in aligns) { + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 14.0, textAlign: align), + (CanvasParagraphBuilder builder) { + builder.pushStyle(TextStyle(color: black)); + builder.addText('Lorem'); + builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.bottom); + builder.pushStyle(TextStyle(color: blue)); + builder.addText('ipsum.'); + }, + )..layout(constrain(200.0)); + + // Draw the paragraph. + canvas.drawParagraph(paragraph, offset); + + // Then fill the placeholders. + fillPlaceholder(canvas, offset, paragraph); + + offset = offset.translate(0.0, paragraph.height + 30.0); + } + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_placeholders_align'); + }); + + test('draws paragraphs with placeholders and text align in DOM mode', () { + final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture')); + + const List aligns = [ + TextAlign.left, + TextAlign.center, + TextAlign.right, + ]; + + Offset offset = Offset.zero; + for (final TextAlign align in aligns) { + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 14.0, textAlign: align), + (CanvasParagraphBuilder builder) { + builder.pushStyle(TextStyle(color: black)); + builder.addText('Lorem'); + builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.bottom); + builder.pushStyle(TextStyle(color: blue)); + builder.addText('ipsum.'); + }, + )..layout(constrain(200.0)); + + // Draw the paragraph. + canvas.drawParagraph(paragraph, offset); + + // Then fill the placeholders. + fillPlaceholder(canvas, offset, paragraph); + + offset = offset.translate(0.0, paragraph.height + 30.0); + } + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_placeholders_align_dom'); + }); + + test('draws paragraphs starting or ending with a placeholder', () { + const Rect bounds = Rect.fromLTWH(0, 0, 420, 300); + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + Offset offset = const Offset(10, 10); + + // First paragraph with a placeholder at the beginning. + final CanvasParagraph paragraph1 = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 24.0, textAlign: TextAlign.center), + (CanvasParagraphBuilder builder) { + builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + builder.pushStyle(TextStyle(color: black)); + builder.addText(' Lorem ipsum.'); + }, + )..layout(constrain(400.0)); + + // Draw the paragraph. + canvas.drawParagraph(paragraph1, offset); + fillPlaceholder(canvas, offset, paragraph1); + surroundParagraph(canvas, offset, paragraph1); + + offset = offset.translate(0.0, paragraph1.height + 30.0); + + // Second paragraph with a placeholder at the end. + final CanvasParagraph paragraph2 = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 24.0, textAlign: TextAlign.center), + (CanvasParagraphBuilder builder) { + builder.pushStyle(TextStyle(color: black)); + builder.addText('Lorem ipsum '); + builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + }, + )..layout(constrain(400.0)); + + // Draw the paragraph. + canvas.drawParagraph(paragraph2, offset); + fillPlaceholder(canvas, offset, paragraph2); + surroundParagraph(canvas, offset, paragraph2); + + offset = offset.translate(0.0, paragraph2.height + 30.0); + + // Third paragraph with a placeholder alone in the second line. + final CanvasParagraph paragraph3 = rich( + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 24.0, textAlign: TextAlign.center), + (CanvasParagraphBuilder builder) { + builder.pushStyle(TextStyle(color: black)); + builder.addText('Lorem ipsum '); + builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + }, + )..layout(constrain(200.0)); + + // Draw the paragraph. + canvas.drawParagraph(paragraph3, offset); + fillPlaceholder(canvas, offset, paragraph3); + surroundParagraph(canvas, offset, paragraph3); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_placeholders_start_and_end'); + }); +} + +void surroundParagraph( + EngineCanvas canvas, + Offset offset, + CanvasParagraph paragraph, +) { + final Rect rect = offset & Size(paragraph.width, paragraph.height); + final SurfacePaint paint = SurfacePaint()..color = blue..style = PaintingStyle.stroke; + canvas.drawRect(rect, paint.paintData); +} + +void fillPlaceholder( + EngineCanvas canvas, + Offset offset, + CanvasParagraph paragraph, +) { + final TextBox placeholderBox = paragraph.getBoxesForPlaceholders().single; + final SurfacePaint paint = SurfacePaint()..color = red; + canvas.drawRect(placeholderBox.toRect().shift(offset), paint.paintData); +} diff --git a/lib/web_ui/test/html/paragraph/shadows_golden_test.dart b/lib/web_ui/test/html/paragraph/shadows_golden_test.dart new file mode 100644 index 0000000000000..8d16908b953de --- /dev/null +++ b/lib/web_ui/test/html/paragraph/shadows_golden_test.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +import 'helper.dart'; +import 'text_scuba.dart'; + +const Rect bounds = Rect.fromLTWH(0, 0, 800, 600); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + setUpStableTestFonts(); + + test('paints multiple shadows', () { + final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); + + final CanvasParagraph paragraph = rich( + EngineParagraphStyle(fontFamily: 'Roboto'), + (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only( + fontSize: 32.0, + color: blue, + shadows: [ + const Shadow(color: red, blurRadius:2.0, offset: Offset(4.0, 2.0)), + const Shadow(color: green, blurRadius: 3.0), + ], + )); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only( + color: green, + background: Paint()..color = yellow, + shadows: [ + const Shadow(color: black, blurRadius: 10.0), + ], + )); + builder.addText('ipsum'); + builder.pop(); + builder.addText('dolor.'); + }, + )..layout(constrain(double.infinity)); + canvas.drawParagraph(paragraph, Offset.zero); + + return takeScreenshot(canvas, bounds, 'canvas_paragraph_shadows'); + }); +} diff --git a/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart b/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart new file mode 100644 index 0000000000000..d1f6fe26f0e22 --- /dev/null +++ b/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart @@ -0,0 +1,202 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'text_scuba.dart'; + +typedef PaintTest = void Function(RecordingCanvas recordingCanvas); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + // Scuba doesn't give us viewport smaller than 472px wide. + final EngineScubaTester scuba = await EngineScubaTester.initialize( + viewportSize: const Size(600, 600), + ); + + setUpStableTestFonts(); + + void paintTest(EngineCanvas canvas, PaintTest painter) { + const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600); + final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); + painter(recordingCanvas); + recordingCanvas.endRecording(); + recordingCanvas.apply(canvas, screenRect); + } + + testEachCanvas( + 'clips multiline text against rectangle', + (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipRect); + return scuba.diffCanvasScreenshot( + canvas, 'multiline_text_clipping_rect'); + } + return null; + }, + ); + + testEachCanvas( + 'clips multiline text against rectangle with transform', + (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipRectTranslated); + return scuba.diffCanvasScreenshot( + canvas, 'multiline_text_clipping_rect_translate'); + } + return null; + }, + ); + + testEachCanvas( + 'clips multiline text against round rectangle', + (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipRoundRect); + return scuba.diffCanvasScreenshot( + canvas, 'multiline_text_clipping_roundrect'); + } + return null; + }, + ); + + testEachCanvas( + 'clips multiline text against path', + (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipPath); + return scuba.diffCanvasScreenshot( + canvas, 'multiline_text_clipping_path'); + } + return null; + }, + ); + + testEachCanvas( + 'clips multiline text against stack of rects', + (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + // TODO(mdebbar): https://github.com/flutter/flutter/issues/35086 + // This produces the wrong result when using [BitmapCanvas] but without + // the new experimental canvas mode. + paintTest(canvas, paintTextWithClipStack); + return scuba.diffCanvasScreenshot( + canvas, 'multiline_text_clipping_stack1'); + } + return null; + }, + ); +} + +const Rect testBounds = Rect.fromLTRB(50, 50, 230, 220); + +void drawBackground(RecordingCanvas canvas) { + canvas.drawRect( + testBounds, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF9E9E9E)); + canvas.drawRect( + testBounds.inflate(-40), + SurfacePaint() + ..strokeWidth = 1 + ..style = PaintingStyle.stroke + ..color = const Color(0xFF009688)); +} + +void drawQuickBrownFox(RecordingCanvas canvas) { + canvas.drawParagraph( + paragraph( + 'The quick brown fox jumps over the lazy dog', + textStyle: TextStyle( + color: const Color(0xFF000000), + decoration: TextDecoration.none, + fontFamily: 'Roboto', + fontSize: 30, + background: Paint()..color = const Color.fromRGBO(50, 255, 50, 1.0), + ), + maxWidth: 180, + ), + Offset(testBounds.left, testBounds.top)); +} + +void paintTextWithClipRect(RecordingCanvas canvas) { + drawBackground(canvas); + canvas.clipRect(testBounds.inflate(-40), ClipOp.intersect); + drawQuickBrownFox(canvas); +} + +void paintTextWithClipRectTranslated(RecordingCanvas canvas) { + drawBackground(canvas); + canvas.clipRect(testBounds.inflate(-40), ClipOp.intersect); + canvas.translate(30, 10); + drawQuickBrownFox(canvas); +} + +const Color deepOrange = Color(0xFFFF5722); + +void paintTextWithClipRoundRect(RecordingCanvas canvas) { + final RRect roundRect = RRect.fromRectAndCorners(testBounds.inflate(-40), + topLeft: Radius.zero, + topRight: const Radius.elliptical(45, 40), + bottomLeft: const Radius.elliptical(50, 40), + bottomRight: const Radius.circular(30)); + drawBackground(canvas); + canvas.drawRRect( + roundRect, + SurfacePaint() + ..color = deepOrange + ..style = PaintingStyle.fill); + canvas.clipRRect(roundRect); + drawQuickBrownFox(canvas); +} + +void paintTextWithClipPath(RecordingCanvas canvas) { + drawBackground(canvas); + final Path path = Path(); + const double delta = 40.0; + final Rect clipBounds = testBounds.inflate(-delta); + final double midX = (clipBounds.left + clipBounds.right) / 2.0; + final double midY = (clipBounds.top + clipBounds.bottom) / 2.0; + path.moveTo(clipBounds.left - delta, midY); + path.quadraticBezierTo(midX, midY, midX, clipBounds.top - delta); + path.quadraticBezierTo(midX, midY, clipBounds.right + delta, midY); + path.quadraticBezierTo(midX, midY, midX, clipBounds.bottom + delta); + path.quadraticBezierTo(midX, midY, clipBounds.left - delta, midY); + path.close(); + canvas.drawPath( + path, + SurfacePaint() + ..color = deepOrange + ..style = PaintingStyle.fill); + canvas.clipPath(path); + drawQuickBrownFox(canvas); +} + +void paintTextWithClipStack(RecordingCanvas canvas) { + drawBackground(canvas); + final Rect inflatedRect = testBounds.inflate(-40); + canvas.clipRect(inflatedRect, ClipOp.intersect); + canvas.rotate(math.pi / 8.0); + canvas.translate(40, -40); + canvas.clipRect(inflatedRect, ClipOp.intersect); + canvas.drawRect( + inflatedRect, + SurfacePaint() + ..color = deepOrange + ..style = PaintingStyle.fill); + drawQuickBrownFox(canvas); +} diff --git a/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart b/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart new file mode 100644 index 0000000000000..234df6027d4c0 --- /dev/null +++ b/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; + +import 'text_scuba.dart'; + +typedef CanvasTest = FutureOr Function(EngineCanvas canvas); + +const String threeLines = 'First\nSecond\nThird'; +const String veryLong = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; +const String longUnbreakable = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + final EngineScubaTester scuba = await EngineScubaTester.initialize( + viewportSize: const Size(800, 800), + ); + + setUpStableTestFonts(); + + testEachCanvas('maxLines clipping', (EngineCanvas canvas) { + Offset offset = Offset.zero; + EngineParagraph p; + + // All three lines are rendered. + p = paragraph(threeLines); + canvas.drawParagraph(p, offset); + offset = offset.translate(0, p.height + 10); + + // Only the first two lines are rendered. + p = paragraph(threeLines, paragraphStyle: ParagraphStyle(maxLines: 2)); + canvas.drawParagraph(p, offset); + offset = offset.translate(0, p.height + 10); + + // The whole text is rendered. + p = paragraph(veryLong, maxWidth: 200); + canvas.drawParagraph(p, offset); + offset = offset.translate(0, p.height + 10); + + // Only the first two lines are rendered. + p = paragraph(veryLong, + paragraphStyle: ParagraphStyle(maxLines: 2), maxWidth: 200); + canvas.drawParagraph(p, offset); + offset = offset.translate(0, p.height + 10); + + return scuba.diffCanvasScreenshot(canvas, 'text_max_lines'); + }); +} diff --git a/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart b/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart new file mode 100644 index 0000000000000..5ab09652e51ab --- /dev/null +++ b/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart @@ -0,0 +1,139 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'text_scuba.dart'; + +typedef PaintTest = void Function(RecordingCanvas recordingCanvas); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +/// Whether we are running on iOS Safari. +// TODO(mdebbar): https://github.com/flutter/flutter/issues/66656 +bool get isIosSafari => browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs; + +Future testMain() async { + final EngineScubaTester scuba = await EngineScubaTester.initialize( + viewportSize: const Size(600, 600), + ); + + + setUpStableTestFonts(); + + testEachCanvas('draws paragraphs with placeholders', (EngineCanvas canvas) { + const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600); + final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); + + Offset offset = Offset.zero; + for (final PlaceholderAlignment placeholderAlignment + in PlaceholderAlignment.values) { + _paintTextWithPlaceholder( + recordingCanvas, + offset, + before: 'Lorem ipsum', + after: 'dolor sit amet, consectetur.', + placeholderAlignment: placeholderAlignment, + ); + offset = offset.translate(0.0, 80.0); + } + recordingCanvas.endRecording(); + recordingCanvas.apply(canvas, screenRect); + if (!isIosSafari) { + return scuba.diffCanvasScreenshot(canvas, 'text_with_placeholders'); + } + }); + + testEachCanvas('text alignment and placeholders', (EngineCanvas canvas) { + const Rect screenRect = Rect.fromLTWH(0, 0, 600, 600); + final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); + + Offset offset = Offset.zero; + _paintTextWithPlaceholder( + recordingCanvas, + offset, + before: 'Lorem', + after: 'ipsum.', + textAlignment: TextAlign.start, + ); + offset = offset.translate(0.0, 80.0); + _paintTextWithPlaceholder( + recordingCanvas, + offset, + before: 'Lorem', + after: 'ipsum.', + textAlignment: TextAlign.center, + ); + offset = offset.translate(0.0, 80.0); + _paintTextWithPlaceholder( + recordingCanvas, + offset, + before: 'Lorem', + after: 'ipsum.', + textAlignment: TextAlign.end, + ); + recordingCanvas.endRecording(); + recordingCanvas.apply(canvas, screenRect); + return scuba.diffCanvasScreenshot(canvas, 'text_align_with_placeholders'); + }); +} + +const Color black = Color(0xFF000000); +const Color blue = Color(0xFF0000FF); +const Color red = Color(0xFFFF0000); + +const Size placeholderSize = Size(80.0, 50.0); + +void _paintTextWithPlaceholder( + RecordingCanvas canvas, + Offset offset, { + required String before, + required String after, + PlaceholderAlignment placeholderAlignment = PlaceholderAlignment.baseline, + TextAlign textAlignment = TextAlign.left, +}) { + // First let's draw the paragraph. + final Paragraph paragraph = _createParagraphWithPlaceholder( + before, + after, + placeholderAlignment, + textAlignment, + ); + canvas.drawParagraph(paragraph, offset); + + // Then fill the placeholders. + final TextBox placeholderBox = paragraph.getBoxesForPlaceholders().single; + canvas.drawRect( + placeholderBox.toRect().shift(offset), + SurfacePaint()..color = red, + ); +} + +Paragraph _createParagraphWithPlaceholder( + String before, + String after, + PlaceholderAlignment placeholderAlignment, + TextAlign textAlignment, +) { + final ParagraphBuilder builder = + ParagraphBuilder(ParagraphStyle(textAlign: textAlignment)); + builder + .pushStyle(TextStyle(color: black, fontFamily: 'Roboto', fontSize: 14)); + builder.addText(before); + builder.addPlaceholder( + placeholderSize.width, + placeholderSize.height, + placeholderAlignment, + baselineOffset: 40.0, + baseline: TextBaseline.alphabetic, + ); + builder.pushStyle(TextStyle(color: blue, fontFamily: 'Roboto', fontSize: 14)); + builder.addText(after); + return builder.build()..layout(const ParagraphConstraints(width: 200.0)); +} diff --git a/lib/web_ui/test/html/paragraph/text_scuba.dart b/lib/web_ui/test/html/paragraph/text_scuba.dart new file mode 100644 index 0000000000000..e63718242bf74 --- /dev/null +++ b/lib/web_ui/test/html/paragraph/text_scuba.dart @@ -0,0 +1,135 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; + +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +/// Class that controls some details of how screenshotting is made. +/// +/// (For Googlers: Not really related with internal Scuba anymore) +class EngineScubaTester { + /// The size of the browser window used in this scuba test. + final ui.Size viewportSize; + + EngineScubaTester(this.viewportSize); + + static Future initialize( + {ui.Size viewportSize = const ui.Size(2400, 1800)}) async { + assert(() { + if (viewportSize.width.ceil() != viewportSize.width || + viewportSize.height.ceil() != viewportSize.height) { + throw Exception( + 'Scuba only supports integer screen sizes, but found: $viewportSize'); + } + if (viewportSize.width < 472) { + throw Exception('Scuba does not support screen width smaller than 472'); + } + return true; + }()); + + return EngineScubaTester(viewportSize); + } + + ui.Rect get viewportRegion => + ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height); + + Future diffScreenshot( + String fileName, { + ui.Rect? region, + double? maxDiffRatePercent, + bool write = false, + }) async { + await matchGoldenFile( + '$fileName.png', + region: region ?? viewportRegion, + maxDiffRatePercent: maxDiffRatePercent, + write: write, + ); + } + + /// Prepares the DOM and inserts all the necessary nodes, then invokes scuba's + /// screenshot diffing. + /// + /// It also cleans up the DOM after itself. + Future diffCanvasScreenshot( + EngineCanvas canvas, + String fileName, { + ui.Rect? region, + double? maxDiffRatePercent, + bool write = false, + }) async { + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(canvas.rootElement); + html.document.body!.append(sceneElement); + String screenshotName = '${fileName}_${canvas.runtimeType}'; + if (canvas is BitmapCanvas) { + screenshotName += '+canvas_measurement'; + } + await diffScreenshot( + screenshotName, + region: region, + maxDiffRatePercent: maxDiffRatePercent, + write: write, + ); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } +} + +typedef CanvasTest = FutureOr Function(EngineCanvas canvas); + +/// Runs the given test [body] with each type of canvas. +void testEachCanvas(String description, CanvasTest body, + {double? maxDiffRate}) { + const ui.Rect bounds = ui.Rect.fromLTWH(0, 0, 600, 800); + test('$description (bitmap + canvas measurement)', () async { + return body(BitmapCanvas(bounds, RenderStrategy())); + }); + test('$description (dom)', () { + return body(DomCanvas(domRenderer.createElement('flt-picture'))); + }); +} + +final ui.TextStyle _defaultTextStyle = ui.TextStyle( + color: const ui.Color(0xFF000000), + fontFamily: 'Roboto', + fontSize: 14, +); + +EngineParagraph paragraph( + String text, { + ui.ParagraphStyle? paragraphStyle, + ui.TextStyle? textStyle, + double maxWidth = double.infinity, +}) { + final ui.ParagraphBuilder builder = + ui.ParagraphBuilder(paragraphStyle ?? ui.ParagraphStyle()); + builder.pushStyle(textStyle ?? _defaultTextStyle); + builder.addText(text); + builder.pop(); + final EngineParagraph paragraph = builder.build() as EngineParagraph; + paragraph.layout(ui.ParagraphConstraints(width: maxWidth)); + return paragraph; +} + +/// Configures the test to use bundled Roboto and Ahem fonts to avoid golden +/// screenshot differences due to differences in the preinstalled system fonts. +void setUpStableTestFonts() { + setUp(() async { + await ui.webOnlyInitializePlatform(); + ui.webOnlyFontCollection.debugRegisterTestFonts(); + await ui.webOnlyFontCollection.ensureFontsLoaded(); + }); +} diff --git a/lib/web_ui/test/html/path_metrics_golden_test.dart b/lib/web_ui/test/html/path_metrics_golden_test.dart new file mode 100644 index 0000000000000..ac2e21bedaac1 --- /dev/null +++ b/lib/web_ui/test/html/path_metrics_golden_test.dart @@ -0,0 +1,205 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; +import 'package:web_engine_tester/golden_tester.dart'; + +import '../matchers.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + const Color black12Color = Color(0x1F000000); + const Color redAccentColor = Color(0xFFFF1744); + const double kDashLength = 5.0; + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(RecordingCanvas rc, String fileName, + {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { + final EngineCanvas engineCanvas = BitmapCanvas(screenRect, + RenderStrategy()); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + await matchGoldenFile('$fileName.png', region: region); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Should calculate tangent on line', () async { + final Path path = Path(); + path.moveTo(50, 130); + path.lineTo(150, 20); + + final PathMetric metric = path.computeMetrics().first; + final Tangent t = metric.getTangentForOffset(50.0)!; + expect(t.position.dx, within(from: 83.633, distance: 0.01)); + expect(t.position.dy, within(from: 93.0, distance: 0.01)); + expect(t.vector.dx, within(from: 0.672, distance: 0.01)); + expect(t.vector.dy, within(from: -0.739, distance: 0.01)); + }); + + test('Should calculate tangent on cubic curve', () async { + final Path path = Path(); + const double p1x = 240; + const double p1y = 120; + const double p2x = 320; + const double p2y = 25; + path.moveTo(150, 20); + path.quadraticBezierTo(p1x, p1y, p2x, p2y); + final PathMetric metric = path.computeMetrics().first; + final Tangent t = metric.getTangentForOffset(50.0)!; + expect(t.position.dx, within(from: 187.25, distance: 0.01)); + expect(t.position.dy, within(from: 53.33, distance: 0.01)); + expect(t.vector.dx, within(from: 0.82, distance: 0.01)); + expect(t.vector.dy, within(from: 0.56, distance: 0.01)); + }); + + test('Should calculate tangent on quadratic curve', () async { + final Path path = Path(); + const double p0x = 150; + const double p0y = 20; + const double p1x = 320; + const double p1y = 25; + path.moveTo(150, 20); + path.quadraticBezierTo(p0x, p0y, p1x, p1y); + final PathMetric metric = path.computeMetrics().first; + final Tangent t = metric.getTangentForOffset(50.0)!; + expect(t.position.dx, within(from: 199.82, distance: 0.01)); + expect(t.position.dy, within(from: 21.46, distance: 0.01)); + expect(t.vector.dx, within(from: 0.99, distance: 0.01)); + expect(t.vector.dy, within(from: 0.02, distance: 0.01)); + }); + + // Test for extractPath to draw 5 pixel length dashed line using quad curve. + test('Should draw dashed line on quadratic curve.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + + final SurfacePaint paint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3 + ..color = black12Color; + final SurfacePaint redPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = redAccentColor; + + final SurfacePath path = SurfacePath(); + path.moveTo(50, 130); + path.lineTo(150, 20); + const double p1x = 240; + const double p1y = 120; + const double p2x = 320; + const double p2y = 25; + path.quadraticBezierTo(p1x, p1y, p2x, p2y); + + rc.drawPath(path, paint); + + const double t0 = 0.2; + const double t1 = 0.7; + + final List metrics = path.computeMetrics().toList(); + double totalLength = 0; + for (final PathMetric m in metrics) { + totalLength += m.length; + } + final Path dashedPath = Path(); + for (final PathMetric measurePath in path.computeMetrics()) { + double distance = totalLength * t0; + bool draw = true; + while (distance < measurePath.length * t1) { + const double length = kDashLength; + if (draw) { + dashedPath.addPath( + measurePath.extractPath(distance, distance + length), + Offset.zero); + } + distance += length; + draw = !draw; + } + } + rc.drawPath(dashedPath, redPaint); + await _checkScreenshot(rc, 'path_dash_quadratic'); + }); + + // Test for extractPath to draw 5 pixel length dashed line using cubic curve. + test('Should draw dashed line on cubic curve.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + + final SurfacePaint paint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3 + ..color = black12Color; + final SurfacePaint redPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = redAccentColor; + + final Path path = Path(); + path.moveTo(50, 130); + path.lineTo(150, 20); + const double p1x = 40; + const double p1y = 120; + const double p2x = 300; + const double p2y = 130; + const double p3x = 320; + const double p3y = 25; + path.cubicTo(p1x, p1y, p2x, p2y, p3x, p3y); + + rc.drawPath(path, paint); + + const double t0 = 0.2; + const double t1 = 0.7; + + final List metrics = path.computeMetrics().toList(); + double totalLength = 0; + for (final PathMetric m in metrics) { + totalLength += m.length; + } + final Path dashedPath = Path(); + for (final PathMetric measurePath in path.computeMetrics()) { + double distance = totalLength * t0; + bool draw = true; + while (distance < measurePath.length * t1) { + const double length = kDashLength; + if (draw) { + dashedPath.addPath( + measurePath.extractPath(distance, distance + length), + Offset.zero); + } + distance += length; + draw = !draw; + } + } + rc.drawPath(dashedPath, redPaint); + await _checkScreenshot(rc, 'path_dash_cubic'); + }); +} diff --git a/lib/web_ui/test/html/path_to_svg_golden_test.dart b/lib/web_ui/test/html/path_to_svg_golden_test.dart new file mode 100644 index 0000000000000..a93a9a3a907d6 --- /dev/null +++ b/lib/web_ui/test/html/path_to_svg_golden_test.dart @@ -0,0 +1,245 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +enum PaintMode { + kStrokeAndFill, + kStroke, + kFill, + kStrokeWidthOnly, +} + +Future testMain() async { + const Rect region = + Rect.fromLTWH(8, 8, 600, 400); // Compensate for old scuba tester padding + + Future testPath(Path path, String scubaFileName, + {SurfacePaint? paint, + double? maxDiffRatePercent, + bool write = false, + PaintMode mode = PaintMode.kStrokeAndFill}) async { + const Rect canvasBounds = Rect.fromLTWH(0, 0, 600, 400); + final BitmapCanvas bitmapCanvas = + BitmapCanvas(canvasBounds, RenderStrategy()); + final RecordingCanvas canvas = RecordingCanvas(canvasBounds); + + final bool enableFill = + mode == PaintMode.kStrokeAndFill || mode == PaintMode.kFill; + if (enableFill) { + paint ??= SurfacePaint() + ..color = const Color(0x807F7F7F) + ..style = PaintingStyle.fill; + canvas.drawPath(path, paint); + } + + if (mode == PaintMode.kStrokeAndFill || mode == PaintMode.kStroke) { + paint = SurfacePaint() + ..strokeWidth = 2 + ..color = enableFill ? const Color(0xFFFF0000) : const Color(0xFF000000) + ..style = PaintingStyle.stroke; + } + + if (mode == PaintMode.kStrokeWidthOnly) { + paint = SurfacePaint() + ..color = const Color(0xFF4060E0) + ..strokeWidth = 10; + } + + canvas.drawPath(path, paint!); + + final html.Element svgElement = pathToSvgElement(path, paint, enableFill); + + html.document.body!.append(bitmapCanvas.rootElement); + html.document.body!.append(svgElement); + + canvas.endRecording(); + canvas.apply(bitmapCanvas, canvasBounds); + + await matchGoldenFile('$scubaFileName.png', + region: region, maxDiffRatePercent: maxDiffRatePercent, write: write); + + bitmapCanvas.rootElement.remove(); + svgElement.remove(); + } + + tearDown(() { + html.document.body!.children.clear(); + }); + + test('render line strokes', () async { + final Path path = Path(); + path.moveTo(50, 60); + path.lineTo(200, 300); + await testPath(path, 'svg_stroke_line', + paint: SurfacePaint() + ..color = const Color(0xFFFF0000) + ..strokeWidth = 2.0 + ..style = PaintingStyle.stroke); + }); + + test('render quad bezier curve', () async { + final Path path = Path(); + path.moveTo(50, 60); + path.quadraticBezierTo(200, 60, 50, 200); + await testPath(path, 'svg_quad_bezier'); + }); + + test('render cubic curve', () async { + final Path path = Path(); + path.moveTo(50, 60); + path.cubicTo(200, 60, -100, -50, 150, 200); + await testPath(path, 'svg_cubic_bezier'); + }); + + test('render arcs', () async { + final List arcs = [ + ArcSample(const Offset(0, 0), + largeArc: false, clockwise: false, distance: 20), + ArcSample(const Offset(200, 0), + largeArc: true, clockwise: false, distance: 20), + ArcSample(const Offset(0, 0), + largeArc: false, clockwise: true, distance: 20), + ArcSample(const Offset(200, 0), + largeArc: true, clockwise: true, distance: 20), + ArcSample(const Offset(0, 0), + largeArc: false, clockwise: false, distance: -20), + ArcSample(const Offset(200, 0), + largeArc: true, clockwise: false, distance: -20), + ArcSample(const Offset(0, 0), + largeArc: false, clockwise: true, distance: -20), + ArcSample(const Offset(200, 0), + largeArc: true, clockwise: true, distance: -20) + ]; + int sampleIndex = 0; + for (final ArcSample sample in arcs) { + ++sampleIndex; + final Path path = sample.createPath(); + await testPath(path, 'svg_arc_$sampleIndex'); + } + }); + + test('render rect', () async { + final Path path = Path(); + path.addRect(const Rect.fromLTRB(15, 15, 60, 20)); + path.addRect(const Rect.fromLTRB(35, 160, 15, 100)); + await testPath(path, 'svg_rect', maxDiffRatePercent: 1.0); + }); + + test('render notch', () async { + final Path path = Path(); + path.moveTo(0, 0); + path.lineTo(83, 0); + path.quadraticBezierTo(98, 0, 99.97, 7.8); + path.arcToPoint(const Offset(162, 7.8), + radius: const Radius.circular(32), + largeArc: false, + clockwise: false, + rotation: 0); + path.lineTo(200, 7.8); + path.lineTo(200, 80); + path.lineTo(0, 80); + path.lineTo(0, 10); + await testPath(path, 'svg_notch'); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/70980 + test('render notch', () async { + const double w = 0.7; + final Path path = Path(); + path.moveTo(0.5, 14); + path.conicTo(0.5, 10.5, 4, 10.5, w); + path.moveTo(4, 10.5); + path.lineTo(6.5, 10.5); + path.moveTo(36.0, 10.5); + path.lineTo(158, 10.5); + path.conicTo(161.5, 10.5, 161.5, 14, w); + path.moveTo(161.5, 14); + path.lineTo(161.5, 48); + path.conicTo(161.5, 51.5, 158, 51.5, w); + path.lineTo(4, 51.5); + path.conicTo(0.5, 51.5, 0.5, 48, w); + path.lineTo(0.5, 14); + await testPath(path, 'svg_editoutline', mode: PaintMode.kStroke); + }); + + /// Regression test for https://github.com/flutter/flutter/issues/74416 + test('render stroke', () async { + final Path path = Path(); + path.moveTo(20, 20); + path.lineTo(200, 200); + await testPath(path, 'svg_stroke_width', mode: PaintMode.kStrokeWidthOnly); + }); +} + +html.Element pathToSvgElement(Path path, Paint paint, bool enableFill) { + final Rect bounds = path.getBounds(); + final StringBuffer sb = StringBuffer(); + sb.write(''); + sb.write(''); + sb.write(''); + final html.Element svgElement = + html.Element.html(sb.toString(), treeSanitizer: NullTreeSanitizer()); + svgElement.style.transform = 'translate(200px, 0px)'; + return svgElement; +} + +class ArcSample { + final Offset offset; + final bool largeArc; + final bool clockwise; + final double distance; + ArcSample(this.offset, + {this.largeArc = false, this.clockwise = false, this.distance = 0}); + + Path createPath() { + final Offset startP = + Offset(75 - distance + offset.dx, 75 - distance + offset.dy); + final Offset endP = + Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); + final Path path = Path(); + path.moveTo(startP.dx, startP.dy); + path.arcToPoint(endP, + rotation: 60, + radius: const Radius.elliptical(40, 60), + largeArc: largeArc, + clockwise: clockwise); + return path; + } + + // Returns bounds of start/end point of arc. + Rect getBounds() { + final Offset startP = + Offset(75 - distance + offset.dx, 75 - distance + offset.dy); + final Offset endP = + Offset(75.0 + distance + offset.dx, 75.0 + distance + offset.dy); + return Rect.fromLTRB(startP.dx, startP.dy, endP.dx, endP.dy); + } +} diff --git a/lib/web_ui/test/html/path_transform_golden_test.dart b/lib/web_ui/test/html/path_transform_golden_test.dart new file mode 100644 index 0000000000000..12e4d3bf935c6 --- /dev/null +++ b/lib/web_ui/test/html/path_transform_golden_test.dart @@ -0,0 +1,229 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(RecordingCanvas rc, String fileName, + {Rect region = const Rect.fromLTWH(0, 0, 500, 500), + double? maxDiffRatePercent}) async { + final EngineCanvas engineCanvas = BitmapCanvas(screenRect, + RenderStrategy()); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: maxDiffRatePercent); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Should draw transformed line.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + final Path path = Path(); + path.moveTo(0, 0); + path.lineTo(300, 200); + rc.drawPath( + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000)); + final Path transformedPath = Path(); + final Matrix4 testMatrixTranslateRotate = + Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, 20); + transformedPath.addPath(path, Offset.zero, + matrix4: testMatrixTranslateRotate.toFloat64()); + rc.drawPath( + transformedPath, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color.fromRGBO(0, 128, 255, 1.0)); + await _checkScreenshot(rc, 'path_transform_with_line'); + }); + + test('Should draw transformed line.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + final Path path = Path(); + path.addRect(const Rect.fromLTRB(50, 40, 300, 100)); + rc.drawPath( + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000)); + final Path transformedPath = Path(); + final Matrix4 testMatrixTranslateRotate = + Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, 20); + transformedPath.addPath(path, Offset.zero, + matrix4: testMatrixTranslateRotate.toFloat64()); + rc.drawPath( + transformedPath, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color.fromRGBO(0, 128, 255, 1.0)); + await _checkScreenshot(rc, 'path_transform_with_rect'); + }); + + test('Should draw transformed quadratic curve.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + final Path path = Path(); + path.moveTo(100, 100); + path.quadraticBezierTo(100, 300, 400, 300); + rc.drawPath( + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000)); + final Path transformedPath = Path(); + final Matrix4 testMatrixTranslateRotate = + Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, -80); + transformedPath.addPath(path, Offset.zero, + matrix4: testMatrixTranslateRotate.toFloat64()); + rc.drawPath( + transformedPath, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color.fromRGBO(0, 128, 255, 1.0)); + await _checkScreenshot(rc, 'path_transform_with_quadratic_curve'); + }); + + test('Should draw transformed conic.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + const double yStart = 20; + + const Offset p0 = Offset(25, yStart + 25); + const Offset pc = Offset(60, yStart + 150); + const Offset p2 = Offset(100, yStart + 50); + + final Path path = Path(); + path.moveTo(p0.dx, p0.dy); + path.conicTo(pc.dx, pc.dy, p2.dx, p2.dy, 0.5); + path.close(); + path.moveTo(p0.dx, p0.dy + 100); + path.conicTo(pc.dx, pc.dy + 100, p2.dx, p2.dy + 100, 10); + path.close(); + + rc.drawPath( + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000)); + final Path transformedPath = Path(); + final Matrix4 testMatrixTranslateRotate = + Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, -80); + transformedPath.addPath(path, Offset.zero, + matrix4: testMatrixTranslateRotate.toFloat64()); + rc.drawPath( + transformedPath, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color.fromRGBO(0, 128, 255, 1.0)); + await _checkScreenshot(rc, 'path_transform_with_conic'); + }); + + test('Should draw transformed arc.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + + final Path path = Path(); + path.moveTo(350, 280); + path.arcToPoint(const Offset(450, 90), + radius: const Radius.elliptical(200, 50), + rotation: -math.pi / 6.0, + largeArc: true, + clockwise: true); + path.close(); + + rc.drawPath( + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000)); + + final Path transformedPath = Path(); + final Matrix4 testMatrixTranslateRotate = + Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, 10); + transformedPath.addPath(path, Offset.zero, + matrix4: testMatrixTranslateRotate.toFloat64()); + rc.drawPath( + transformedPath, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color.fromRGBO(0, 128, 255, 1.0)); + await _checkScreenshot(rc, 'path_transform_with_arc', + maxDiffRatePercent: 1.4); + }); + + test('Should draw transformed rrect.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + + final Path path = Path(); + path.addRRect(RRect.fromLTRBR(50, 50, 300, 200, const Radius.elliptical(4, 8))); + + rc.drawPath( + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000)); + + final Path transformedPath = Path(); + final Matrix4 testMatrixTranslateRotate = + Matrix4.rotationZ(math.pi * 30.0 / 180.0)..translate(100, -80); + transformedPath.addPath(path, Offset.zero, + matrix4: testMatrixTranslateRotate.toFloat64()); + rc.drawPath( + transformedPath, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color.fromRGBO(0, 128, 255, 1.0)); + await _checkScreenshot(rc, 'path_transform_with_rrect'); + }); +} diff --git a/lib/web_ui/test/html/picture_golden_test.dart b/lib/web_ui/test/html/picture_golden_test.dart new file mode 100644 index 0000000000000..1ab71d754f8c2 --- /dev/null +++ b/lib/web_ui/test/html/picture_golden_test.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + group('Picture', () { + test('toImage produces an image', () async { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 200, 100)); + canvas.drawCircle( + const ui.Offset(100, 50), + 40, + SurfacePaint() + ..color = const ui.Color.fromARGB(255, 255, 100, 100), + ); + final ui.Picture picture = recorder.endRecording(); + final HtmlImage image = await picture.toImage(200, 100) as HtmlImage; + expect(image, isNotNull); + html.document.body! + ..style.margin = '0' + ..append(image.imgElement); + try { + await matchGoldenFile( + 'picture_to_image.png', + region: const ui.Rect.fromLTRB(0, 0, 200, 100), + ); + } finally { + image.imgElement.remove(); + } + }); + }); +} diff --git a/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart b/lib/web_ui/test/html/recording_canvas_golden_test.dart similarity index 94% rename from lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart rename to lib/web_ui/test/html/recording_canvas_golden_test.dart index c7854c961a798..649c7cda70375 100644 --- a/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart +++ b/lib/web_ui/test/html/recording_canvas_golden_test.dart @@ -2,41 +2,41 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'dart:math' as math; import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart'; - -import '../../matchers.dart'; +import 'package:ui/ui.dart' hide TextStyle; import 'package:web_engine_tester/golden_tester.dart'; +import '../matchers.dart'; + void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { +Future testMain() async { const double screenWidth = 600.0; const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - final Paint testPaint = Paint()..color = const Color(0xFFFF0000); + final SurfacePaint testPaint = SurfacePaint()..color = const Color(0xFFFF0000); // Commit a recording canvas to a bitmap, and compare with the expected Future _checkScreenshot(RecordingCanvas rc, String fileName, { Rect region = const Rect.fromLTWH(0, 0, 500, 500), bool write = false }) async { - final EngineCanvas engineCanvas = BitmapCanvas(screenRect); + final EngineCanvas engineCanvas = BitmapCanvas(screenRect, + RenderStrategy()); // Draws the estimated bounds so we can spot the bug in Scuba. engineCanvas ..save() ..drawRect( - rc.pictureBounds, + rc.pictureBounds!, SurfacePaintData() ..color = const Color.fromRGBO(0, 0, 255, 1.0) ..style = PaintingStyle.stroke @@ -50,7 +50,7 @@ void testMain() async { final html.Element sceneElement = html.Element.tag('flt-scene'); try { sceneElement.append(engineCanvas.rootElement); - html.document.body.append(sceneElement); + html.document.body!.append(sceneElement); await matchGoldenFile('paint_bounds_for_$fileName.png', region: region, write: write); } finally { @@ -218,7 +218,7 @@ void testMain() async { test('drawColor should cover full size', () async { final RecordingCanvas rc = RecordingCanvas(screenRect); - final Paint testPaint = Paint()..color = const Color(0xFF80FF00); + final SurfacePaint testPaint = SurfacePaint()..color = const Color(0xFF80FF00); 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); @@ -286,7 +286,7 @@ void testMain() async { test('Computes paint bounds for draw image', () { final RecordingCanvas rc = RecordingCanvas(screenRect); - rc.drawImage(TestImage(), const Offset(50, 100), Paint()); + rc.drawImage(TestImage(), const Offset(50, 100), SurfacePaint()); rc.endRecording(); expect(rc.pictureBounds, const Rect.fromLTRB(50.0, 100.0, 70.0, 110.0)); }); @@ -294,7 +294,7 @@ void testMain() async { 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()); + const Rect.fromLTRB(5, 6, 400, 500), SurfacePaint()); rc.endRecording(); expect(rc.pictureBounds, const Rect.fromLTRB(5.0, 6.0, 400.0, 500.0)); }); @@ -313,7 +313,7 @@ void testMain() async { const Rect.fromLTRB(textLeft, textTop, textLeft + widthConstraint, 21.0), ); await _checkScreenshot(rc, 'draw_paragraph'); - }, // TODO: https://github.com/flutter/flutter/issues/65789 + }, // TODO(mdebbar): https://github.com/flutter/flutter/issues/65789 skip: browserEngine == BrowserEngine.webkit && operatingSystem == OperatingSystem.iOs); @@ -332,7 +332,7 @@ void testMain() async { const Rect.fromLTRB(textLeft, textTop, textLeft + widthConstraint, 35.0), ); await _checkScreenshot(rc, 'draw_paragraph_multi_line'); - }, // TODO: https://github.com/flutter/flutter/issues/65789 + }, // TODO(mdebbar): https://github.com/flutter/flutter/issues/65789 skip: browserEngine == BrowserEngine.webkit && operatingSystem == OperatingSystem.iOs); @@ -413,7 +413,7 @@ void testMain() async { ..translate(0, 100) ..scale(1, -1) ..clipRect(const Rect.fromLTRB(0, 0, 100, 50), ClipOp.intersect) - ..drawRect(const Rect.fromLTRB(0, 0, 100, 100), Paint()); + ..drawRect(const Rect.fromLTRB(0, 0, 100, 100), SurfacePaint()); rc.endRecording(); expect(rc.pictureBounds, const Rect.fromLTRB(0.0, 50.0, 100.0, 100.0)); @@ -427,7 +427,7 @@ void testMain() async { ..translate(50, 50) ..rotate(math.pi / 4.0) ..clipRect(const Rect.fromLTWH(-20, -20, 40, 40), ClipOp.intersect) - ..drawRect(const Rect.fromLTWH(-80, -80, 160, 160), Paint()); + ..drawRect(const Rect.fromLTWH(-80, -80, 160, 160), SurfacePaint()); rc.endRecording(); expect( @@ -446,7 +446,7 @@ void testMain() async { rc ..translate(50, 50) ..rotate(math.pi / 4.0) - ..drawLine(const Offset(0, 0), const Offset(20, 20), Paint()); + ..drawLine(const Offset(0, 0), const Offset(20, 20), SurfacePaint()); rc.endRecording(); expect( @@ -466,7 +466,7 @@ void testMain() async { path.lineTo(100, 97); rc.drawPath( path, - Paint() + SurfacePaint() ..style = PaintingStyle.stroke ..strokeWidth = 2.0 ..color = const Color(0xFFFF0000)); @@ -475,7 +475,7 @@ void testMain() async { path.lineTo(97, 100); rc.drawPath( path, - Paint() + SurfacePaint() ..style = PaintingStyle.stroke ..strokeWidth = 2.0 ..color = const Color(0xFF00FF00)); @@ -497,7 +497,7 @@ void testMain() async { RRect.fromLTRBR(0.5, 100.5, 80.7, 150.7, const Radius.circular(10))); rc.drawPath( path, - Paint() + SurfacePaint() ..style = PaintingStyle.stroke ..strokeWidth = 2.0 ..color = const Color(0xFF404000)); @@ -514,14 +514,14 @@ void testMain() async { rc.translate(50.0, 100.0); final Path path = Path(); // Draw a vertical small line (caret). - path.addPolygon([Offset(0, 10), Offset(20,5), Offset(50,10)], + path.addPolygon(const [Offset(0, 10), Offset(20,5), Offset(50,10)], false); path.lineTo(60, 80); path.lineTo(0, 80); path.close(); rc.drawPath( path, - Paint() + SurfacePaint() ..style = PaintingStyle.stroke ..strokeWidth = 2.0 ..color = const Color(0xFF404000)); @@ -549,14 +549,14 @@ void testMain() async { }, (RecordingCanvas canvas, SurfacePaint paint) { canvas.drawRRect( - RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, Radius.circular(7.0)), + RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, const Radius.circular(7.0)), paint, ); }, (RecordingCanvas canvas, SurfacePaint paint) { canvas.drawDRRect( - RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, Radius.circular(5.0)), - RRect.fromLTRBR(4.0, 4.0, 16.0, 16.0, Radius.circular(5.0)), + RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, const Radius.circular(5.0)), + RRect.fromLTRBR(4.0, 4.0, 16.0, 16.0, const Radius.circular(5.0)), paint, ); }, @@ -623,7 +623,7 @@ void testMain() async { final SurfacePaint zeroSpreadPaint = SurfacePaint(); painter(canvas, zeroSpreadPaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); sb.pop(); } @@ -637,7 +637,7 @@ void testMain() async { ..strokeWidth = 5.0; painter(canvas, thickStrokePaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); sb.pop(); } @@ -650,7 +650,7 @@ void testMain() async { ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0); painter(canvas, maskFilterBlurPaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); sb.pop(); } @@ -665,20 +665,20 @@ void testMain() async { ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0); painter(canvas, thickStrokeAndBlurPaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds!)); sb.pop(); } sb.pop(); } - final html.Element sceneElement = sb.build().webOnlyRootElement; - html.document.body.append(sceneElement); + final html.Element sceneElement = sb.build().webOnlyRootElement!; + html.document.body!.append(sceneElement); try { await matchGoldenFile( 'paint_spread_bounds.png', region: const Rect.fromLTRB(0, 0, 250, 600), - maxDiffRatePercent: 0.01, + maxDiffRatePercent: 0.21, pixelComparison: PixelComparison.precise, ); } finally { @@ -723,6 +723,9 @@ class TestImage implements Image { @override void dispose() {} + @override + bool get debugDisposed => false; + @override Image clone() => this; diff --git a/lib/web_ui/test/html/screenshot.dart b/lib/web_ui/test/html/screenshot.dart new file mode 100644 index 0000000000000..825fac66d5576 --- /dev/null +++ b/lib/web_ui/test/html/screenshot.dart @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:web_engine_tester/golden_tester.dart'; + +/// Commit a recording canvas to a bitmap, and compare with the expected. +Future canvasScreenshot(RecordingCanvas rc, String fileName, + {ui.Rect region = const ui.Rect.fromLTWH(0, 0, 600, 800), + double maxDiffRatePercent = 0.0, bool setupPerspective = false, + bool write = false}) async { + final EngineCanvas engineCanvas = BitmapCanvas(region, + RenderStrategy()); + + rc.endRecording(); + rc.apply(engineCanvas, region); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + if (setupPerspective) { + // iFrame disables perspective, set it explicitly for test. + engineCanvas.rootElement.style.perspective = '400px'; + for (final html.Element element in engineCanvas.rootElement.querySelectorAll( + 'div')) { + element.style.perspective = '400px'; + } + } + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + await matchGoldenFile('$fileName.png', + region: region, maxDiffRatePercent: maxDiffRatePercent, write: write); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } +} + +Future sceneScreenshot(SurfaceSceneBuilder sceneBuilder, String fileName, + {ui.Rect region = const ui.Rect.fromLTWH(0, 0, 600, 800), + double maxDiffRatePercent = 0.0, bool write = false}) async { + html.Element? sceneElement; + try { + sceneElement = sceneBuilder + .build() + .webOnlyRootElement; + html.document.body!.append(sceneElement!); + await matchGoldenFile('$fileName.png', + region: region, maxDiffRatePercent: maxDiffRatePercent, write: write); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement?.remove(); + } +} + + +/// Configures the test to use bundled Roboto and Ahem fonts to avoid golden +/// screenshot differences due to differences in the preinstalled system fonts. +void setUpStableTestFonts() { + setUp(() async { + await ui.webOnlyInitializePlatform(); + ui.webOnlyFontCollection.debugRegisterTestFonts(); + await ui.webOnlyFontCollection.ensureFontsLoaded(); + }); +} diff --git a/lib/web_ui/test/html/shaders/gradient_golden_test.dart b/lib/web_ui/test/html/shaders/gradient_golden_test.dart new file mode 100644 index 0000000000000..72a2c353bafad --- /dev/null +++ b/lib/web_ui/test/html/shaders/gradient_golden_test.dart @@ -0,0 +1,461 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +import '../../common.dart'; +import '../paragraph/text_scuba.dart'; + +// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. +// https://github.com/flutter/flutter/issues/86623 + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + // Commit a recording canvas to a bitmap, and compare with the expected + Future _checkScreenshot(RecordingCanvas rc, String fileName, + {Rect region = const Rect.fromLTWH(0, 0, 500, 240), + double maxDiffRatePercent = 0.0, bool write = false}) async { + final EngineCanvas engineCanvas = BitmapCanvas(screenRect, + RenderStrategy()); + + rc.endRecording(); + rc.apply(engineCanvas, screenRect); + + // Wrap in so that our CSS selectors kick in. + final html.Element sceneElement = html.Element.tag('flt-scene'); + try { + sceneElement.append(engineCanvas.rootElement); + html.document.body!.append(sceneElement); + await matchGoldenFile('$fileName.png', + region: region, maxDiffRatePercent: maxDiffRatePercent, write: write); + } finally { + // The page is reused across tests, so remove the element after taking the + // Scuba screenshot. + sceneElement.remove(); + } + } + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + }); + + setUpStableTestFonts(); + + test('Paints sweep gradient rectangles', () async { + final RecordingCanvas canvas = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + canvas.save(); + + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78),]; + const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + GradientSweep sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + 0, 360.0 / 180.0 * math.pi, + Matrix4.rotationZ(math.pi / 6.0).storage); + + final GradientSweep sweepGradientRotated = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + 0, 360.0 / 180.0 * math.pi, + Matrix4.rotationZ(math.pi / 6.0).storage); + + const double kBoxWidth = 150; + const double kBoxHeight = 80; + // Gradient with default center. + Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Gradient with shifted center and rotation. + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradientRotated, Rect.fromLTWH(rectBounds.center.dx, rectBounds.top, rectBounds.width / 2, rectBounds.height))); + canvas.drawRect(rectBounds, borderPaint); + + // Gradient with start/endangle. + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode repeat + rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.repeated, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode mirror + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.mirror, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + canvas.restore(); + await _checkScreenshot(canvas, 'sweep_gradient_rect'); + }, skip: isFirefox); + + test('Paints sweep gradient ovals', () async { + final RecordingCanvas canvas = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + canvas.save(); + + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78),]; + final List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + GradientSweep sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + 0, 360.0 / 180.0 * math.pi, + Matrix4.rotationZ(math.pi / 6.0).storage); + + final GradientSweep sweepGradientRotated = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + 0, 360.0 / 180.0 * math.pi, + Matrix4.rotationZ(math.pi / 6.0).storage); + + const double kBoxWidth = 150; + const double kBoxHeight = 80; + // Gradient with default center. + Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); + canvas.drawOval(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Gradient with shifted center and rotation. + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + canvas.drawOval(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradientRotated, Rect.fromLTWH(rectBounds.center.dx, rectBounds.top, rectBounds.width / 2, rectBounds.height))); + canvas.drawRect(rectBounds, borderPaint); + + // Gradient with start/endangle. + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + canvas.drawOval(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode repeat + rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.repeated, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + + canvas.drawOval(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode mirror + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.mirror, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + canvas.drawOval(rectBounds, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + canvas.restore(); + await _checkScreenshot(canvas, 'sweep_gradient_oval'); + }, skip: isFirefox); + + test('Paints sweep gradient paths', () async { + final RecordingCanvas canvas = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + canvas.save(); + + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78),]; + const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + GradientSweep sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + 0, 360.0 / 180.0 * math.pi, + Matrix4.rotationZ(math.pi / 6.0).storage); + + final GradientSweep sweepGradientRotated = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + 0, 360.0 / 180.0 * math.pi, + Matrix4.rotationZ(math.pi / 6.0).storage); + + const double kBoxWidth = 150; + const double kBoxHeight = 80; + // Gradient with default center. + Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); + Path path = samplePathFromRect(rectBounds); + canvas.drawPath(path, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Gradient with shifted center and rotation. + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + path = samplePathFromRect(rectBounds); + canvas.drawPath(path, + SurfacePaint()..shader = engineGradientToShader(sweepGradientRotated, Rect.fromLTWH(rectBounds.center.dx, rectBounds.top, rectBounds.width / 2, rectBounds.height))); + canvas.drawRect(rectBounds, borderPaint); + + // Gradient with start/endangle. + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.clamp, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + path = samplePathFromRect(rectBounds); + canvas.drawPath(path, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode repeat + rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.repeated, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + + path = samplePathFromRect(rectBounds); + canvas.drawPath(path, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode mirror + rectBounds = rectBounds.translate(kBoxWidth + 10, 0); + sweepGradient = GradientSweep(const Offset(0.5, 0.5), + colors, stops, TileMode.mirror, + math.pi / 6, 3 * math.pi / 4, + Matrix4.rotationZ(math.pi / 6.0).storage); + path = samplePathFromRect(rectBounds); + canvas.drawPath(path, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + canvas.restore(); + await _checkScreenshot(canvas, 'sweep_gradient_path'); + }, skip: isFirefox); + + /// Regression test for https://github.com/flutter/flutter/issues/74137. + test('Paints rotated and shifted linear gradient', () async { + final RecordingCanvas canvas = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + canvas.save(); + + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78),]; + const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + GradientLinear linearGradient = GradientLinear(const Offset(50, 50), + const Offset(200,130), + colors, stops, TileMode.clamp, + Matrix4.identity().storage); + + const double kBoxWidth = 150; + const double kBoxHeight = 80; + // Gradient with default center. + Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode repeat + rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); + linearGradient = GradientLinear(const Offset(50, 50), + const Offset(200,130), + colors, stops, TileMode.repeated, + Matrix4.identity().storage); + + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + canvas.restore(); + await _checkScreenshot(canvas, 'linear_gradient_rect_shifted'); + }, skip: isFirefox); + + /// Regression test for https://github.com/flutter/flutter/issues/82748. + test('Paints gradient with gradient stop outside range', () async { + final RecordingCanvas canvas = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + canvas.save(); + + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38)]; + const List stops = [0.0, 10.0]; + + final GradientLinear linearGradient = GradientLinear(const Offset(50, 50), + const Offset(200,130), + colors, stops, TileMode.clamp, + Matrix4.identity().storage); + + const double kBoxWidth = 150; + const double kBoxHeight = 80; + // Gradient with default center. + const Rect rectBounds = Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + canvas.restore(); + + final EngineCanvas engineCanvas = BitmapCanvas(screenRect, + RenderStrategy()); + canvas.endRecording(); + canvas.apply(engineCanvas, screenRect); + }); + + test('Paints clamped, rotated and shifted linear gradient', () async { + final RecordingCanvas canvas = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + canvas.save(); + + final SurfacePaint borderPaint = SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78),]; + const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + GradientLinear linearGradient = GradientLinear(const Offset(50, 50), + const Offset(200,130), + colors, stops, TileMode.clamp, + Matrix4.identity().storage); + + const double kBoxWidth = 150; + const double kBoxHeight = 80; + // Gradient with default center. + Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + // Tile mode repeat + rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); + linearGradient = GradientLinear(const Offset(50, 50), + const Offset(200,130), + colors, stops, TileMode.clamp, + Matrix4.identity().storage); + + canvas.drawRect(rectBounds, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds)); + canvas.drawRect(rectBounds, borderPaint); + + canvas.restore(); + await _checkScreenshot(canvas, 'linear_gradient_rect_clamp_rotated'); + }); +} + +Shader engineGradientToShader(GradientSweep gradient, Rect rect) { + return Gradient.sweep( + Offset(rect.left + gradient.center.dx * rect.width, + rect.top + gradient.center.dy * rect.height), + gradient.colors, gradient.colorStops, gradient.tileMode, + gradient.startAngle, + gradient.endAngle, + gradient.matrix4 == null ? null : + Float64List.fromList(gradient.matrix4!), + ); +} + +Shader engineLinearGradientToShader(GradientLinear gradient, Rect rect) { + return Gradient.linear(gradient.from, gradient.to, + gradient.colors, gradient.colorStops, gradient.tileMode, + gradient.matrix4 == null ? null : Float64List.fromList( + gradient.matrix4!.matrix), + ); +} + +Path samplePathFromRect(Rect rectBounds) => + Path() + ..moveTo(rectBounds.center.dx, rectBounds.top) + ..lineTo(rectBounds.left, rectBounds.bottom) + ..quadraticBezierTo(rectBounds.center.dx + 20, rectBounds.bottom - 40, + rectBounds.right, rectBounds.bottom) + ..close(); diff --git a/lib/web_ui/test/html/shaders/image_shader_golden_test.dart b/lib/web_ui/test/html/shaders/image_shader_golden_test.dart new file mode 100644 index 0000000000000..082ce39f1aa3c --- /dev/null +++ b/lib/web_ui/test/html/shaders/image_shader_golden_test.dart @@ -0,0 +1,150 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; + +import '../../common.dart'; +import '../screenshot.dart'; + +// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. +// https://github.com/flutter/flutter/issues/86623 + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 400.0; + const double screenHeight = 400.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + final HtmlImage testImage = createTestImage(); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + void _drawShapes(RecordingCanvas rc, SurfacePaint paint, Rect shaderRect) { + /// Rect. + rc.drawRect(shaderRect, paint); + shaderRect = shaderRect.translate(100, 0); + + /// Circle. + rc.drawCircle(shaderRect.center, shaderRect.width / 2, paint); + shaderRect = shaderRect.translate(110, 0); + + /// Oval. + rc.drawOval(Rect.fromLTWH(shaderRect.left, shaderRect.top, shaderRect.width, shaderRect.height / 2), paint); + shaderRect = shaderRect.translate(-210, 120); + + /// Path. + final Path path = Path() + ..moveTo(shaderRect.center.dx, shaderRect.top) + ..lineTo(shaderRect.right, shaderRect.bottom) + ..lineTo(shaderRect.left, shaderRect.bottom) + ..close(); + rc.drawPath(path, paint); + shaderRect = shaderRect.translate(100, 0); + + /// RRect. + rc.drawRRect(RRect.fromRectXY(shaderRect, 10, 20), paint); + shaderRect = shaderRect.translate(110, 0); + + /// DRRect. + rc.drawDRRect(RRect.fromRectXY(shaderRect, 20, 30), + RRect.fromRectXY(shaderRect.deflate(24), 16, 24), + paint); + shaderRect = shaderRect.translate(-200, 120); + } + + Future testImageShader( + TileMode tmx, TileMode tmy, String fileName, + {double maxDiffRatePercent = 0.0}) async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, screenWidth, screenHeight)); + //Rect shaderRect = const Rect.fromLTRB(20, 20, 100, 100); + const Rect shaderRect = Rect.fromLTRB(0, 0, 100, 100); + final SurfacePaint paint = Paint() as SurfacePaint; + paint.shader = + ImageShader(testImage, tmx, tmy, Matrix4.identity().toFloat64() + , filterQuality: FilterQuality.high); + + _drawShapes(rc, paint, shaderRect); + + expect(rc.renderStrategy.hasArbitraryPaint, isTrue); + await canvasScreenshot(rc, fileName, + region: screenRect, maxDiffRatePercent: maxDiffRatePercent); + } + + test('Should draw with tiled imageshader.', () async { + await testImageShader( + TileMode.repeated, TileMode.repeated, 'image_shader_tiled', + maxDiffRatePercent: 5.0); + }); + + test('Should draw with horizontally mirrored imageshader.', () async { + await testImageShader( + TileMode.mirror, TileMode.repeated, 'image_shader_horiz_mirror', + maxDiffRatePercent: 6.0); + }); + + test('Should draw with vertically mirrored imageshader.', () async { + await testImageShader( + TileMode.repeated, TileMode.mirror, 'image_shader_vert_mirror', + maxDiffRatePercent: 5.0); + }); + + test('Should draw with mirrored imageshader.', () async { + await testImageShader( + TileMode.mirror, TileMode.mirror, 'image_shader_mirror', + maxDiffRatePercent: 6.0); + }); + + test('Should draw with horizontal clamp imageshader.', () async { + await testImageShader( + TileMode.clamp, TileMode.repeated, 'image_shader_clamp_horiz', + maxDiffRatePercent: 13.0); + }, skip: isFirefox); + + test('Should draw with vertical clamp imageshader.', () async { + await testImageShader( + TileMode.repeated, TileMode.clamp, 'image_shader_clamp_vertical', + maxDiffRatePercent: 1.0); + }, skip: isFirefox); + + test('Should draw with clamp imageshader.', () async { + await testImageShader( + TileMode.clamp, TileMode.clamp, 'image_shader_clamp', + maxDiffRatePercent: 1.0); + }, skip: isFirefox); +} + +HtmlImage createTestImage() { + const int width = 16; + const int width2 = width ~/ 2; + const int height = 16; + final html.CanvasElement canvas = + html.CanvasElement(width: width, height: height); + final html.CanvasRenderingContext2D ctx = canvas.context2D; + ctx.fillStyle = '#E04040'; + ctx.fillRect(0, 0, width2, width2); + ctx.fill(); + ctx.fillStyle = '#40E080'; + ctx.fillRect(width2, 0, width2, width2); + ctx.fill(); + ctx.fillStyle = '#2040E0'; + ctx.fillRect(width2, width2, width2, width2); + ctx.fill(); + final html.ImageElement imageElement = html.ImageElement(); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []) as String; + return HtmlImage(imageElement, width, height); +} diff --git a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart new file mode 100644 index 0000000000000..75ba08279356b --- /dev/null +++ b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart @@ -0,0 +1,150 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; +import '../../common.dart'; +import '../screenshot.dart'; + +// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. +// https://github.com/flutter/flutter/issues/86623 + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + const double screenWidth = 500.0; + const double screenHeight = 500.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('Should draw linear gradient using rectangle.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); + final SurfacePaint paint = SurfacePaint()..shader = Gradient.linear( + Offset(shaderRect.left, shaderRect.top), + Offset(shaderRect.right, shaderRect.bottom), + const [Color(0xFFcfdfd2), Color(0xFF042a85)]); + rc.drawRect(shaderRect, paint); + expect(rc.renderStrategy.hasArbitraryPaint, isTrue); + await canvasScreenshot(rc, 'linear_gradient_rect', + region: screenRect, + maxDiffRatePercent: 0.01); + }); + + test('Should draw linear gradient with transform.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + final List angles = [0.0, 90.0, 180.0]; + double yOffset = 0; + for (final double angle in angles) { + final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); + final Matrix4 matrix = Matrix4.identity(); + matrix.translate(shaderRect.left, shaderRect.top); + matrix.multiply(Matrix4 + .rotationZ((angle / 180) * math.pi)); + final Matrix4 post = Matrix4.identity(); + post.translate(-shaderRect.left, -shaderRect.top); + matrix.multiply(post); + final SurfacePaint paint = SurfacePaint() + ..shader = Gradient.linear( + Offset(shaderRect.left, shaderRect.top), + Offset(shaderRect.right, shaderRect.bottom), + const [Color(0xFFFF0000), Color(0xFF042a85)], + null, + TileMode.clamp, + matrix.toFloat64()); + rc.drawRect(shaderRect, SurfacePaint() + ..color = const Color(0xFF000000)); + rc.drawOval(shaderRect, paint); + yOffset += 120; + } + expect(rc.renderStrategy.hasArbitraryPaint, isTrue); + await canvasScreenshot(rc, 'linear_gradient_oval_matrix', + region: screenRect, + maxDiffRatePercent: 0.2); + }); + + // Regression test for https://github.com/flutter/flutter/issues/50010 + test('Should draw linear gradient using rounded rect.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); + final SurfacePaint paint = SurfacePaint()..shader = Gradient.linear( + Offset(shaderRect.left, shaderRect.top), + Offset(shaderRect.right, shaderRect.bottom), + const [Color(0xFFcfdfd2), Color(0xFF042a85)]); + rc.drawRRect(RRect.fromRectAndRadius(shaderRect, const Radius.circular(16)), paint); + expect(rc.renderStrategy.hasArbitraryPaint, isTrue); + await canvasScreenshot(rc, 'linear_gradient_rounded_rect', + region: screenRect, + maxDiffRatePercent: 0.1); + }); + + test('Should draw tiled repeated linear gradient with transform.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + final List angles = [0.0, 30.0, 210.0]; + double yOffset = 0; + for (final double angle in angles) { + final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); + final SurfacePaint paint = SurfacePaint() + ..shader = Gradient.linear( + Offset(shaderRect.left, shaderRect.top), + Offset(shaderRect.left + shaderRect.width / 2, shaderRect.top), + const [Color(0xFFFF0000), Color(0xFF042a85)], + null, + TileMode.repeated, + Matrix4 + .rotationZ((angle / 180) * math.pi) + .toFloat64()); + rc.drawRect(shaderRect, SurfacePaint() + ..color = const Color(0xFF000000)); + rc.drawOval(shaderRect, paint); + yOffset += 120; + } + expect(rc.renderStrategy.hasArbitraryPaint, isTrue); + await canvasScreenshot(rc, 'linear_gradient_tiled_repeated_rect', + region: screenRect); + }, skip: isFirefox); + + test('Should draw tiled mirrored linear gradient with transform.', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + final List angles = [0.0, 30.0, 210.0]; + double yOffset = 0; + for (final double angle in angles) { + final Rect shaderRect = Rect.fromLTWH(50, 50 + yOffset, 100, 100); + final SurfacePaint paint = SurfacePaint() + ..shader = Gradient.linear( + Offset(shaderRect.left, shaderRect.top), + Offset(shaderRect.left + shaderRect.width / 2, shaderRect.top), + const [Color(0xFFFF0000), Color(0xFF042a85)], + null, + TileMode.mirror, + Matrix4 + .rotationZ((angle / 180) * math.pi) + .toFloat64()); + rc.drawRect(shaderRect, SurfacePaint() + ..color = const Color(0xFF000000)); + rc.drawOval(shaderRect, paint); + yOffset += 120; + } + expect(rc.renderStrategy.hasArbitraryPaint, isTrue); + await canvasScreenshot(rc, 'linear_gradient_tiled_mirrored_rect', + region: screenRect); + }, skip: isFirefox); +} diff --git a/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart new file mode 100644 index 0000000000000..0e82ea3aaa86f --- /dev/null +++ b/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart @@ -0,0 +1,135 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide TextStyle; +import '../../common.dart'; +import '../screenshot.dart'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + Future _testGradient(String fileName, Shader shader, + {Rect paintRect = const Rect.fromLTRB(50, 50, 300, 300), + Rect shaderRect = const Rect.fromLTRB(50, 50, 300, 300), + bool write = false, + double maxDiffRatePercent = 0, + Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { + final RecordingCanvas rc = RecordingCanvas(region); + final SurfacePaint paint = SurfacePaint()..shader = shader; + final Path path = Path(); + path.addRect(paintRect); + rc.drawPath(path, paint); + await canvasScreenshot(rc, fileName, write: write, region: region, + maxDiffRatePercent: maxDiffRatePercent); + } + + test('Should draw centered radial gradient.', () async { + const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); + await _testGradient( + 'radial_gradient_centered', + Gradient.radial( + Offset((shaderRect.left + shaderRect.right) / 2, + (shaderRect.top + shaderRect.bottom) / 2), + shaderRect.width / 2, + [ + const Color.fromARGB(255, 0, 0, 0), + const Color.fromARGB(255, 0, 0, 255) + ]), + shaderRect: shaderRect, + maxDiffRatePercent: 0.2); + }); + + test('Should draw right bottom centered radial gradient.', () async { + const Rect shaderRect = Rect.fromLTRB(50, 50, 300, 300); + await _testGradient( + 'radial_gradient_right_bottom', + Gradient.radial( + Offset(shaderRect.right, shaderRect.bottom), + shaderRect.width / 2, + [ + const Color.fromARGB(255, 0, 0, 0), + const Color.fromARGB(255, 0, 0, 255) + ], + ), + shaderRect: shaderRect, + maxDiffRatePercent: 0.3, + ); + }); + + test('Should draw with radial gradient with TileMode.clamp.', () async { + const Rect shaderRect = Rect.fromLTRB(50, 50, 100, 100); + await _testGradient( + 'radial_gradient_tilemode_clamp', + Gradient.radial( + Offset((shaderRect.left + shaderRect.right) / 2, + (shaderRect.top + shaderRect.bottom) / 2), + shaderRect.width / 2, + [ + const Color.fromARGB(255, 0, 0, 0), + const Color.fromARGB(255, 0, 0, 255) + ], + [0.0, 1.0], + TileMode.clamp, + ), + shaderRect: shaderRect, + maxDiffRatePercent: 0.2, + ); + }); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78),]; + const List colorStops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + test('Should draw with radial gradient with TileMode.repeated.', () async { + const Rect shaderRect = Rect.fromLTRB(50, 50, 100, 100); + await _testGradient( + 'radial_gradient_tilemode_repeated', + Gradient.radial( + Offset((shaderRect.left + shaderRect.right) / 2, + (shaderRect.top + shaderRect.bottom) / 2), + shaderRect.width / 2, + colors, + colorStops, + TileMode.repeated), + shaderRect: shaderRect, + region: const Rect.fromLTWH(0, 0, 600, 800)); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); + + test('Should draw with radial gradient with TileMode.mirrored.', () async { + const Rect shaderRect = Rect.fromLTRB(50, 50, 100, 100); + await _testGradient( + 'radial_gradient_tilemode_mirror', + Gradient.radial( + Offset((shaderRect.left + shaderRect.right) / 2, + (shaderRect.top + shaderRect.bottom) / 2), + shaderRect.width / 2, + colors, + colorStops, + TileMode.mirror), + shaderRect: shaderRect, + region: const Rect.fromLTWH(0, 0, 600, 800)); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox); +} diff --git a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart new file mode 100644 index 0000000000000..394d447ac520b --- /dev/null +++ b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart @@ -0,0 +1,222 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart' + hide ClipRectEngineLayer, BackdropFilterEngineLayer; +import 'package:ui/ui.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +import '../../common.dart'; + +/// To debug compositing failures on browsers, set this flag to true and run +/// flutter run -d chrome --web-renderer=html +/// test/golden_tests/engine/shader_mask_golden_test.dart --profile +const bool debugTest = false; + +Future main() async { + if (!debugTest) { + internalBootstrapBrowserTest(() => testMain); + } else { + _renderCirclesScene(BlendMode.color); + } +} + +// TODO(ferhat): unskip webkit tests once flakiness is resolved. See +// https://github.com/flutter/flutter/issues/76713 +// TODO(yjbanov): unskip Firefox tests when Firefox implements WebGL in headless mode. +// https://github.com/flutter/flutter/issues/86623 + +Future testMain() async { + setUp(() async { + debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + for (final html.Node scene in + domRenderer.sceneHostElement!.querySelectorAll('flt-scene')) { + scene.remove(); + } + initWebGl(); + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + /// Should render the picture unmodified. + test('Renders shader mask with linear gradient BlendMode dst', () async { + _renderCirclesScene(BlendMode.dst); + await matchGoldenFile('shadermask_linear_dst.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + /// Should render the gradient only where circles have alpha channel. + test('Renders shader mask with linear gradient BlendMode srcIn', () async { + _renderCirclesScene(BlendMode.srcIn); + await matchGoldenFile('shadermask_linear_srcin.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + test('Renders shader mask with linear gradient BlendMode color', () async { + _renderCirclesScene(BlendMode.color); + await matchGoldenFile('shadermask_linear_color.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + test('Renders shader mask with linear gradient BlendMode xor', () async { + _renderCirclesScene(BlendMode.xor); + await matchGoldenFile('shadermask_linear_xor.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + test('Renders shader mask with linear gradient BlendMode plus', () async { + _renderCirclesScene(BlendMode.plus); + await matchGoldenFile('shadermask_linear_plus.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + test('Renders shader mask with linear gradient BlendMode modulate', () async { + _renderCirclesScene(BlendMode.modulate); + await matchGoldenFile('shadermask_linear_modulate.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + test('Renders shader mask with linear gradient BlendMode overlay', () async { + _renderCirclesScene(BlendMode.overlay); + await matchGoldenFile('shadermask_linear_overlay.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + /// Should render the gradient opaque on top of content. + test('Renders shader mask with linear gradient BlendMode src', () async { + _renderCirclesScene(BlendMode.src); + await matchGoldenFile('shadermask_linear_src.png', + region: const Rect.fromLTWH(0, 0, 360, 200)); + }, skip: isSafari || isFirefox); + + /// Should render text with gradient. + test('Renders text with linear gradient shader mask', () async { + _renderTextScene(BlendMode.srcIn); + await matchGoldenFile('shadermask_linear_text.png', + region: const Rect.fromLTWH(0, 0, 360, 200), maxDiffRatePercent: 2.0); + }, skip: isSafari || isFirefox); +} + +Picture _drawTestPictureWithCircles( + Rect region, double offsetX, double offsetY) { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(region); + canvas.drawCircle(Offset(offsetX + 30, offsetY + 30), 30, + SurfacePaint()..style = PaintingStyle.fill); + canvas.drawCircle( + Offset(offsetX + 110, offsetY + 30), + 30, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFFFF0000)); + canvas.drawCircle( + Offset(offsetX + 30, offsetY + 110), + 30, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF00FF00)); + canvas.drawCircle( + Offset(offsetX + 110, offsetY + 110), + 30, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF0000FF)); + return recorder.endRecording(); +} + +void _renderCirclesScene(BlendMode blendMode) { + const Rect region = Rect.fromLTWH(0, 0, 400, 400); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture circles1 = _drawTestPictureWithCircles(region, 10, 10); + builder.addPicture(Offset.zero, circles1); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78), + ]; + const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + const Rect shaderBounds = Rect.fromLTWH(180, 10, 140, 140); + + final EngineGradient shader = GradientLinear( + Offset(200 - shaderBounds.left, 30 - shaderBounds.top), + Offset(320 - shaderBounds.left, 150 - shaderBounds.top), + colors, stops, TileMode.clamp, Matrix4.identity().storage); + + builder.pushShaderMask(shader, shaderBounds, blendMode, + oldLayer: null); + final Picture circles2 = _drawTestPictureWithCircles(region, 180, 10); + builder.addPicture(Offset.zero, circles2); + builder.pop(); + + domRenderer.sceneHostElement!.append(builder.build().webOnlyRootElement!); +} + +Picture _drawTestPictureWithText( + Rect region, double offsetX, double offsetY) { + final EnginePictureRecorder recorder = EnginePictureRecorder(); + final RecordingCanvas canvas = recorder.beginRecording(region); + const String text = 'Shader test'; + + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'Roboto', + fontSize: 40.0, + ); + + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(paragraphStyle); + builder.pushStyle(EngineTextStyle.only(color: const Color(0xFFFF0000))); + builder.addText(text); + final CanvasParagraph paragraph = builder.build(); + + const double maxWidth = 200 - 10; + paragraph.layout(const ParagraphConstraints(width: maxWidth)); + canvas.drawParagraph(paragraph, Offset(offsetX, offsetY)); + return recorder.endRecording(); +} + +void _renderTextScene(BlendMode blendMode) { + const Rect region = Rect.fromLTWH(0, 0, 600, 400); + + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + final Picture textPicture = _drawTestPictureWithText(region, 10, 10); + builder.addPicture(Offset.zero, textPicture); + + const List colors = [ + Color(0xFF000000), + Color(0xFFFF3C38), + Color(0xFFFF8C42), + Color(0xFFFFF275), + Color(0xFF6699CC), + Color(0xFF656D78), + ]; + const List stops = [0.0, 0.05, 0.4, 0.6, 0.9, 1.0]; + + const Rect shaderBounds = Rect.fromLTWH(180, 10, 140, 140); + + final EngineGradient shader = GradientLinear( + Offset(200 - shaderBounds.left, 30 - shaderBounds.top), + Offset(320 - shaderBounds.left, 150 - shaderBounds.top), + colors, stops, TileMode.clamp, Matrix4.identity().storage); + + builder.pushShaderMask(shader, shaderBounds, blendMode, + oldLayer: null); + + final Picture textPicture2 = _drawTestPictureWithText(region, 180, 10); + builder.addPicture(Offset.zero, textPicture2); + builder.pop(); + + domRenderer.sceneHostElement!.append(builder.build().webOnlyRootElement!); +} diff --git a/lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart b/lib/web_ui/test/html/shadow_golden_test.dart similarity index 87% rename from lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart rename to lib/web_ui/test/html/shadow_golden_test.dart index 1822bc1c3e985..b1b4c7596b5ca 100644 --- a/lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart +++ b/lib/web_ui/test/html/shadow_golden_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; @@ -12,7 +11,7 @@ import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; -import 'scuba.dart'; +import 'paragraph/text_scuba.dart'; const Color _kShadowColor = Color.fromARGB(255, 0, 0, 0); @@ -20,10 +19,10 @@ void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { - final Rect region = Rect.fromLTWH(0, 0, 550, 300); +Future testMain() async { + const Rect region = Rect.fromLTWH(0, 0, 550, 300); - SurfaceSceneBuilder builder; + late SurfaceSceneBuilder builder; setUpStableTestFonts(); @@ -32,12 +31,12 @@ void testMain() async { }); void _paintShapeOutline() { - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); canvas.drawRect( const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0), SurfacePaint() - ..color = Color.fromARGB(255, 0, 0, 255) + ..color = const Color.fromARGB(255, 0, 0, 255) ..style = PaintingStyle.stroke ..strokeWidth = 1.0, ); @@ -52,7 +51,7 @@ void testMain() async { canvas.drawRect( shadowBounds, SurfacePaint() - ..color = Color.fromARGB(255, 0, 255, 0) + ..color = const Color.fromARGB(255, 0, 255, 0) ..style = PaintingStyle.stroke ..strokeWidth = 1.0, ); @@ -67,7 +66,7 @@ void testMain() async { path: path, elevation: elevation, shadowColor: _kShadowColor, - color: Color.fromARGB(255, 255, 255, 255), + color: const Color.fromARGB(255, 255, 255, 255), ); builder.pop(); // physical shape _paintShapeOutline(); @@ -81,7 +80,7 @@ void testMain() async { ..addRect(const Rect.fromLTRB(0, 0, 20, 20)); builder.pushOffset(offset.dx, offset.dy); - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); canvas .debugEnforceArbitraryPaint(); // make sure DOM canvas doesn't take over @@ -107,7 +106,7 @@ void testMain() async { ..close(); builder.pushOffset(offset.dx, offset.dy); - final EnginePictureRecorder recorder = PictureRecorder(); + final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); canvas .debugEnforceArbitraryPaint(); // make sure DOM canvas doesn't take over @@ -122,7 +121,7 @@ void testMain() async { SurfacePaint() ..style = PaintingStyle.stroke ..strokeWidth = 1 - ..color = Color.fromARGB(255, 0, 0, 255), + ..color = const Color.fromARGB(255, 0, 0, 255), ); builder.addPicture(Offset.zero, recorder.endRecording()); _paintShadowBounds(path, elevation); @@ -157,13 +156,13 @@ void testMain() async { builder.pop(); - final html.Element sceneElement = builder.build().webOnlyRootElement; - html.document.body.append(sceneElement); + final html.Element sceneElement = builder.build().webOnlyRootElement!; + html.document.body!.append(sceneElement); await matchGoldenFile( 'shadows.png', region: region, - maxDiffRatePercent: 0.0, + maxDiffRatePercent: 0.23, pixelComparison: PixelComparison.precise, ); }, diff --git a/lib/web_ui/test/html/testimage.dart b/lib/web_ui/test/html/testimage.dart new file mode 100644 index 0000000000000..c1b569063a4aa --- /dev/null +++ b/lib/web_ui/test/html/testimage.dart @@ -0,0 +1,125 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +import 'package:ui/src/engine.dart'; + +/// 50x50 pixel flutter logo image that contains alpha ramps and colors +/// specifically to transparency and blending. +const String _flutterLogoBase64 = + 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAKRlWElm' + 'TU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgExAAIAAAAg' + 'AAAAWodpAAQAAAABAAAAegAAAAAAAABIAAAAAQAAAEgAAAABQWRvYmUgUGhvdG9zaG9wIENT' + 'NiAoTWFjaW50b3NoKQAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAMqADAAQAAAABAAAAMgAA' + 'AABWBXsWAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEemlUWHRYTUw6Y29tLmFkb2JlLnhtcAAA' + 'AAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENv' + 'cmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5' + 'OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjph' + 'Ym91dD0iIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94' + 'YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5j' + 'b20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0i' + 'aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0i' + 'aHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8eG1wTU06SW5zdGFu' + 'Y2VJRD54bXAuaWlkOjMyOERERjc5ODRCRjExRUE5QUE4OEM5NTZDREM5QkUyPC94bXBNTTp' + 'JbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD54bXAuZGlkOjMyOERERj' + 'dBODRCRjExRUE5QUE4OEM5NTZDREM5QkUyPC94bXBNTTpEb2N1bWVudElEPgogICAgICAgI' + 'CA8eG1wTU06T3JpZ2luYWxEb2N1bWVudElEPnhtcC5kaWQ6MDE4MDExNzQwNzIwNjgxMTgy' + 'MkFBQjU0OEFBMDMwM0E8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHht' + 'cE1NOkRlcml2ZWRGcm9tIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICA' + 'gPHN0UmVmOmluc3RhbmNlSUQ+eG1wLmlpZDowNDgwMTE3NDA3MjA2ODExODIyQUFCNTQ4QU' + 'EwMzAzQTwvc3RSZWY6aW5zdGFuY2VJRD4KICAgICAgICAgICAgPHN0UmVmOmRvY3VtZW50SU' + 'Q+eG1wLmRpZDowMTgwMTE3NDA3MjA2ODExODIyQUFCNTQ4QUEwMzAzQTwvc3RSZWY6ZG9jd' + 'W1lbnRJRD4KICAgICAgICAgPC94bXBNTTpEZXJpdmVkRnJvbT4KICAgICAgICAgPHhtcDpD' + 'cmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpPC94bXA6Q3JlYXRv' + 'clRvb2w+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb2' + '4+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhP' + 'gr/+ApQAAAQNUlEQVRoBbVaDYxdRRWemTv3vt2WbrctpRYxtCVU4io1LomElrjYWnbbgtr2' + 'CbISwGpR0UBUjD8YHyZCNCqIP5GmtLWSAn3SVYqw7PbnFUQFXGOMRUFlW9G2gKalu/veu38' + 'zfufce3ff275dWtBJ5s7cuTPnnO+cM2d+3pN2urDiVSHEVCFuOpgT35vVIuaLV8Tgtw8Icf' + 'PZkZgntDggrhNCbhHCuihD9J402Y4OLUulqNy1+iwvFIscaaqRMWqyQRofIxFH2ohpwqjDc' + 't/OZyyYSTAVHQUtSoVIfPiL7xWu3i2i0KA7PnFmstpaEVMLBnHm1rFHhEbiQZ9PKtl83pPF' + 'YnB0xVVne2HlKcdz5wgTC62cZLwlGdJUSxXtWnngFItIOhdzj3xeiWIxzrpPVmpAZg4EJjc' + 'tAcmqazqNxjnJVwEOnKjDhMmuX+/KDRuCoeVXn9EcDf/K0c4cEwYBBmgBjSUJZVbnJiuh9' + '1BZ4ZnYREpNucgtPfAMW7VYjHjM7Ldlg1MaJxYaxlnNzVCUP4THrLRTdRiVWVaEcQ54fpu2' + 'JoTTl9rid+3tBCL8a1d3S1M0/ARAnGWiyEcfjK+xaB0Icg0ZQXGekEoo4V0qdwNEatVR+q8' + '8O6kCqZ+Wx0QPD6jgeTop72Xxd26Yx0/xYlJAFmJa4xdZO74kcwIgPpObF//rce3qhSYMAw' + 'zIpaqsEYSqaE0Kcmso04F/q/fr/uKeE0AQm5OwCCwqvCzn90MzMHEbsijsJ4f1RBuysFCa' + 'TGUaA0C1bGKjKufFh/Zo111kopAsQXQnTABAvu9IR6OiunX/jocagpiQQv0HDYJkhiS1Jc' + 'V+Lupe0g71BRg7mNjsbmHnml7t6IswJ3wog9xpggR4Uhh4mFTapTjwCb17xzbblgSJCQa9' + 'ZvOkIXGy0SkIjihh5+odcKfl6cSeBAQoSrinhYm1qwDi887uHXdbml/7i2MKnYzxBN9eFx' + 'ArCgqWYBDBpWu2wp0+aAKOTll0m4AdQbBGuZ4DELcCxHfINcXAwIRBhAmdxGQ/ZSAEQogC' + '0w861/7Q9dyrGQTNqwkTzxaAEBFAaBOa7zq7dhTIqqJUogk/2XQ6uck+Ie8GH9i7O0oKjI3' + 'ftfabAPEpE/i00mPFz1IDmZKmUHmua6J4g7O753NMq9hGobDBADRaK7NcaJuNbkLMoaV5gn' + 'RqFunocGjr4XeuvcXT7heMXyWXIPLMiHk0FEsEsIQXh/F9zq6fX8/9CgWsxYWG4Zy/1zzmz' + 'n2e6U9VpMPGacIP47vbdqzaAxvCYEX+RtfRd2Jix8IaGq/qdJoteCkBdPCldnNxFO9EiL2' + 'cmmsDRdrtJIv3X+Rdc/6TQRiQAoj3qPyTGGuMNhhj/7QhCDrz61zlEAjonbwCIGrTOBD4xC' + 'BsFO3R/T2vCQKupCSiGkpsYcU25NORg7Q9eu6Fg7POu+lOMWN6kxyB65EUWXpNIAmIYlBen' + 'r/CddRGG4UEAiHUJrvAGmIZ0bQMEGJhiehpZ/Gi94n+nmTxzPZP4zqnr5mGSa7lyE3UDnB' + 'UiJmtrUJUA+u2NktaySEHPTjVazRrTUvb1ZWjnWz10vzKZi3vp9ULoacxiHpr+ADhYa/1p' + '8PD8zpkoWDYNTG/xrEY/5pJRuWx9GNoDBhjl18ul7EJULzFoBBYmyYEwpZ49FG/0pV/T07Z' + 'h0l+YwlJaolaKmk9VWegHJ1DdHpBTZ1+8Vt+c0eFN5SYXw2GTNREpNJ9P0qJzVgSVFDWA8g' + 'INASSudPIijUXuNbsojkFrYR1IGrppdZAE0A4HhR4WLkti+XPtxwjWhcMDJwKiEy2sTLlVc' + 'uyZp5zvxPmSAbC78q/TRtbgiV1DEcHYneU0GgFNACC1IemUCkFEOaoEqctlo9sOZLRGpPo/' + '1ers0jG+Njy/HzHmCeUElPh5yE6aZadHg1B4GCklItwU45k08Wy76eDGa3/jei1TDOK9W2' + 'jQHgyYmIPL/3wnGkqfsJx5EwTxwECRmKJ+nFsCSKJ5gjLFIGIY6kvyT123/4sSOCjPJVcEi' + 'WW55g4lk63TOjXLnlgttgd7fhAa5OuPgk/fzOBwHCP3b8WBDWkcwLfAULCPSWCSW6Z91jxab' + 'YEgkTBpmvMKUC5RF7CUa1VtNJ5pkGqFaT+s04ORhvCwY5rm07zjpccxznHhFEV3Wg7bngC0J' + 'h6GqQxCok43SkRW3mZ7r2/lFoCIAqqIJPtx3K7fOp0QWfoydM/8Xn1S6vVzXNu9ntF75xO0Z' + 'mNsbRI0mgc7vlJ9WSyjwnF1zbUfJZ3fJfWDk53URmvCQjqjmU1nc/ULUkSNzfQuMLNSGzVlb' + 'qv+HA6J/y8zTsAwVv8lfHSexGx3zsim4fQPwunqI4JwHXI1wIuj5/RL5eY86f+Otj31c6mTr' + 'o0mIOcbUcgDAHBZjIZnxLhHYbQe3EeWOLO6NeOWgwQw+iTA3AIkvQb8yLGQJToYAQUjo6ts0' + '73bX8guz1JLcEgVkXLtjrK6Q5tSCvz3FHLMpnkQQIlWiZvBWPpisA/duvXf3v7FtEhvp52' + 'HQPCwmRWyZRB73CNDpwH7PLV/8FqBxFZ97Ryk6gpnaxg5CSkxRyaYoy4ESA28ekOtyckU0E' + 'UmPqqeOkPYK6rQxMeBxtsKhOmia8k9XFWiRylWlUgbnu6+R8F0GoBH+qe5URaFiUJ91yted' + 'C+2Kq+HWutMT3KUdNgPbgW+SSW8vpMExFhlkDILzt9D97F84sWu4S2gnrtqnjZ7VKpGyIT0f' + '2lQxrJMt5JO2nmCw36FmuAcEJz1/bcL7+C79ibHKM+pLTaDFWTTKPg6uoKvs2+q/p7VsMQvT' + 'D1DHSukHsBJM0FIgYQ0ldStUTWfMPp+9ntPA7WJBAdogPbCBGvsku/BDG/iHu2oxhDWiRmpI' + 'AYQoAO5awuqa3iKDFdhXbjTm/fjeDPboCoReNGQaA9fZdp2xgtsGH6fPbmczNGymDRZRhUQn' + 'UmBKxAWxlBHxxmGmPvcPt6biENi/R0RyBKshQttUtuALvbsB/7NyihyygI0ABjChDImKoRWQ' + 'FtFSy4s5zAbtvplT6O/mJADIwGBLyOAsEcI2GjRLGkAIl60gY2JCPtPMAWu9IkDBewMMcrMe' + 'A3YNQK34ab2Qosejri8I+dXT2fpf50ZqfTXbttdwnEvOicaw6Yl+4oi+rLjlUkOBNHRxKYB' + 'E8si0WT2iAERcZZOrA7dub2daMu8ohq7aKdvlMiRVCdAWCK8ThugyIQSfGe0IL0iUXwkRODo' + 'WuZnTvL0puyCjr9Iz7QAacVN4Fbnf6eT1JHW8ANSgpiQA6EC6IFq+FyP8BN+n9eEEeiEVnF' + 'tbuC8BYMUhAQiN6BLgEhzUzHyN6HcvvWEk0K2UWOlPQ2mjIgMeYtA8KXpMzeUyuTpdki2VC' + 'Jicur/C+3HcWEXQlBDkN7jwDEtdSH5gWdLcgSBGK+Pfd9WBDvRph71REKBzZrB+3hqCICEF' + 'YQnMAkIFDSnCvD3VsB4okFB5rX4N122A5dlA1v3OuFz0DAAuRaRBd2G811QPBR0Lmc3ayv5' + '2VldCe2UeuonUHgLqvNtnkE4hx7zkUw8T3wAdwa2ypOKwYzPoyFiV4Qh7A1qAHDmpMj6DcN' + 'IH4/52/hmu+f+6hPIMg1iX6DlAEJMW+5DhdDCdciS3Od3pN8wjaeCLKbJdehB+md3alQiF' + 'NLBPPtwkU4c2zGygJCxoeGcPUqcQam5VuJEPP8gDgsFoi52MNoeKmhfdt0x8q/tLwi8pvO' + 'e3IonV+TnVPoG+UIkx3GQ+IYQnakOjKVaWoIhL7RTSKH2DbcPcGdUu2FC+yCc42JtkB4zB' + 'U7hJ4uaAa8XIIwgdEAU4EMg+KInC/mRtq6LdLEL055VV1535l7/r0errkBVs2EaFCSiKTtD' + 'AwDgbK4KzZfKYgxJMmXBpTqmmgni0PiPDvvPBk7GyH0TAwsI5TiB5w6xWAY9I8nxVEf7jZN' + 'TvEWSOdwa1V//P7mPxxotwLziwWsY0EvtI5A+dhfWPqVaSfybGS6RSGjyAP/PCTmf+w2MfuM' + 'aeI4lkefGKXphDmSfRhXMmAbOXOxZM2AiY+DOKYDbvKtCGBxMGOGXNJ7bGTgKeWPhH82T93' + 'bdYRAEM0Bub2G/Tgu9a9kDXLJzCqoS3oHD4nfXlCX+J7mkwNCoRHuf9D9+14o7CaAyAEQX' + 'Sv5VCLeBtiIMBPYLmHoaNyiDcaV4mePlje8Y9G06++5O5HzQ7HIb69d+JLm+if5DB0lKFe' + 'w5HHJ75JAMEDwIZ5JPjkgoADksdgr9KAc7MeK/DVw8gDCxx4NHgTCsAATNSCsXJwsnw/K2' + 'z8TlO98Z6wXRkfiYdXe3L35W0RKFAEG6xHXGz/IA+goQfdaTeBH9WY+xBnrsxUYAPFMrDQ' + 'ZMYwdly7BBETIBJiHsFP+NgRvggWqWMwpI3oBmMj5Vj7nl39xvV/5xrt85/zAt1W4obWHs' + 'OAvabpq0y1MFQGkARiyBCVypz3IjyD3wgMeQ/nwSLn6lMi5EWIvpocEIALFwHz2fXQ6+YR' + 'whc40+eOzo7OvwvRchw0JXaZBKTn8RvycrfReJ6ufXizkhcmSaHCKhPWgPEVXSrOMUL/wt1' + '33vYQpxTuOq8nrpM8FC5u683dh0SqDP8kxKv+pWYSY0BmcNoSIZAf1wW04Nf0E95dNiMJlg' + 'KgGu7v94NoLq84FcdUGsIVUsFbq7zguIigcQgC8tKl780eJXJI4eGcvXMIKKst79+7lZaJ9f' + 'Xeuis0sZKAzPeaNHc2nDoTYJGBoyZD/kINbrWl60Kq/5oJf5YcrH1tStctUNdROJdJuFf' + 'eSVei7CgCcMZbc5hDm1grvqs15tkbh1jrtMgtEFQrFlH/0o1fY5Q5VeIWniZ9k0ISpOb8' + '+IMyJLUM18aJ+blP49BW9w99Z5oUXNg+FWlWMlVV4DJUVSAMtphlC4NcICptHsIG9PNd9T' + 'xfmCs2XE8AwcTyKbc8ykMMWgZAOfpJ3z2QZZMP59QMhLmSZNPq89O4HNsYt7uPWiTUCJB1x' + 'y7AENomU+VcA/BKQvBNAjIQmASY2l+srN1+cgDnRxTIwSakoaJQ5SzpiEKAkvzEgRL0m+lS' + '3fnQjzn/PQEsKAQCLphgBGGQLUJSTd2onUABCIF9WNrrMvWLTu0QBR+zJwjJub2ENuiAhA' + 'NiEZtmOvHEg48Hcv24z/ABnGUnniFeh46HxGYCGpDVDCDzD+M0Ll14KkyDuyn3knrfWKia' + 'xQs2TNtcYC08YwrpFgOiaCVmB1v8ykTbZQnu19/zgSmXs6QjKFHKZD52U6pIBJPoGc+G6i' + 'koVGq9PFK/5FyISxuA7p7R+7c1vEqGzAusRBQwk2j0mabSSNbzhMgPTsVe7Zx54O85TYz9b' + 'G7q0QYqx43NQp5J+DyaxgBqC4d9IEn9j8Z8VxRvogo76E5ik7C7gmmjkHXjFDz64oKFxDs5' + 'rMQ4IqP4fUo0219/tijMXppoFq1LKbmHySy2/PY/v9H50hhEz8KvE0RkS2xhsP8YlvvGZ3S' + 'yapiT0mk/DqjIsBcr/Atffr8/hCjApAAAAAElFTkSuQmCC'; + +HtmlImage createFlutterLogoTestImage() { + return HtmlImage( + html.ImageElement() + ..src = 'data:text/plain;base64,$_flutterLogoBase64', + 50, + 50, + ); +} diff --git a/lib/web_ui/test/keyboard_converter_test.dart b/lib/web_ui/test/keyboard_converter_test.dart new file mode 100644 index 0000000000000..6183097f65a02 --- /dev/null +++ b/lib/web_ui/test/keyboard_converter_test.dart @@ -0,0 +1,1022 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:meta/meta.dart' show isTest; +import 'package:quiver/testing/async.dart'; +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +const int kLocationLeft = 1; +const int kLocationRight = 2; +const int kLocationNumpad = 3; + +final int kPhysicalKeyA = kWebToPhysicalKey['KeyA']!; +final int kPhysicalKeyE = kWebToPhysicalKey['KeyE']!; +final int kPhysicalKeyU = kWebToPhysicalKey['KeyU']!; +final int kPhysicalDigit1 = kWebToPhysicalKey['Digit1']!; +final int kPhysicalNumpad1 = kWebToPhysicalKey['Numpad1']!; +final int kPhysicalShiftLeft = kWebToPhysicalKey['ShiftLeft']!; +final int kPhysicalShiftRight = kWebToPhysicalKey['ShiftRight']!; +final int kPhysicalMetaLeft = kWebToPhysicalKey['MetaLeft']!; +final int kPhysicalTab = kWebToPhysicalKey['Tab']!; +final int kPhysicalCapsLock = kWebToPhysicalKey['CapsLock']!; +final int kPhysicalScrollLock = kWebToPhysicalKey['ScrollLock']!; + +const int kLogicalKeyA = 0x00000000061; +const int kLogicalKeyU = 0x00000000075; +const int kLogicalDigit1 = 0x00000000031; +final int kLogicalNumpad1 = kWebLogicalLocationMap['1']![kLocationNumpad]!; +final int kLogicalShiftLeft = kWebLogicalLocationMap['Shift']![kLocationLeft]!; +final int kLogicalShiftRight = kWebLogicalLocationMap['Shift']![kLocationRight]!; +final int kLogicalMetaLeft = kWebLogicalLocationMap['Meta']![kLocationLeft]!; +const int kLogicalTab = 0x0000000009; +final int kLogicalCapsLock = kWebToLogicalKey['CapsLock']!; +final int kLogicalScrollLock = kWebToLogicalKey['ScrollLock']!; + +typedef VoidCallback = void Function(); + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + test('KeyData.toString', () { + expect(const ui.KeyData( + type: ui.KeyEventType.down, + physical: 0x700e5, + logical: 0x61, + character: 'A', + timeStamp: Duration.zero, + synthesized: false, + ).toString(), 'KeyData(type: down, physical: 0x700e5, logical: 0x61 (Unicode), character: "A" (0x41))'); + + expect(const ui.KeyData( + type: ui.KeyEventType.up, + physical: 0x700e6, + logical: 0x100000061, + character: '\n', + timeStamp: Duration.zero, + synthesized: true, + ).toString(), r'KeyData(type: up, physical: 0x700e6, logical: 0x100000061 (Unprintable), character: "\n" (0x0a), synthesized)'); + + expect(const ui.KeyData( + type: ui.KeyEventType.repeat, + physical: 0x700e7, + logical: 0x9900000071, + character: null, + timeStamp: Duration.zero, + synthesized: false, + ).toString(), 'KeyData(type: repeat, physical: 0x700e7, logical: 0x9900000071, character: )'); + }); + + test('Single key press, repeat, and release', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + // Only handle down events + return key.type == ui.KeyEventType.down; + }); + bool preventedDefault = false; + void onPreventDefault() { preventedDefault = true; } + + converter.handleEvent(keyDownEvent('KeyA', 'a') + ..timeStamp = 1 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1), + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, isTrue); + preventedDefault = false; + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..timeStamp = 1.5 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1, microseconds: 500), + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, isFalse); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..timeStamp = 1500 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: const Duration(seconds: 1, milliseconds: 500), + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, isFalse); + + converter.handleEvent(keyUpEvent('KeyA', 'a') + ..timeStamp = 2000.5 + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + timeStamp: const Duration(seconds: 2, microseconds: 500), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + expect(preventedDefault, isFalse); + }); + + test('Release modifier during a repeated sequence', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + // Only handle down events + return key.type == ui.KeyEventType.down; + }); + bool preventedDefault = false; + void onPreventDefault() { preventedDefault = true; } + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + expect(preventedDefault, isTrue); + preventedDefault = false; + + converter.handleEvent(keyDownEvent('KeyA', 'A', kShift) + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'A', + ); + expect(preventedDefault, isTrue); + preventedDefault = false; + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift) + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'A', + ); + expect(preventedDefault, isFalse); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + expect(preventedDefault, isFalse); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, isFalse); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a') + ..onPreventDefault = onPreventDefault + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.repeat, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + expect(preventedDefault, isFalse); + + converter.handleEvent(keyUpEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + expect(preventedDefault, isFalse); + }); + + test('Distinguish between left and right modifiers', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + }); + + test('Distinguish between normal and numpad digits', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + converter.handleEvent(keyDownEvent('Digit1', '1', 0, 0)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalDigit1, + logical: kLogicalDigit1, + character: '1', + ); + + converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalNumpad1, + logical: kLogicalNumpad1, + character: '1', + ); + + converter.handleEvent(keyUpEvent('Digit1', '1', 0, 0)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalDigit1, + logical: kLogicalDigit1, + character: null, + ); + + converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalNumpad1, + logical: kLogicalNumpad1, + character: null, + ); + }); + + test('Dead keys are distinguishable', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + // The absolute values of the following logical keys are not guaranteed. + const int kLogicalAltE = 0x1740070008; + const int kLogicalAltU = 0x1740070018; + const int kLogicalAltShiftE = 0x1760070008; + // The values must be distinguishable. + expect(kLogicalAltE, isNot(equals(kLogicalAltU))); + expect(kLogicalAltE, isNot(equals(kLogicalAltShiftE))); + + converter.handleEvent(keyDownEvent('AltLeft', 'Alt', kAlt, kLocationLeft)); + + converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyE, + logical: kLogicalAltE, + character: null, + ); + + converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalKeyE, + logical: kLogicalAltE, + character: null, + ); + + converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyU, + logical: kLogicalAltU, + character: null, + ); + + converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalKeyU, + logical: kLogicalAltU, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kAlt | kShift, kLocationLeft)); + + // This does not actually produce a Dead key on macOS (US layout); just for + // testing. + converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyE, + logical: kLogicalAltShiftE, + character: null, + ); + + converter.handleEvent(keyUpEvent('AltLeft', 'Alt', kShift, kLocationLeft)); + + converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalKeyE, + logical: kLogicalAltShiftE, + character: null, + ); + + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); + }); + + test('Duplicate down is ignored', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + bool preventedDefault = false; + void onPreventDefault() { preventedDefault = true; } + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expect(preventedDefault, isTrue); + preventedDefault = false; + // A KeyUp of ShiftLeft is missed due to loss of focus. + + keyDataList.clear(); + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expect(keyDataList, hasLength(1)); + expect(keyDataList[0].physical, 0); + expect(keyDataList[0].logical, 0); + expect(preventedDefault, isTrue); + + keyDataList.clear(); + converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft) + ..onPreventDefault = onPreventDefault + ); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + expect(preventedDefault, isTrue); + }); + + test('Duplicate ups are skipped', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + bool preventedDefault = false; + void onPreventDefault() { preventedDefault = true; } + + // A KeyDown of ShiftRight is missed due to loss of focus. + converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight) + ..onPreventDefault = onPreventDefault + ); + expect(keyDataList, hasLength(1)); + expect(keyDataList[0].physical, 0); + expect(keyDataList[0].logical, 0); + expect(preventedDefault, isTrue); + }); + + test('Conflict from multiple keyboards do not crash', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + // Same layout + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + + // Different layout + converter.handleEvent(keyDownEvent('KeyA', 'a')); + converter.handleEvent(keyDownEvent('KeyA', 'u')); + converter.handleEvent(keyUpEvent('KeyA', 'u')); + converter.handleEvent(keyUpEvent('KeyA', 'a')); + + // Passes if there's no crash, and states are reset after everything is released. + keyDataList.clear(); + converter.handleEvent(keyDownEvent('KeyA', 'a')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + + converter.handleEvent(keyDownEvent('KeyU', 'u')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyU, + logical: kLogicalKeyU, + character: 'u', + ); + }); + + testFakeAsync('CapsLock down synthesizes an immediate cancel on macOS', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }, onMacOs: true); + bool preventedDefault = false; + void onPreventDefault() { preventedDefault = true; } + + // A KeyDown of ShiftRight is missed due to loss of focus. + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock') + ..onPreventDefault = onPreventDefault + ); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + expect(preventedDefault, isTrue); + keyDataList.clear(); + preventedDefault = false; + + async.elapse(const Duration(microseconds: 1)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + synthesized: true, + ); + expect(preventedDefault, isFalse); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock') + ..onPreventDefault = onPreventDefault + ); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + expect(preventedDefault, isTrue); + keyDataList.clear(); + preventedDefault = false; + + async.elapse(const Duration(microseconds: 1)); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + synthesized: true, + ); + expect(preventedDefault, isFalse); + keyDataList.clear(); + + // Another key down works + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + + // Schedules are canceled after disposal + converter.dispose(); + async.elapse(const Duration(seconds: 10)); + expect(keyDataList, isEmpty); + }); + + testFakeAsync('CapsLock behaves normally on non-macOS', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(const Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(const Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + + converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalCapsLock, + logical: kLogicalCapsLock, + character: null, + ); + }); + + testFakeAsync('Key guards: key down events are guarded', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 200), + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + keyDataList.clear(); + + // Keyup of KeyA is omitted due to being a shortcut. + + async.elapse(const Duration(milliseconds: 2500)); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1200), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2700), + type: ui.KeyEventType.up, + physical: kPhysicalMetaLeft, + logical: kLogicalMetaLeft, + character: null, + ); + async.elapse(const Duration(milliseconds: 100)); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2800), + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2900), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Key guards: key repeated down events refreshes guards', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); + async.elapse(const Duration(milliseconds: 400)); + + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 600); + async.elapse(const Duration(milliseconds: 50)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 650); + async.elapse(const Duration(milliseconds: 50)); + converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a', kMeta)..timeStamp = 700); + + // Keyup of KeyA is omitted due to being a shortcut. + + async.elapse(const Duration(milliseconds: 2500)); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 1700), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + synthesized: true, + ); + keyDataList.clear(); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3200), + type: ui.KeyEventType.up, + physical: kPhysicalMetaLeft, + logical: kLogicalMetaLeft, + character: null, + ); + async.elapse(const Duration(milliseconds: 100)); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3300), + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 3400), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Key guards: cleared by keyups', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }); + + converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 200), + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + keyDataList.clear(); + async.elapse(const Duration(milliseconds: 500)); + + converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 700); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 800), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + keyDataList.clear(); + async.elapse(const Duration(milliseconds: 2000)); + expect(keyDataList, isEmpty); + + // Key A states are cleared + converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2800), + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + async.elapse(const Duration(milliseconds: 100)); + + converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); + expectKeyData(keyDataList.last, + timeStamp: const Duration(milliseconds: 2900), + type: ui.KeyEventType.up, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: null, + ); + }); + + testFakeAsync('Lock flags of other keys', (FakeAsync async) { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + keyDataList.clear(); + + async.elapse(const Duration(seconds: 10)); + expect(keyDataList, isEmpty); + + converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); + expect(keyDataList, hasLength(1)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + keyDataList.clear(); + + converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + + converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.up, + physical: kPhysicalScrollLock, + logical: kLogicalScrollLock, + character: null, + ); + }); + + test('Deduce modifier key up from modifier field', () { + final List keyDataList = []; + final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { + keyDataList.add(key); + return true; + }, onMacOs: false); + + converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + ); + + converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + ); + keyDataList.clear(); + + // The release of the shift keys are omitted + + converter.handleEvent(keyDownEvent('KeyA', 'a')); + expect(keyDataList, hasLength(3)); + expectKeyData(keyDataList[0], + type: ui.KeyEventType.up, + physical: kPhysicalShiftLeft, + logical: kLogicalShiftLeft, + character: null, + synthesized: true, + ); + expectKeyData(keyDataList[1], + type: ui.KeyEventType.up, + physical: kPhysicalShiftRight, + logical: kLogicalShiftRight, + character: null, + synthesized: true, + ); + expectKeyData(keyDataList.last, + type: ui.KeyEventType.down, + physical: kPhysicalKeyA, + logical: kLogicalKeyA, + character: 'a', + ); + }); +} + +class MockKeyboardEvent implements FlutterHtmlKeyboardEvent { + MockKeyboardEvent({ + required this.type, + required this.code, + required this.key, + this.timeStamp = 0, + this.repeat = false, + this.altKey = false, + this.ctrlKey = false, + this.shiftKey = false, + this.metaKey = false, + this.location = 0, + this.onPreventDefault, + }); + + @override + String type; + + @override + String? code; + + @override + String? key; + + @override + bool? repeat; + + @override + num? timeStamp; + + @override + bool altKey; + + @override + bool ctrlKey; + + @override + bool shiftKey; + + @override + bool metaKey; + + @override + int? location; + + @override + bool getModifierState(String key) => modifierState.contains(key); + final Set modifierState = {}; + + @override + void preventDefault() { onPreventDefault?.call(); } + VoidCallback? onPreventDefault; +} + +// Flags used for the `modifiers` argument of `key***Event` functions. +const int kAlt = 0x1; +const int kCtrl = 0x2; +const int kShift = 0x4; +const int kMeta = 0x8; + +// Utility functions to make code more concise. +// +// To add timeStamp or onPreventDefault, use syntax like `..timeStamp = `. +MockKeyboardEvent keyDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keydown', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + location: location, + ); +} + +MockKeyboardEvent keyUpEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keyup', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + location: location, + ); +} + +MockKeyboardEvent keyRepeatedDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { + return MockKeyboardEvent( + type: 'keydown', + code: code, + key: key, + altKey: modifiers & kAlt != 0, + ctrlKey: modifiers & kCtrl != 0, + shiftKey: modifiers & kShift != 0, + metaKey: modifiers & kMeta != 0, + repeat: true, + location: location, + ); +} + +// Flags used for the `activeLocks` argument of expectKeyData. +const int kCapsLock = 0x1; +const int kNumlLock = 0x2; +const int kScrollLock = 0x4; + +void expectKeyData( + ui.KeyData target, { + required ui.KeyEventType type, + required int physical, + required int logical, + required String? character, + Duration? timeStamp, + bool synthesized = false, +}) { + expect(target.type, type); + expect(target.physical, physical); + expect(target.logical, logical); + expect(target.character, character); + expect(target.synthesized, synthesized); + if (timeStamp != null) + expect(target.timeStamp, equals(timeStamp)); +} + +typedef FakeAsyncTest = void Function(FakeAsync); + +@isTest +void testFakeAsync(String description, FakeAsyncTest fn) { + test(description, () { + FakeAsync().run(fn); + }); +} diff --git a/lib/web_ui/test/keyboard_test.dart b/lib/web_ui/test/keyboard_test.dart index c16fe89467b03..8787196d3ed52 100644 --- a/lib/web_ui/test/keyboard_test.dart +++ b/lib/web_ui/test/keyboard_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'dart:js_util' as js_util; import 'dart:typed_data'; @@ -10,7 +9,10 @@ import 'dart:typed_data'; import 'package:quiver/testing/async.dart'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/keyboard.dart'; +import 'package:ui/src/engine/services.dart'; +import 'package:ui/src/engine/text_editing/text_editing.dart'; import 'package:ui/ui.dart' as ui; void main() { @@ -20,7 +22,7 @@ void main() { void testMain() { group('Keyboard', () { /// Used to save and restore [ui.window.onPlatformMessage] after each test. - ui.PlatformMessageCallback savedCallback; + ui.PlatformMessageCallback? savedCallback; setUp(() { savedCallback = ui.window.onPlatformMessage; @@ -34,19 +36,19 @@ void testMain() { expect(Keyboard.instance, isNull); Keyboard.initialize(); expect(Keyboard.instance, isA()); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); expect(Keyboard.instance, isNull); }); test('dispatches keyup to flutter/keyevent channel', () { Keyboard.initialize(); - String channelReceived; - Map dataReceived; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { + String? channelReceived; + Map? dataReceived; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { channelReceived = channel; - dataReceived = const JSONMessageCodec().decodeMessage(data); + dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; html.KeyboardEvent event; @@ -59,24 +61,25 @@ void testMain() { 'type': 'keyup', 'keymap': 'web', 'code': 'SomeCode', + 'location': 0, 'key': 'SomeKey', 'metaState': 0x0, }); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50815 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50815 skip: browserEngine == BrowserEngine.edge); test('dispatches keydown to flutter/keyevent channel', () { Keyboard.initialize(); - String channelReceived; - Map dataReceived; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { + String? channelReceived; + Map? dataReceived; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { channelReceived = channel; - dataReceived = const JSONMessageCodec().decodeMessage(data); + dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; html.KeyboardEvent event; @@ -90,22 +93,23 @@ void testMain() { 'keymap': 'web', 'code': 'SomeCode', 'key': 'SomeKey', + 'location': 0, 'metaState': 0x0, }); expect(event.defaultPrevented, isFalse); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50815 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50815 skip: browserEngine == BrowserEngine.edge); test('dispatches correct meta state', () { Keyboard.initialize(); - Map dataReceived; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - dataReceived = const JSONMessageCodec().decodeMessage(data); + Map? dataReceived; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; html.KeyboardEvent event; @@ -122,6 +126,7 @@ void testMain() { 'keymap': 'web', 'code': 'SomeCode', 'key': 'SomeKey', + 'location': 0, // ctrl 'metaState': 0x4, }); @@ -140,22 +145,23 @@ void testMain() { 'keymap': 'web', 'code': 'SomeCode', 'key': 'SomeKey', + 'location': 0, // shift alt meta 'metaState': 0x1 | 0x2 | 0x8, }); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50815 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50815 skip: browserEngine == BrowserEngine.edge); test('dispatches repeat events', () { Keyboard.initialize(); - List> messages = >[]; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - messages.add(const JSONMessageCodec().decodeMessage(data)); + final List> messages = >[]; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; html.KeyboardEvent event; @@ -189,6 +195,7 @@ void testMain() { 'keymap': 'web', 'code': 'SomeCode', 'key': 'SomeKey', + 'location': 0, 'metaState': 0, }; expect(messages, >[ @@ -197,17 +204,17 @@ void testMain() { expectedMessage, ]); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50815 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50815 skip: browserEngine == BrowserEngine.edge); test('stops dispatching events after dispose', () { Keyboard.initialize(); int count = 0; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { count += 1; }; @@ -216,7 +223,7 @@ void testMain() { dispatchKeyboardEvent('keyup'); expect(count, 2); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); expect(Keyboard.instance, isNull); // No more event dispatching. @@ -226,13 +233,15 @@ void testMain() { expect(count, 2); }); - test('prevents default when "Tab" is pressed', () { + test('prevents default when key is handled by the framework', () { Keyboard.initialize(); int count = 0; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { count += 1; + final ByteData response = const JSONMessageCodec().encodeMessage({'handled': true})!; + callback!(response); }; final html.KeyboardEvent event = dispatchKeyboardEvent( @@ -244,15 +253,38 @@ void testMain() { expect(event.defaultPrevented, isTrue); expect(count, 1); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); + }); + + test("Doesn't prevent default when key is not handled by the framework", () { + Keyboard.initialize(); + + int count = 0; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + count += 1; + final ByteData response = const JSONMessageCodec().encodeMessage({'handled': false})!; + callback!(response); + }; + + final html.KeyboardEvent event = dispatchKeyboardEvent( + 'keydown', + key: 'Tab', + code: 'Tab', + ); + + expect(event.defaultPrevented, isFalse); + expect(count, 1); + + Keyboard.instance!.dispose(); }); test('keyboard events should be triggered on text fields', () { Keyboard.initialize(); int count = 0; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { count += 1; }; @@ -268,16 +300,18 @@ void testMain() { expect(count, 1); }); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }); test('the "Tab" key should never be ignored', () { Keyboard.initialize(); int count = 0; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { count += 1; + final ByteData response = const JSONMessageCodec().encodeMessage({'handled': true})!; + callback!(response); }; useTextEditingElement((html.Element element) { @@ -292,7 +326,7 @@ void testMain() { expect(count, 1); }); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }); testFakeAsync( @@ -307,22 +341,24 @@ void testMain() { // `keyup(i)` event. Keyboard.initialize(); - List> messages = >[]; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - messages.add(const JSONMessageCodec().decodeMessage(data)); + final List> messages = >[]; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; dispatchKeyboardEvent( 'keydown', key: 'Meta', code: 'MetaLeft', + location: 1, isMetaPressed: true, ); dispatchKeyboardEvent( 'keydown', key: 'Alt', code: 'AltLeft', + location: 1, isMetaPressed: true, isAltPressed: true, ); @@ -333,14 +369,20 @@ void testMain() { isMetaPressed: true, isAltPressed: true, ); - async.elapse(Duration(milliseconds: 10)); + async.elapse(const Duration(milliseconds: 10)); dispatchKeyboardEvent( 'keyup', key: 'Meta', code: 'MetaLeft', + location: 1, isAltPressed: true, ); - dispatchKeyboardEvent('keyup', key: 'Alt', code: 'AltLeft'); + dispatchKeyboardEvent( + 'keyup', + key: 'Alt', + code: 'AltLeft', + location: 1, + ); // Notice no `keyup` for "i". expect(messages, >[ @@ -349,6 +391,7 @@ void testMain() { 'keymap': 'web', 'key': 'Meta', 'code': 'MetaLeft', + 'location': 1, // meta 'metaState': 0x8, }, @@ -357,6 +400,7 @@ void testMain() { 'keymap': 'web', 'key': 'Alt', 'code': 'AltLeft', + 'location': 1, // alt meta 'metaState': 0x2 | 0x8, }, @@ -365,6 +409,7 @@ void testMain() { 'keymap': 'web', 'key': 'i', 'code': 'KeyI', + 'location': 0, // alt meta 'metaState': 0x2 | 0x8, }, @@ -373,6 +418,7 @@ void testMain() { 'keymap': 'web', 'key': 'Meta', 'code': 'MetaLeft', + 'location': 1, // alt 'metaState': 0x2, }, @@ -381,27 +427,29 @@ void testMain() { 'keymap': 'web', 'key': 'Alt', 'code': 'AltLeft', + 'location': 1, 'metaState': 0x0, }, ]); messages.clear(); // Still too eary to synthesize a keyup event. - async.elapse(Duration(milliseconds: 50)); + async.elapse(const Duration(milliseconds: 50)); expect(messages, isEmpty); - async.elapse(Duration(seconds: 3)); + async.elapse(const Duration(seconds: 3)); expect(messages, >[ { 'type': 'keyup', 'keymap': 'web', 'key': 'i', 'code': 'KeyI', + 'location': 0, 'metaState': 0x0, } ]); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, ); @@ -410,22 +458,24 @@ void testMain() { (FakeAsync async) { Keyboard.initialize(); - List> messages = >[]; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - messages.add(const JSONMessageCodec().decodeMessage(data)); + final List> messages = >[]; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; dispatchKeyboardEvent( 'keydown', key: 'Meta', code: 'MetaLeft', + location: 1, isMetaPressed: true, ); dispatchKeyboardEvent( 'keydown', key: 'Alt', code: 'AltLeft', + location: 1, isMetaPressed: true, isAltPressed: true, ); @@ -436,14 +486,20 @@ void testMain() { isMetaPressed: true, isAltPressed: true, ); - async.elapse(Duration(milliseconds: 10)); + async.elapse(const Duration(milliseconds: 10)); dispatchKeyboardEvent( 'keyup', key: 'Meta', code: 'MetaLeft', + location: 1, isAltPressed: true, ); - dispatchKeyboardEvent('keyup', key: 'Alt', code: 'AltLeft'); + dispatchKeyboardEvent( + 'keyup', + key: 'Alt', + code: 'AltLeft', + location: 1, + ); // Notice no `keyup` for "i". messages.clear(); @@ -451,7 +507,7 @@ void testMain() { // Spend more than 2 seconds sending repeat events and make sure no // keyup was synthesized. for (int i = 0; i < 20; i++) { - async.elapse(Duration(milliseconds: 100)); + async.elapse(const Duration(milliseconds: 100)); dispatchKeyboardEvent( 'keydown', key: 'i', @@ -468,12 +524,13 @@ void testMain() { 'keymap': 'web', 'key': 'i', 'code': 'KeyI', + 'location': 0, 'metaState': 0x0, }); } messages.clear(); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, ); @@ -482,10 +539,10 @@ void testMain() { (FakeAsync async) { Keyboard.initialize(); - List> messages = >[]; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - messages.add(const JSONMessageCodec().decodeMessage(data)); + final List> messages = >[]; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; dispatchKeyboardEvent( @@ -502,32 +559,34 @@ void testMain() { // Wait for a long-enough period of time and no events // should be synthesized - async.elapse(Duration(seconds: 3)); + async.elapse(const Duration(seconds: 3)); expect(messages, hasLength(0)); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }, ); testFakeAsync('do not synthesize keyup for meta keys', (FakeAsync async) { Keyboard.initialize(); - List> messages = >[]; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - messages.add(const JSONMessageCodec().decodeMessage(data)); + final List> messages = >[]; + ui.window.onPlatformMessage = (String channel, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; dispatchKeyboardEvent( 'keydown', key: 'Meta', code: 'MetaLeft', + location: 1, isMetaPressed: true, ); dispatchKeyboardEvent( 'keydown', key: 'Alt', code: 'AltLeft', + location: 1, isMetaPressed: true, isAltPressed: true, ); @@ -538,11 +597,12 @@ void testMain() { isMetaPressed: true, isAltPressed: true, ); - async.elapse(Duration(milliseconds: 10)); + async.elapse(const Duration(milliseconds: 10)); dispatchKeyboardEvent( 'keyup', key: 'Meta', code: 'MetaLeft', + location: 1, isAltPressed: true, ); // Notice no `keyup` for "AltLeft" and "i". @@ -551,19 +611,20 @@ void testMain() { // There has been no repeat events for "AltLeft" nor "i". Only "i" should // synthesize a keyup event. - async.elapse(Duration(seconds: 3)); + async.elapse(const Duration(seconds: 3)); expect(messages, >[ { 'type': 'keyup', 'keymap': 'web', 'key': 'i', 'code': 'KeyI', + 'location': 0, // alt 'metaState': 0x2, } ]); - Keyboard.instance.dispose(); + Keyboard.instance!.dispose(); }); }); } @@ -575,7 +636,7 @@ void useTextEditingElement(ElementCallback callback) { input.classes.add(HybridTextEditing.textEditingClass); try { - html.document.body.append(input); + html.document.body!.append(input); callback(input); } finally { input.remove(); @@ -584,9 +645,10 @@ void useTextEditingElement(ElementCallback callback) { html.KeyboardEvent dispatchKeyboardEvent( String type, { - html.EventTarget target, - String key, - String code, + html.EventTarget? target, + String? key, + String? code, + int location = 0, bool repeat = false, bool isShiftPressed = false, bool isAltPressed = false, @@ -596,12 +658,13 @@ html.KeyboardEvent dispatchKeyboardEvent( target ??= html.window; final Function jsKeyboardEvent = - js_util.getProperty(html.window, 'KeyboardEvent'); + js_util.getProperty(html.window, 'KeyboardEvent') as Function; final List eventArgs = [ type, { 'key': key, 'code': code, + 'location': location, 'repeat': repeat, 'shiftKey': isShiftPressed, 'altKey': isAltPressed, @@ -611,8 +674,10 @@ html.KeyboardEvent dispatchKeyboardEvent( 'cancelable': true, } ]; - final html.KeyboardEvent event = - js_util.callConstructor(jsKeyboardEvent, js_util.jsify(eventArgs)); + final html.KeyboardEvent event = js_util.callConstructor( + jsKeyboardEvent, + js_util.jsify(eventArgs) as List, + ) as html.KeyboardEvent; target.dispatchEvent(event); return event; diff --git a/lib/web_ui/test/lerp_test.dart b/lib/web_ui/test/lerp_test.dart index 433ffbd066c01..f7f13c2b4fdd1 100644 --- a/lib/web_ui/test/lerp_test.dart +++ b/lib/web_ui/test/lerp_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -154,9 +153,9 @@ void expectAssertion(Function callback) { try { callback(); } catch (e) { - expect(e is AssertionError, true); + expect(e is AssertionError, isTrue); threw = true; } - expect(threw, true); + expect(threw, isTrue); } } diff --git a/lib/web_ui/test/locale_test.dart b/lib/web_ui/test/locale_test.dart index 316a42ae1101f..6e80b0fbbe94b 100644 --- a/lib/web_ui/test/locale_test.dart +++ b/lib/web_ui/test/locale_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; @@ -13,7 +12,7 @@ void main() { void testMain() { test('Locale', () { - const Null $null = null; + const String? $null = null; expect(const Locale('en').toString(), 'en'); expect(const Locale('en'), const Locale('en', $null)); expect(const Locale('en').hashCode, const Locale('en', $null).hashCode); diff --git a/lib/web_ui/test/matchers.dart b/lib/web_ui/test/matchers.dart index 586d8a01de319..d21407222f469 100644 --- a/lib/web_ui/test/matchers.dart +++ b/lib/web_ui/test/matchers.dart @@ -3,29 +3,27 @@ // found in the LICENSE file. /// Provides utilities for testing engine code. -// @dart = 2.6 library matchers; import 'dart:html' as html; import 'dart:math' as math; -import 'package:html/parser.dart' as html_package; import 'package:html/dom.dart' as html_package; +import 'package:html/parser.dart' as html_package; -import 'package:meta/meta.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; /// Enumerates all persisted surfaces in the tree rooted at [root]. /// /// If [root] is `null` returns all surfaces from the last rendered scene. /// /// Surfaces are returned in a depth-first order. -Iterable enumerateSurfaces([PersistedSurface root]) { +Iterable enumerateSurfaces([PersistedSurface? root]) { root ??= SurfaceSceneBuilder.debugLastFrameScene; - final List surfaces = [root]; + final List surfaces = [root!]; root.visitChildren((PersistedSurface surface) { surfaces.addAll(enumerateSurfaces(surface)); @@ -37,7 +35,7 @@ Iterable enumerateSurfaces([PersistedSurface root]) { /// Enumerates all pictures nested under [root]. /// /// If [root] is `null` returns all pictures from the last rendered scene. -Iterable enumeratePictures([PersistedSurface root]) { +Iterable enumeratePictures([PersistedSurface? root]) { root ??= SurfaceSceneBuilder.debugLastFrameScene; return enumerateSurfaces(root).whereType(); } @@ -45,7 +43,7 @@ Iterable enumeratePictures([PersistedSurface root]) { /// Enumerates all offset surfaces nested under [root]. /// /// If [root] is `null` returns all pictures from the last rendered scene. -Iterable enumerateOffsets([PersistedSurface root]) { +Iterable enumerateOffsets([PersistedSurface? root]) { root ??= SurfaceSceneBuilder.debugLastFrameScene; return enumerateSurfaces(root).whereType(); } @@ -76,7 +74,7 @@ typedef DistanceFunction = num Function(T a, T b); /// /// Calling an instance of this type must either be done dynamically, or by /// first casting it to a [DistanceFunction] for some concrete T. -typedef AnyDistanceFunction = num Function(Null a, Null b); +typedef AnyDistanceFunction = num Function(Never a, Never b); const Map _kStandardDistanceFunctions = { @@ -108,7 +106,7 @@ double _rectDistance(Rect a, Rect b) { } double _sizeDistance(Size a, Size b) { - final Offset delta = b - a; + final Offset delta = (b - a) as Offset; // ignore: unnecessary_parenthesis return delta.distance; } @@ -134,16 +132,16 @@ double _sizeDistance(Size a, Size b) { /// [double]s and has an optional `epsilon` parameter. /// * [closeTo], which specializes in numbers only. Matcher within({ - @required num distance, - @required T from, - DistanceFunction distanceFunction, + required num distance, + required T from, + DistanceFunction? distanceFunction, }) { - distanceFunction ??= _kStandardDistanceFunctions[T]; + distanceFunction ??= _kStandardDistanceFunctions[T] as DistanceFunction?; if (distanceFunction == null) { throw ArgumentError( 'The specified distanceFunction was null, and a standard distance ' - 'function was not found for type ${T} of the provided ' + 'function was not found for type $T of the provided ' '`from` argument.'); } @@ -158,7 +156,7 @@ class _IsWithinDistance extends Matcher { final num epsilon; @override - bool matches(Object object, Map matchState) { + bool matches(Object? object, Map matchState) { if (object is! T) { return false; } @@ -183,7 +181,7 @@ class _IsWithinDistance extends Matcher { @override Description describeMismatch( - Object object, + Object? object, Description mismatchDescription, Map matchState, bool verbose, @@ -230,11 +228,11 @@ enum HtmlComparisonMode { String canonicalizeHtml(String htmlContent, {HtmlComparisonMode mode = HtmlComparisonMode.nonLayoutOnly, bool throwOnUnusedAttributes = false}) { - if (htmlContent == null || htmlContent.trim().isEmpty) { + if (htmlContent.trim().isEmpty) { return ''; } - String _unusedAttribute(String name) { + String? _unusedAttribute(String name) { if (throwOnUnusedAttributes) { fail('Provided HTML contains style attribute "$name" which ' 'is not used for comparison in the test. The HTML was:\n\n$htmlContent'); @@ -244,7 +242,7 @@ String canonicalizeHtml(String htmlContent, } html_package.Element _cleanup(html_package.Element original) { - String replacementTag = original.localName; + String replacementTag = original.localName!; switch (replacementTag) { case 'flt-scene': replacementTag = 's'; @@ -256,7 +254,7 @@ String canonicalizeHtml(String htmlContent, replacementTag = 'o'; break; case 'flt-clip': - final String clipType = original.attributes['clip-type']; + final String? clipType = original.attributes['clip-type']; switch (clipType) { case 'rect': replacementTag = 'clip'; @@ -305,6 +303,9 @@ String canonicalizeHtml(String htmlContent, if (mode != HtmlComparisonMode.noAttributes) { original.attributes.forEach((dynamic name, String value) { + if (name is! String) { + throw '"$name" should be String but was ${name.runtimeType}.'; + } if (name == 'style') { return; } @@ -314,7 +315,7 @@ String canonicalizeHtml(String htmlContent, }); if (original.attributes.containsKey('style')) { - final String styleValue = original.attributes['style']; + final String styleValue = original.attributes['style']!; int attrCount = 0; final String processedAttributes = styleValue @@ -368,7 +369,7 @@ String canonicalizeHtml(String htmlContent, attrCount++; return attr.trim(); }) - .where((String attr) => attr != null && attr.isNotEmpty) + .where((String? attr) => attr != null && attr.isNotEmpty) .join('; '); if (attrCount > 0) { @@ -380,7 +381,7 @@ String canonicalizeHtml(String htmlContent, 'is $mode. The HTML was:\n\n$htmlContent'); } - for (html_package.Node child in original.nodes) { + for (final html_package.Node child in original.nodes) { if (child is html_package.Text && child.text.trim().isEmpty) { continue; } @@ -400,7 +401,7 @@ String canonicalizeHtml(String htmlContent, final html_package.DocumentFragment cleanDom = html_package.DocumentFragment(); - for (html_package.Element child in originalDom.children) { + for (final html_package.Element child in originalDom.children) { cleanDom.append(_cleanup(child)); } @@ -412,7 +413,7 @@ void expectHtml(html.Element element, String expectedHtml, {HtmlComparisonMode mode = HtmlComparisonMode.nonLayoutOnly}) { expectedHtml = canonicalizeHtml(expectedHtml, mode: mode, throwOnUnusedAttributes: true); - final String actualHtml = canonicalizeHtml(element.outerHtml, mode: mode); + final String actualHtml = canonicalizeHtml(element.outerHtml!, mode: mode); expect(actualHtml, expectedHtml); } @@ -462,7 +463,7 @@ class SceneTester { final SurfaceScene scene; void expectSceneHtml(String expectedHtml) { - expectHtml(scene.webOnlyRootElement, expectedHtml, + expectHtml(scene.webOnlyRootElement!, expectedHtml, mode: HtmlComparisonMode.noAttributes); } } diff --git a/lib/web_ui/test/mock_engine_canvas.dart b/lib/web_ui/test/mock_engine_canvas.dart index a88dccc0b12ca..69569b085b921 100644 --- a/lib/web_ui/test/mock_engine_canvas.dart +++ b/lib/web_ui/test/mock_engine_canvas.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'dart:typed_data'; @@ -13,7 +12,7 @@ import 'package:ui/ui.dart'; /// that were passed. class MockCanvasCall { MockCanvasCall._({ - this.methodName, + required this.methodName, this.arguments, }); @@ -35,7 +34,8 @@ class MockEngineCanvas implements EngineCanvas { final List methodCallLog = []; @override - html.Element get rootElement => null; + html.Element get rootElement => _rootElement; + html.Element _rootElement = html.DivElement(); void _called(String methodName, {dynamic arguments}) { methodCallLog.add(MockCanvasCall._( @@ -99,7 +99,7 @@ class MockEngineCanvas implements EngineCanvas { } @override - void clipRect(Rect rect) { + void clipRect(Rect rect, ClipOp op) { _called('clipRect', arguments: rect); } diff --git a/lib/web_ui/test/paragraph_builder_test.dart b/lib/web_ui/test/paragraph_builder_test.dart index fe3041bc1539f..9671a9d70648e 100644 --- a/lib/web_ui/test/paragraph_builder_test.dart +++ b/lib/web_ui/test/paragraph_builder_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; @@ -28,10 +27,20 @@ void testMain() { expect(paragraph.height, isNonZero); }); - test('PushStyle should not segfault after build()', () { + test('pushStyle should not segfault after build()', () { final ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle()); paragraphBuilder.build(); paragraphBuilder.pushStyle(TextStyle()); }); + + test('the presence of foreground style should not throw', () { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); + builder.pushStyle(TextStyle( + foreground: Paint()..color = const Color(0xFFABCDEF), + )); + builder.addText('hi'); + + expect(() => builder.build(), returnsNormally); + }); } diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart deleted file mode 100644 index 81396d81b9fbe..0000000000000 --- a/lib/web_ui/test/paragraph_test.dart +++ /dev/null @@ -1,990 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; -import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart' hide window; - - -void testEachMeasurement(String description, VoidCallback body, {bool skip}) { - test('$description (dom measurement)', () async { - try { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - WebExperiments.instance.useCanvasText = false; - return body(); - } finally { - WebExperiments.instance.useCanvasText = null; - TextMeasurementService.clearCache(); - } - }, skip: skip); - test('$description (canvas measurement)', () async { - try { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - WebExperiments.instance.useCanvasText = true; - return body(); - } finally { - WebExperiments.instance.useCanvasText = null; - TextMeasurementService.clearCache(); - } - }, skip: skip); -} - -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - await webOnlyInitializeTestDomRenderer(); - - // Ahem font uses a constant ideographic/alphabetic baseline ratio. - const double kAhemBaselineRatio = 1.25; - - testEachMeasurement('predictably lays out a single-line paragraph', () { - for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); - builder.addText('Test'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 400.0)); - - expect(paragraph.height, closeTo(fontSize, 0.001)); - expect(paragraph.width, closeTo(400.0, 0.001)); - expect(paragraph.minIntrinsicWidth, closeTo(fontSize * 4.0, 0.001)); - expect(paragraph.maxIntrinsicWidth, closeTo(fontSize * 4.0, 0.001)); - expect(paragraph.alphabeticBaseline, closeTo(fontSize * .8, 0.001)); - expect( - paragraph.ideographicBaseline, - closeTo(paragraph.alphabeticBaseline * kAhemBaselineRatio, 3.0), - ); - } - }); - - testEachMeasurement('predictably lays out a multi-line paragraph', () { - for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); - builder.addText('Test Ahem'); - final Paragraph paragraph = builder.build(); - paragraph.layout(ParagraphConstraints(width: fontSize * 5.0)); - - expect( - paragraph.height, closeTo(fontSize * 2.0, 0.001)); // because it wraps - expect(paragraph.width, closeTo(fontSize * 5.0, 0.001)); - expect(paragraph.minIntrinsicWidth, closeTo(fontSize * 4.0, 0.001)); - - // TODO(yjbanov): see https://github.com/flutter/flutter/issues/21965 - expect(paragraph.maxIntrinsicWidth, closeTo(fontSize * 9.0, 0.001)); - expect(paragraph.alphabeticBaseline, closeTo(fontSize * .8, 0.001)); - expect( - paragraph.ideographicBaseline, - closeTo(paragraph.alphabeticBaseline * kAhemBaselineRatio, 3.0), - ); - } - }); - - testEachMeasurement('predictably lays out a single-line rich paragraph', () { - for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); - builder.addText('span1'); - builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); - builder.addText('span2'); - final Paragraph paragraph = builder.build(); - paragraph.layout(ParagraphConstraints(width: fontSize * 10.0)); - - expect(paragraph.height, fontSize); - expect(paragraph.width, fontSize * 10.0); - expect(paragraph.minIntrinsicWidth, fontSize * 10.0); - expect(paragraph.maxIntrinsicWidth, fontSize * 10.0); - } - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50771 - // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - skip: (browserEngine != BrowserEngine.blink)); - - testEachMeasurement('predictably lays out a multi-line rich paragraph', () { - for (double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); - builder.addText('12345 '); - builder.addText('67890 '); - builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); - builder.addText('bold'); - final Paragraph paragraph = builder.build(); - paragraph.layout(ParagraphConstraints(width: fontSize * 5.0)); - - expect(paragraph.height, fontSize * 3.0); // because it wraps - expect(paragraph.width, fontSize * 5.0); - expect(paragraph.minIntrinsicWidth, fontSize * 5.0); - expect(paragraph.maxIntrinsicWidth, fontSize * 16.0); - } - }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/46638 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50590 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50771 - skip: (browserEngine != BrowserEngine.blink)); - - testEachMeasurement('getPositionForOffset single-line', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - )); - builder.addText('abcd efg'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - - // At the beginning of the line. - expect( - paragraph.getPositionForOffset(Offset(0, 5)), - TextPosition(offset: 0, affinity: TextAffinity.downstream), - ); - // Below the line. - expect( - paragraph.getPositionForOffset(Offset(0, 12)), - TextPosition(offset: 8, affinity: TextAffinity.upstream), - ); - // Above the line. - expect( - paragraph.getPositionForOffset(Offset(0, -5)), - TextPosition(offset: 0, affinity: TextAffinity.downstream), - ); - // At the end of the line. - expect( - paragraph.getPositionForOffset(Offset(80, 5)), - TextPosition(offset: 8, affinity: TextAffinity.upstream), - ); - // On the left side of "b". - expect( - paragraph.getPositionForOffset(Offset(14, 5)), - TextPosition(offset: 1, affinity: TextAffinity.downstream), - ); - // On the right side of "b". - expect( - paragraph.getPositionForOffset(Offset(16, 5)), - TextPosition(offset: 2, affinity: TextAffinity.upstream), - ); - }); - - test('getPositionForOffset multi-line', () { - // [Paragraph.getPositionForOffset] for multi-line text doesn't work well - // with dom-based measurement. - WebExperiments.instance.useCanvasText = true; - TextMeasurementService.initialize(rulerCacheCapacity: 2); - - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - )); - builder.addText('abcd\n'); - builder.addText('abcdefg\n'); - builder.addText('ab'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 100)); - - // First line: "abcd\n" - - // At the beginning of the first line. - expect( - paragraph.getPositionForOffset(Offset(0, 5)), - TextPosition(offset: 0, affinity: TextAffinity.downstream), - ); - // Above the first line. - expect( - paragraph.getPositionForOffset(Offset(0, -15)), - TextPosition(offset: 0, affinity: TextAffinity.downstream), - ); - // At the end of the first line. - expect( - paragraph.getPositionForOffset(Offset(50, 5)), - TextPosition(offset: 4, affinity: TextAffinity.upstream), - ); - // On the left side of "b" in the first line. - expect( - paragraph.getPositionForOffset(Offset(14, 5)), - TextPosition(offset: 1, affinity: TextAffinity.downstream), - ); - // On the right side of "b" in the first line. - expect( - paragraph.getPositionForOffset(Offset(16, 5)), - TextPosition(offset: 2, affinity: TextAffinity.upstream), - ); - - // Second line: "abcdefg\n" - - // At the beginning of the second line. - expect( - paragraph.getPositionForOffset(Offset(0, 15)), - TextPosition(offset: 5, affinity: TextAffinity.downstream), - ); - // At the end of the second line. - expect( - paragraph.getPositionForOffset(Offset(100, 15)), - TextPosition(offset: 12, affinity: TextAffinity.upstream), - ); - // On the left side of "e" in the second line. - expect( - paragraph.getPositionForOffset(Offset(44, 15)), - TextPosition(offset: 9, affinity: TextAffinity.downstream), - ); - // On the right side of "e" in the second line. - expect( - paragraph.getPositionForOffset(Offset(46, 15)), - TextPosition(offset: 10, affinity: TextAffinity.upstream), - ); - - // Last (third) line: "ab" - - // At the beginning of the last line. - expect( - paragraph.getPositionForOffset(Offset(0, 25)), - TextPosition(offset: 13, affinity: TextAffinity.downstream), - ); - // At the end of the last line. - expect( - paragraph.getPositionForOffset(Offset(100, 25)), - TextPosition(offset: 15, affinity: TextAffinity.upstream), - ); - // Below the last line. - expect( - paragraph.getPositionForOffset(Offset(0, 32)), - TextPosition(offset: 15, affinity: TextAffinity.upstream), - ); - // On the left side of "b" in the last line. - expect( - paragraph.getPositionForOffset(Offset(12, 25)), - TextPosition(offset: 14, affinity: TextAffinity.downstream), - ); - // On the right side of "a" in the last line. - expect( - paragraph.getPositionForOffset(Offset(9, 25)), - TextPosition(offset: 14, affinity: TextAffinity.upstream), - ); - - TextMeasurementService.clearCache(); - WebExperiments.instance.useCanvasText = null; - }); - - test('getPositionForOffset multi-line centered', () { - WebExperiments.instance.useCanvasText = true; - TextMeasurementService.initialize(rulerCacheCapacity: 2); - - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - textAlign: TextAlign.center, - )); - builder.addText('abcd\n'); - builder.addText('abcdefg\n'); - builder.addText('ab'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 100)); - - // First line: "abcd\n" - - // At the beginning of the first line. - expect( - paragraph.getPositionForOffset(Offset(0, 5)), - TextPosition(offset: 0, affinity: TextAffinity.downstream), - ); - // Above the first line. - expect( - paragraph.getPositionForOffset(Offset(0, -15)), - TextPosition(offset: 0, affinity: TextAffinity.downstream), - ); - // At the end of the first line. - expect( - paragraph.getPositionForOffset(Offset(100, 5)), - TextPosition(offset: 4, affinity: TextAffinity.upstream), - ); - // On the left side of "b" in the first line. - expect( - // The line is centered so it's shifted to the right by "30.0px". - paragraph.getPositionForOffset(Offset(30.0 + 14, 5)), - TextPosition(offset: 1, affinity: TextAffinity.downstream), - ); - // On the right side of "b" in the first line. - expect( - // The line is centered so it's shifted to the right by "30.0px". - paragraph.getPositionForOffset(Offset(30.0 + 16, 5)), - TextPosition(offset: 2, affinity: TextAffinity.upstream), - ); - - // Second line: "abcdefg\n" - - // At the beginning of the second line. - expect( - paragraph.getPositionForOffset(Offset(0, 15)), - TextPosition(offset: 5, affinity: TextAffinity.downstream), - ); - // At the end of the second line. - expect( - paragraph.getPositionForOffset(Offset(100, 15)), - TextPosition(offset: 12, affinity: TextAffinity.upstream), - ); - // On the left side of "e" in the second line. - expect( - // The line is centered so it's shifted to the right by "15.0px". - paragraph.getPositionForOffset(Offset(15.0 + 44, 15)), - TextPosition(offset: 9, affinity: TextAffinity.downstream), - ); - // On the right side of "e" in the second line. - expect( - // The line is centered so it's shifted to the right by "15.0px". - paragraph.getPositionForOffset(Offset(15.0 + 46, 15)), - TextPosition(offset: 10, affinity: TextAffinity.upstream), - ); - - // Last (third) line: "ab" - - // At the beginning of the last line. - expect( - paragraph.getPositionForOffset(Offset(0, 25)), - TextPosition(offset: 13, affinity: TextAffinity.downstream), - ); - // At the end of the last line. - expect( - paragraph.getPositionForOffset(Offset(100, 25)), - TextPosition(offset: 15, affinity: TextAffinity.upstream), - ); - // Below the last line. - expect( - paragraph.getPositionForOffset(Offset(0, 32)), - TextPosition(offset: 15, affinity: TextAffinity.upstream), - ); - // On the left side of "b" in the last line. - expect( - // The line is centered so it's shifted to the right by "40.0px". - paragraph.getPositionForOffset(Offset(40.0 + 12, 25)), - TextPosition(offset: 14, affinity: TextAffinity.downstream), - ); - // On the right side of "a" in the last line. - expect( - // The line is centered so it's shifted to the right by "40.0px". - paragraph.getPositionForOffset(Offset(40.0 + 9, 25)), - TextPosition(offset: 14, affinity: TextAffinity.upstream), - ); - - TextMeasurementService.clearCache(); - WebExperiments.instance.useCanvasText = null; - }); - - test('getWordBoundary', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()) - ..addText('Lorem ipsum dolor'); - final Paragraph paragraph = builder.build(); - - const TextRange loremRange = TextRange(start: 0, end: 5); - expect(paragraph.getWordBoundary(TextPosition(offset: 0)), loremRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 1)), loremRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 2)), loremRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 3)), loremRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 4)), loremRange); - - const TextRange firstSpace = TextRange(start: 5, end: 6); - expect(paragraph.getWordBoundary(TextPosition(offset: 5)), firstSpace); - - const TextRange ipsumRange = TextRange(start: 6, end: 11); - expect(paragraph.getWordBoundary(TextPosition(offset: 6)), ipsumRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 7)), ipsumRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 8)), ipsumRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 9)), ipsumRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 10)), ipsumRange); - - const TextRange secondSpace = TextRange(start: 11, end: 12); - expect(paragraph.getWordBoundary(TextPosition(offset: 11)), secondSpace); - - const TextRange dolorRange = TextRange(start: 12, end: 17); - expect(paragraph.getWordBoundary(TextPosition(offset: 12)), dolorRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 13)), dolorRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 14)), dolorRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 15)), dolorRange); - expect(paragraph.getWordBoundary(TextPosition(offset: 16)), dolorRange); - - const TextRange endRange = TextRange(start: 17, end: 17); - expect(paragraph.getWordBoundary(TextPosition(offset: 17)), endRange); - }); - - testEachMeasurement('getBoxesForRange returns a box', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.rtl, - )); - builder.addText('abcd'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - expect( - paragraph.getBoxesForRange(1, 2).single, - const TextBox.fromLTRBD( - 970, - 0, - 980, - 10, - TextDirection.rtl, - ), - ); - }); - - testEachMeasurement('getBoxesForRange returns a box for rich text', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - )); - builder.addText('abcd'); - builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); - builder.addText('xyz'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - expect( - paragraph.getBoxesForRange(1, 2).single, - const TextBox.fromLTRBD(0, 0, 0, 10, TextDirection.ltr), - ); - }); - - testEachMeasurement( - 'getBoxesForRange return empty list for zero-length range', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - )); - builder.addText('abcd'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - expect(paragraph.getBoxesForRange(0, 0), isEmpty); - }); - - testEachMeasurement('getBoxesForRange multi-line', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - )); - builder.addText('abcd\n'); - builder.addText('abcdefg\n'); - builder.addText('ab'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 100)); - - // First line: "abcd\n" - - // At the beginning of the first line. - expect( - paragraph.getBoxesForRange(0, 0), - [], - ); - // At the end of the first line. - expect( - paragraph.getBoxesForRange(4, 4), - [], - ); - // Between "b" and "c" in the first line. - expect( - paragraph.getBoxesForRange(2, 2), - [], - ); - // The range "ab" in the first line. - expect( - paragraph.getBoxesForRange(0, 2), - [ - TextBox.fromLTRBD(0.0, 0.0, 20.0, 10.0, TextDirection.ltr), - ], - ); - // The range "bc" in the first line. - expect( - paragraph.getBoxesForRange(1, 3), - [ - TextBox.fromLTRBD(10.0, 0.0, 30.0, 10.0, TextDirection.ltr), - ], - ); - // The range "d" in the first line. - expect( - paragraph.getBoxesForRange(3, 4), - [ - TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr), - ], - ); - // The range "\n" in the first line. - expect( - paragraph.getBoxesForRange(4, 5), - [ - TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr), - ], - ); - // The range "cd\n" in the first line. - expect( - paragraph.getBoxesForRange(2, 5), - [ - TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr), - ], - ); - - // Second line: "abcdefg\n" - - // At the beginning of the second line. - expect( - paragraph.getBoxesForRange(5, 5), - [], - ); - // At the end of the second line. - expect( - paragraph.getBoxesForRange(12, 12), - [], - ); - // The range "efg" in the second line. - expect( - paragraph.getBoxesForRange(9, 12), - [ - TextBox.fromLTRBD(40.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - // The range "bcde" in the second line. - expect( - paragraph.getBoxesForRange(6, 10), - [ - TextBox.fromLTRBD(10.0, 10.0, 50.0, 20.0, TextDirection.ltr), - ], - ); - // The range "fg\n" in the second line. - expect( - paragraph.getBoxesForRange(10, 13), - [ - TextBox.fromLTRBD(50.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - - // Last (third) line: "ab" - - // At the beginning of the last line. - expect( - paragraph.getBoxesForRange(13, 13), - [], - ); - // At the end of the last line. - expect( - paragraph.getBoxesForRange(15, 15), - [], - ); - // The range "a" in the last line. - expect( - paragraph.getBoxesForRange(14, 15), - [ - TextBox.fromLTRBD(10.0, 20.0, 20.0, 30.0, TextDirection.ltr), - ], - ); - // The range "ab" in the last line. - expect( - paragraph.getBoxesForRange(13, 15), - [ - TextBox.fromLTRBD(0.0, 20.0, 20.0, 30.0, TextDirection.ltr), - ], - ); - - - // Combine multiple lines - - // The range "cd\nabc". - expect( - paragraph.getBoxesForRange(2, 8), - [ - TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 30.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "\nabcd". - expect( - paragraph.getBoxesForRange(4, 9), - [ - TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 40.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "d\nabcdefg\na". - expect( - paragraph.getBoxesForRange(3, 14), - [ - TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 20.0, 10.0, 30.0, TextDirection.ltr), - ], - ); - - // The range "abcd\nabcdefg\n". - expect( - paragraph.getBoxesForRange(0, 13), - [ - TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "abcd\nabcdefg\nab". - expect( - paragraph.getBoxesForRange(0, 15), - [ - TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 20.0, 20.0, 30.0, TextDirection.ltr), - ], - ); - }); - - testEachMeasurement('getBoxesForRange with maxLines', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - maxLines: 2, - )); - builder.addText('abcd\n'); - builder.addText('abcdefg\n'); - builder.addText('ab'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 100)); - - // First line: "abcd\n" - - // At the beginning of the first line. - expect( - paragraph.getBoxesForRange(0, 0), - [], - ); - // At the end of the first line. - expect( - paragraph.getBoxesForRange(4, 4), - [], - ); - // Between "b" and "c" in the first line. - expect( - paragraph.getBoxesForRange(2, 2), - [], - ); - // The range "ab" in the first line. - expect( - paragraph.getBoxesForRange(0, 2), - [ - TextBox.fromLTRBD(0.0, 0.0, 20.0, 10.0, TextDirection.ltr), - ], - ); - // The range "bc" in the first line. - expect( - paragraph.getBoxesForRange(1, 3), - [ - TextBox.fromLTRBD(10.0, 0.0, 30.0, 10.0, TextDirection.ltr), - ], - ); - // The range "d" in the first line. - expect( - paragraph.getBoxesForRange(3, 4), - [ - TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr), - ], - ); - // The range "\n" in the first line. - expect( - paragraph.getBoxesForRange(4, 5), - [ - TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr), - ], - ); - // The range "cd\n" in the first line. - expect( - paragraph.getBoxesForRange(2, 5), - [ - TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr), - ], - ); - - // Second line: "abcdefg\n" - - // At the beginning of the second line. - expect( - paragraph.getBoxesForRange(5, 5), - [], - ); - // At the end of the second line. - expect( - paragraph.getBoxesForRange(12, 12), - [], - ); - // The range "efg" in the second line. - expect( - paragraph.getBoxesForRange(9, 12), - [ - TextBox.fromLTRBD(40.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - // The range "bcde" in the second line. - expect( - paragraph.getBoxesForRange(6, 10), - [ - TextBox.fromLTRBD(10.0, 10.0, 50.0, 20.0, TextDirection.ltr), - ], - ); - // The range "fg\n" in the second line. - expect( - paragraph.getBoxesForRange(10, 13), - [ - TextBox.fromLTRBD(50.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - - // Last (third) line: "ab" - - // At the beginning of the last line. - expect( - paragraph.getBoxesForRange(13, 13), - [], - ); - // At the end of the last line. - expect( - paragraph.getBoxesForRange(15, 15), - [], - ); - // The range "a" in the last line. - expect( - paragraph.getBoxesForRange(14, 15), - [], - ); - // The range "ab" in the last line. - expect( - paragraph.getBoxesForRange(13, 15), - [], - ); - - - // Combine multiple lines - - // The range "cd\nabc". - expect( - paragraph.getBoxesForRange(2, 8), - [ - TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 30.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "\nabcd". - expect( - paragraph.getBoxesForRange(4, 9), - [ - TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 40.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "d\nabcdefg\na". - expect( - paragraph.getBoxesForRange(3, 14), - [ - TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "abcd\nabcdefg\n". - expect( - paragraph.getBoxesForRange(0, 13), - [ - TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - - // The range "abcd\nabcdefg\nab". - expect( - paragraph.getBoxesForRange(0, 15), - [ - TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - ], - ); - }); - - testEachMeasurement('getBoxesForRange includes trailing spaces', () { - const String text = 'abcd abcde '; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - )); - builder.addText(text); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: double.infinity)); - expect( - paragraph.getBoxesForRange(0, text.length), - [ - TextBox.fromLTRBD(0.0, 0.0, 120.0, 10.0, TextDirection.ltr), - ], - ); - }); - - testEachMeasurement('getBoxesForRange multi-line includes trailing spaces', () { - const String text = 'abcd\nabcde \nabc'; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - )); - builder.addText(text); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: double.infinity)); - expect( - paragraph.getBoxesForRange(0, text.length), - [ - TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr), - TextBox.fromLTRBD(0.0, 20.0, 30.0, 30.0, TextDirection.ltr), - ], - ); - }); - - test('longestLine', () { - // [Paragraph.longestLine] is only supported by canvas-based measurement. - WebExperiments.instance.useCanvasText = true; - TextMeasurementService.initialize(rulerCacheCapacity: 2); - - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - )); - builder.addText('abcd\nabcde abc'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 80.0)); - expect(paragraph.longestLine, 50.0); - - TextMeasurementService.clearCache(); - WebExperiments.instance.useCanvasText = null; - }); - - testEachMeasurement('getLineBoundary (single-line)', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - )); - builder.addText('One single line'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 400.0)); - - // "One single line".length == 15 - for (int i = 0; i < 15; i++) { - expect( - paragraph.getLineBoundary(TextPosition(offset: i)), - TextRange(start: 0, end: 15), - reason: 'failed at offset $i', - ); - } - }); - - test('getLineBoundary (multi-line)', () { - // [Paragraph.getLineBoundary] for multi-line paragraphs is only supported - // by canvas-based measurement. - WebExperiments.instance.useCanvasText = true; - TextMeasurementService.initialize(rulerCacheCapacity: 2); - - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - )); - builder.addText('First line\n'); - builder.addText('Second line\n'); - builder.addText('Third line'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 400.0)); - - // "First line\n".length == 11 - for (int i = 0; i < 11; i++) { - expect( - paragraph.getLineBoundary(TextPosition(offset: i)), - TextRange(start: 0, end: 11), - reason: 'failed at offset $i', - ); - } - - // "Second line\n".length == 12 - for (int i = 11; i < 23; i++) { - expect( - paragraph.getLineBoundary(TextPosition(offset: i)), - TextRange(start: 11, end: 23), - reason: 'failed at offset $i', - ); - } - - // "Third line".length == 10 - for (int i = 23; i < 33; i++) { - expect( - paragraph.getLineBoundary(TextPosition(offset: i)), - TextRange(start: 23, end: 33), - reason: 'failed at offset $i', - ); - } - - TextMeasurementService.clearCache(); - WebExperiments.instance.useCanvasText = null; - }); - - testEachMeasurement('width should be a whole integer', () { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 10, - textDirection: TextDirection.ltr, - )); - builder.addText('abc'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 30.8)); - - expect(paragraph.width, 30); - expect(paragraph.height, 10); - }); -} diff --git a/lib/web_ui/test/path_test.dart b/lib/web_ui/test/path_test.dart index 6f19c9cc8f439..55f9496dd16ab 100644 --- a/lib/web_ui/test/path_test.dart +++ b/lib/web_ui/test/path_test.dart @@ -2,15 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 -import 'dart:js_util' as js_util; import 'dart:html' as html; +import 'dart:js_util' as js_util; import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart' hide window; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' hide window; import 'matchers.dart'; @@ -22,7 +21,7 @@ void testMain() { group('Path', () { test('Should have no subpaths when created', () { final SurfacePath path = SurfacePath(); - expect(path.isEmpty, true); + expect(path.isEmpty, isTrue); }); test('LineTo should add command', () { @@ -83,12 +82,32 @@ void testMain() { test('Should detect rectangular path', () { final SurfacePath path = SurfacePath(); path.addRect(const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); - expect(path.webOnlyPathAsRect, const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); + expect(path.toRect(), const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); + }); + + test('Should detect horizontal line path', () { + SurfacePath path = SurfacePath(); + path.moveTo(10, 20); + path.lineTo(100, 0); + expect(path.toStraightLine(), null); + path = SurfacePath(); + path.moveTo(10, 20); + path.lineTo(200, 20); + final Rect r = path.toStraightLine()!; + expect(r, equals(const Rect.fromLTRB(10, 20, 200, 20))); + }); + + test('Should detect vertical line path', () { + final SurfacePath path = SurfacePath(); + path.moveTo(10, 20); + path.lineTo(10, 200); + final Rect r = path.toStraightLine()!; + expect(r, equals(const Rect.fromLTRB(10, 20, 10, 200))); }); test('Should detect non rectangular path if empty', () { final SurfacePath path = SurfacePath(); - expect(path.webOnlyPathAsRect, null); + expect(path.toRect(), null); }); test('Should detect non rectangular path if there are multiple subpaths', @@ -96,7 +115,7 @@ void testMain() { final SurfacePath path = SurfacePath(); path.addRect(const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); path.addRect(const Rect.fromLTWH(5.0, 6.0, 7.0, 8.0)); - expect(path.webOnlyPathAsRect, null); + expect(path.toRect(), null); }); test('Should detect rounded rectangular path', () { @@ -105,20 +124,20 @@ void testMain() { const Rect.fromLTRB(1.0, 2.0, 30.0, 40.0), const Radius.circular(2.0))); expect( - path.webOnlyPathAsRoundedRect, + path.toRoundedRect(), RRect.fromRectAndRadius(const Rect.fromLTRB(1.0, 2.0, 30.0, 40.0), const Radius.circular(2.0))); }); test('Should detect non rounded rectangular path if empty', () { final SurfacePath path = SurfacePath(); - expect(path.webOnlyPathAsRoundedRect, null); + expect(path.toRoundedRect(), null); }); test('Should detect rectangular path is not round', () { final SurfacePath path = SurfacePath(); path.addRect(const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0)); - expect(path.webOnlyPathAsRoundedRect, null); + expect(path.toRoundedRect(), null); }); test( @@ -129,7 +148,7 @@ void testMain() { const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), const Radius.circular(2.0))); path.addRRect(RRect.fromRectAndRadius( const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), const Radius.circular(2.0))); - expect(path.webOnlyPathAsRoundedRect, null); + expect(path.toRoundedRect(), null); }); test('Should compute bounds as empty for empty and moveTo only path', () { @@ -146,67 +165,67 @@ void testMain() { expect(emptyPath.getBounds(), Rect.zero); final SurfacePath path = SurfacePath(); - path.addRect(Rect.fromLTWH(0, 0, 270, 45)); - path.addRect(Rect.fromLTWH(134.5, 0, 1, 45)); + path.addRect(const Rect.fromLTWH(0, 0, 270, 45)); + path.addRect(const Rect.fromLTWH(134.5, 0, 1, 45)); expect(path.getBounds(), const Rect.fromLTRB(0, 0, 270, 45)); }); test('Should compute bounds for addRRect', () { SurfacePath path = SurfacePath(); - final Rect bounds = Rect.fromLTRB(30, 40, 400, 300); + const Rect bounds = Rect.fromLTRB(30, 40, 400, 300); RRect rrect = RRect.fromRectAndCorners(bounds, - topLeft: Radius.elliptical(1, 2), - topRight: Radius.elliptical(3, 4), - bottomLeft: Radius.elliptical(5, 6), - bottomRight: Radius.elliptical(7, 8)); + topLeft: const Radius.elliptical(1, 2), + topRight: const Radius.elliptical(3, 4), + bottomLeft: const Radius.elliptical(5, 6), + bottomRight: const Radius.elliptical(7, 8)); path.addRRect(rrect); expect(path.getBounds(), bounds); - expect(path.webOnlyPathAsRoundedRect, rrect); + expect(path.toRoundedRect(), rrect); path = SurfacePath(); rrect = RRect.fromRectAndCorners(bounds, - topLeft: Radius.elliptical(0, 2), - topRight: Radius.elliptical(3, 4), - bottomLeft: Radius.elliptical(5, 6), - bottomRight: Radius.elliptical(7, 8)); + topLeft: const Radius.elliptical(0, 2), + topRight: const Radius.elliptical(3, 4), + bottomLeft: const Radius.elliptical(5, 6), + bottomRight: const Radius.elliptical(7, 8)); path.addRRect(rrect); expect(path.getBounds(), bounds); - expect(path.webOnlyPathAsRoundedRect, rrect); + expect(path.toRoundedRect(), rrect); path = SurfacePath(); rrect = RRect.fromRectAndCorners(bounds, - topLeft: Radius.elliptical(0, 0), - topRight: Radius.elliptical(3, 4), - bottomLeft: Radius.elliptical(5, 6), - bottomRight: Radius.elliptical(7, 8)); + topLeft: const Radius.elliptical(0, 0), + topRight: const Radius.elliptical(3, 4), + bottomLeft: const Radius.elliptical(5, 6), + bottomRight: const Radius.elliptical(7, 8)); path.addRRect(rrect); expect(path.getBounds(), bounds); - expect(path.webOnlyPathAsRoundedRect, rrect); + expect(path.toRoundedRect(), rrect); path = SurfacePath(); rrect = RRect.fromRectAndCorners(bounds, - topLeft: Radius.elliptical(1, 2), - topRight: Radius.elliptical(0, 0), - bottomLeft: Radius.elliptical(5, 6), - bottomRight: Radius.elliptical(7, 8)); + topLeft: const Radius.elliptical(1, 2), + topRight: const Radius.elliptical(0, 0), + bottomLeft: const Radius.elliptical(5, 6), + bottomRight: const Radius.elliptical(7, 8)); path.addRRect(rrect); expect(path.getBounds(), bounds); - expect(path.webOnlyPathAsRoundedRect, rrect); + expect(path.toRoundedRect(), rrect); path = SurfacePath(); rrect = RRect.fromRectAndCorners(bounds, - topLeft: Radius.elliptical(1, 2), - topRight: Radius.elliptical(3, 4), - bottomLeft: Radius.elliptical(0, 0), - bottomRight: Radius.elliptical(7, 8)); + topLeft: const Radius.elliptical(1, 2), + topRight: const Radius.elliptical(3, 4), + bottomLeft: const Radius.elliptical(0, 0), + bottomRight: const Radius.elliptical(7, 8)); path.addRRect(rrect); expect(path.getBounds(), bounds); - expect(path.webOnlyPathAsRoundedRect, rrect); + expect(path.toRoundedRect(), rrect); path = SurfacePath(); rrect = RRect.fromRectAndCorners(bounds, - topLeft: Radius.elliptical(1, 2), - topRight: Radius.elliptical(3, 4), - bottomLeft: Radius.elliptical(5, 6), - bottomRight: Radius.elliptical(0, 0)); + topLeft: const Radius.elliptical(1, 2), + topRight: const Radius.elliptical(3, 4), + bottomLeft: const Radius.elliptical(5, 6), + bottomRight: const Radius.elliptical(0, 0)); path.addRRect(rrect); expect(path.getBounds(), bounds); - expect(path.webOnlyPathAsRoundedRect, rrect); + expect(path.toRoundedRect(), rrect); }); test('Should compute bounds for lines', () { @@ -223,7 +242,7 @@ void testMain() { test('Should compute bounds for polygon', () { final SurfacePath path = SurfacePath(); - path.addPolygon([ + path.addPolygon(const [ Offset(50, 100), Offset(250, 100), Offset(152, 180), @@ -356,10 +375,10 @@ void testMain() { path.lineTo(110, 190); path.lineTo(50, 190); path.close(); - expect(path.contains(Offset(80, 190)), true); - expect(path.contains(Offset(110, 80)), true); - expect(path.contains(Offset(110, 190)), true); - expect(path.contains(Offset(110, 191)), false); + expect(path.contains(const Offset(80, 190)), isTrue); + expect(path.contains(const Offset(110, 80)), isTrue); + expect(path.contains(const Offset(110, 190)), isTrue); + expect(path.contains(const Offset(110, 191)), isFalse); }); test('Should not contain top-left of beveled border', () { @@ -373,7 +392,7 @@ void testMain() { path.lineTo(15, 40); path.lineTo(10, 35); path.close(); - expect(path.contains(Offset(10, 20)), false); + expect(path.contains(const Offset(10, 20)), isFalse); }); test('Computes contains for cubic curves', () { @@ -387,8 +406,8 @@ void testMain() { path.lineTo(15, 40); path.cubicTo(10, 40, 10, 40, 10, 35); path.close(); - expect(path.contains(Offset(10, 20)), false); - expect(path.contains(Offset(30, 40)), false); + expect(path.contains(const Offset(10, 20)), isFalse); + expect(path.contains(const Offset(30, 40)), isFalse); }); // Regression test for https://github.com/flutter/flutter/issues/44470 @@ -401,10 +420,10 @@ void testMain() { ..lineTo(0, 100) ..lineTo(50, 0) ..close(); - expect(path.contains(Offset(50, 50)), isTrue); + expect(path.contains(const Offset(50, 50)), isTrue); js_util.setProperty(html.window, 'devicePixelRatio', 1.0); window.debugOverrideDevicePixelRatio(1.0); - // TODO: Investigate failure on CI. Locally this passes. + // TODO(ferhat): Investigate failure on CI. Locally this passes. // [Exception... "Failure" nsresult: "0x80004005 (NS_ERROR_FAILURE)" }, skip: browserEngine == BrowserEngine.firefox); @@ -417,22 +436,22 @@ void testMain() { test('Should hit test correctly for malformed rrect', () { // Correctly formed rrect. final Path path1 = Path() - ..addRRect(RRect.fromLTRBR(50, 50, 100, 100, Radius.circular(20))); - expect(path1.contains(Offset(75, 75)), isTrue); - expect(path1.contains(Offset(52, 75)), isTrue); - expect(path1.contains(Offset(50, 50)), isFalse); - expect(path1.contains(Offset(100, 50)), isFalse); - expect(path1.contains(Offset(100, 100)), isFalse); - expect(path1.contains(Offset(50, 100)), isFalse); + ..addRRect(RRect.fromLTRBR(50, 50, 100, 100, const Radius.circular(20))); + expect(path1.contains(const Offset(75, 75)), isTrue); + expect(path1.contains(const Offset(52, 75)), isTrue); + expect(path1.contains(const Offset(50, 50)), isFalse); + expect(path1.contains(const Offset(100, 50)), isFalse); + expect(path1.contains(const Offset(100, 100)), isFalse); + expect(path1.contains(const Offset(50, 100)), isFalse); final Path path2 = Path() - ..addRRect(RRect.fromLTRBR(50, 50, 100, 100, Radius.circular(100))); - expect(path2.contains(Offset(75, 75)), isTrue); - expect(path2.contains(Offset(52, 75)), isTrue); - expect(path2.contains(Offset(50, 50)), isFalse); - expect(path2.contains(Offset(100, 50)), isFalse); - expect(path2.contains(Offset(100, 100)), isFalse); - expect(path2.contains(Offset(50, 100)), isFalse); + ..addRRect(RRect.fromLTRBR(50, 50, 100, 100, const Radius.circular(100))); + expect(path2.contains(const Offset(75, 75)), isTrue); + expect(path2.contains(const Offset(52, 75)), isTrue); + expect(path2.contains(const Offset(50, 50)), isFalse); + expect(path2.contains(const Offset(100, 50)), isFalse); + expect(path2.contains(const Offset(100, 100)), isFalse); + expect(path2.contains(const Offset(50, 100)), isFalse); }); test('Should set segment masks', () { @@ -456,21 +475,22 @@ void testMain() { test('Should be able to construct from empty path', () { final SurfacePath path = SurfacePath(); - final SurfacePath? path2 = SurfacePath.from(path); - assert(path2 != null, true); + expect(path.isEmpty, isTrue); + final SurfacePath path2 = SurfacePath.from(path); + expect(path2.isEmpty, isTrue); }); }); group('PathRef', () { test('Should return empty when created', () { final PathRef pathRef = PathRef(); - expect(pathRef.isEmpty, true); + expect(pathRef.isEmpty, isTrue); }); test('Should return non-empty when mutated', () { final PathRef pathRef = PathRef(); pathRef.growForVerb(SPath.kMoveVerb, 0); - expect(pathRef.isEmpty, false); + expect(pathRef.isEmpty, isFalse); }); }); group('PathRefIterator', () { @@ -536,5 +556,19 @@ void testMain() { end = iter.skipToNextContour(); expect(start, end); }); + + /// Regression test for https://github.com/flutter/flutter/issues/68702. + test('Path should return correct bounds after transform', () { + final Path path1 = Path() + ..moveTo(100, 100) + ..lineTo(200, 100) + ..lineTo(150, 200) + ..close(); + final SurfacePath path2 = Path.from(path1) as SurfacePath; + final Rect bounds = path2.pathRef.getBounds(); + final SurfacePath transformedPath = path2.transform( + Matrix4.identity().scaled(0.5, 0.5).toFloat64()); + expect(transformedPath.pathRef.getBounds(), isNot(bounds)); + }); }); } diff --git a/lib/web_ui/test/rect_test.dart b/lib/web_ui/test/rect_test.dart index a526b4590e9dc..3899e4ffe4390 100644 --- a/lib/web_ui/test/rect_test.dart +++ b/lib/web_ui/test/rect_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; import 'package:ui/ui.dart'; diff --git a/lib/web_ui/test/rrect_test.dart b/lib/web_ui/test/rrect_test.dart index 02943b8413adc..03a2d9854a04b 100644 --- a/lib/web_ui/test/rrect_test.dart +++ b/lib/web_ui/test/rrect_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; import 'package:ui/ui.dart'; diff --git a/lib/web_ui/test/spy.dart b/lib/web_ui/test/spy.dart index ed7596e7313ad..8df49dcac1065 100644 --- a/lib/web_ui/test/spy.dart +++ b/lib/web_ui/test/spy.dart @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 +import 'dart:async'; import 'dart:typed_data'; +import 'package:quiver/testing/async.dart'; import 'package:ui/src/engine.dart' hide window; import 'package:ui/ui.dart'; @@ -31,8 +32,8 @@ class PlatformMessage { /// It holds all intercepted platform messages in a [messages] list that can /// be inspected in tests. class PlatformMessagesSpy { - PlatformMessageCallback _callback; - PlatformMessageCallback _backup; + PlatformMessageCallback? _callback; + PlatformMessageCallback? _backup; bool get _isActive => _callback != null; @@ -44,8 +45,8 @@ class PlatformMessagesSpy { /// This is typically called inside a test's `setUp` callback. void setUp() { assert(!_isActive); - _callback = (String channel, ByteData data, - PlatformMessageResponseCallback callback) { + _callback = (String channel, ByteData? data, + PlatformMessageResponseCallback? callback) { messages.add(PlatformMessage( channel, const JSONMethodCodec().decodeMethodCall(data), @@ -68,3 +69,22 @@ class PlatformMessagesSpy { window.onPlatformMessage = _backup; } } + +/// Runs code in a [FakeAsync] zone and spies on what's going on in it. +class ZoneSpy { + final FakeAsync fakeAsync = FakeAsync(); + final List printLog = []; + + dynamic run(dynamic Function() function) { + final ZoneSpecification printInterceptor = ZoneSpecification( + print: (Zone self, ZoneDelegate parent, Zone zone, String line) { + printLog.add(line); + }, + ); + return Zone.current.fork(specification: printInterceptor).run(() { + return fakeAsync.run((FakeAsync self) { + return function(); + }); + }); + } +} diff --git a/lib/web_ui/test/text/canvas_paragraph_builder_test.dart b/lib/web_ui/test/text/canvas_paragraph_builder_test.dart new file mode 100644 index 0000000000000..a9e5122b90a20 --- /dev/null +++ b/lib/web_ui/test/text/canvas_paragraph_builder_test.dart @@ -0,0 +1,427 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +bool get isIosSafari => browserEngine == BrowserEngine.webkit && + operatingSystem == OperatingSystem.iOs; + +String fontFamilyToAttribute(String fontFamily) { + fontFamily = canonicalizeFontFamily(fontFamily)!; + if (browserEngine == BrowserEngine.firefox) { + fontFamily = fontFamily.replaceAll('"', '"'); + } else if (browserEngine == BrowserEngine.blink || + browserEngine == BrowserEngine.samsung || + browserEngine == BrowserEngine.webkit) { + fontFamily = fontFamily.replaceAll('"', ''); + } + return 'font-family: $fontFamily;'; +} + +final String defaultFontFamily = fontFamilyToAttribute('Ahem'); +const String defaultColor = 'color: rgb(255, 0, 0);'; +const String defaultFontSize = 'font-size: 14px;'; +const String paragraphStyle = + 'position: absolute; white-space: pre;'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + await webOnlyInitializeTestDomRenderer(); + + setUpAll(() { + WebExperiments.ensureInitialized(); + }); + + test('Builds a text-only canvas paragraph', () { + final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 13.0); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.addText('Hello'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.paragraphStyle, style); + expect(paragraph.toPlainText(), 'Hello'); + expect(paragraph.spans, hasLength(1)); + + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '

', + ); + + // Should break "Hello" into "Hel" and "lo". + paragraph.layout(const ParagraphConstraints(width: 39.0)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hel
lo' + '
' + '

', + ); + + final ParagraphSpan span = paragraph.spans.single; + expect(span, isA()); + final FlatTextSpan textSpan = span as FlatTextSpan; + expect(textSpan.textOf(paragraph), 'Hello'); + expect(textSpan.style, styleWithDefaults(fontSize: 13.0)); + }); + + test('Correct defaults', () { + final EngineParagraphStyle style = EngineParagraphStyle(); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.addText('Hello'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.paragraphStyle, style); + expect(paragraph.toPlainText(), 'Hello'); + expect(paragraph.spans, hasLength(1)); + + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '

', + ); + + final FlatTextSpan textSpan = paragraph.spans.single as FlatTextSpan; + expect(textSpan.style, styleWithDefaults()); + }); + + test('Sets correct styles for max-lines', () { + final EngineParagraphStyle style = EngineParagraphStyle(maxLines: 2); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.addText('Hello'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.paragraphStyle, style); + expect(paragraph.toPlainText(), 'Hello'); + + double expectedHeight = 14.0; + if (isIosSafari) { + // On iOS Safari, the height measurement is one extra pixel. + expectedHeight++; + } + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '

', + ); + }); + + test('Sets correct styles for ellipsis', () { + final EngineParagraphStyle style = EngineParagraphStyle(ellipsis: '...'); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.addText('HelloWorld'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.paragraphStyle, style); + expect(paragraph.toPlainText(), 'HelloWorld'); + + double expectedHeight = 14.0; + if (isIosSafari) { + // On iOS Safari, the height measurement is one extra pixel. + expectedHeight++; + } + paragraph.layout(const ParagraphConstraints(width: 100.0)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hell...' + '' + '

', + ); + }); + + test('Builds a single-span paragraph with complex styles', () { + final EngineParagraphStyle style = + EngineParagraphStyle(fontSize: 13.0, height: 1.5); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.pushStyle(TextStyle(fontSize: 9.0)); + builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); + builder.pushStyle(TextStyle(fontSize: 40.0)); + builder.pop(); + builder + .pushStyle(TextStyle(fontStyle: FontStyle.italic, letterSpacing: 2.0)); + builder.addText('Hello'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.toPlainText(), 'Hello'); + expect(paragraph.spans, hasLength(1)); + + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '

', + ); + + final FlatTextSpan span = paragraph.spans.single as FlatTextSpan; + expect(span.textOf(paragraph), 'Hello'); + expect( + span.style, + styleWithDefaults( + height: 1.5, + fontSize: 9.0, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + letterSpacing: 2.0, + ), + ); + }); + + test('Builds a multi-span paragraph', () { + final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 13.0); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); + builder.addText('Hello'); + builder.pop(); + builder.pushStyle(TextStyle(fontStyle: FontStyle.italic)); + builder.addText(' world'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.toPlainText(), 'Hello world'); + expect(paragraph.spans, hasLength(2)); + + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '' + ' world' + '' + '

', + ); + + // Should break "Hello world" into "Hello" and " world". + paragraph.layout(const ParagraphConstraints(width: 75.0)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '' + '
world' + '
' + '

', + ); + + final FlatTextSpan hello = paragraph.spans.first as FlatTextSpan; + expect(hello.textOf(paragraph), 'Hello'); + expect( + hello.style, + styleWithDefaults( + fontSize: 13.0, + fontWeight: FontWeight.bold, + ), + ); + + final FlatTextSpan world = paragraph.spans.last as FlatTextSpan; + expect(world.textOf(paragraph), ' world'); + expect( + world.style, + styleWithDefaults( + fontSize: 13.0, + fontStyle: FontStyle.italic, + ), + ); + }); + + test('Builds a multi-span paragraph with complex styles', () { + final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 13.0); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); + builder.pushStyle(TextStyle(height: 2.0)); + builder.addText('Hello'); + builder.pop(); // pop TextStyle(height: 2.0). + builder.pushStyle(TextStyle(fontStyle: FontStyle.italic)); + builder.addText(' world'); + builder.pushStyle(TextStyle(fontWeight: FontWeight.normal)); + builder.addText('!'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.toPlainText(), 'Hello world!'); + expect(paragraph.spans, hasLength(3)); + + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'Hello' + '' + '' + ' world' + '' + '' + '!' + '' + '

', + ); + + final FlatTextSpan hello = paragraph.spans[0] as FlatTextSpan; + expect(hello.textOf(paragraph), 'Hello'); + expect( + hello.style, + styleWithDefaults( + fontSize: 13.0, + fontWeight: FontWeight.bold, + height: 2.0, + ), + ); + + final FlatTextSpan world = paragraph.spans[1] as FlatTextSpan; + expect(world.textOf(paragraph), ' world'); + expect( + world.style, + styleWithDefaults( + fontSize: 13.0, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + ), + ); + + final FlatTextSpan bang = paragraph.spans[2] as FlatTextSpan; + expect(bang.textOf(paragraph), '!'); + expect( + bang.style, + styleWithDefaults( + fontSize: 13.0, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.italic, + ), + ); + }); + + test('Paragraph with new lines generates correct DOM', () { + final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 13.0); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.addText('First\nSecond '); + builder.pushStyle(TextStyle(fontStyle: FontStyle.italic)); + builder.addText('ThirdLongLine'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.toPlainText(), 'First\nSecond ThirdLongLine'); + expect(paragraph.spans, hasLength(2)); + + // There's a new line between "First" and "Second", but "Second" and + // "ThirdLongLine" remain together since constraints are infinite. + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'First
Second ' + '
' + '' + 'ThirdLongLine' + '' + '

', + ); + + // Should break the paragraph into "First", "Second" and "ThirdLongLine". + paragraph.layout(const ParagraphConstraints(width: 180.0)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'First
Second
' + '
' + '' + 'ThirdLongLine' + '' + '

', + ); + }); + + test('various font sizes', () { + // Paragraphs and spans force the Ahem font in test mode. We need to trick + // them into thinking they are not in test mode, so they use the provided + // font family. + debugEmulateFlutterTesterEnvironment = false; + final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 12.0, fontFamily: 'first'); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + + builder.addText('First '); + builder.pushStyle(TextStyle(fontSize: 18.0, fontFamily: 'second')); + builder.addText('Second '); + builder.pushStyle(TextStyle(fontSize: 10.0, fontFamily: 'third')); + builder.addText('Third'); + + final CanvasParagraph paragraph = builder.build(); + expect(paragraph.toPlainText(), 'First Second Third'); + expect(paragraph.spans, hasLength(3)); + + // The paragraph should take the font size and family from the span with the + // greatest font size. + paragraph.layout(const ParagraphConstraints(width: double.infinity)); + expect( + paragraph.toDomElement().outerHtml, + '

' + '' + 'First ' + '' + '' + 'Second ' + '' + '' + 'Third' + '' + '

', + ); + debugEmulateFlutterTesterEnvironment = true; + }); +} + +TextStyle styleWithDefaults({ + Color color = const Color(0xFFFF0000), + String fontFamily = DomRenderer.defaultFontFamily, + double fontSize = DomRenderer.defaultFontSize, + FontWeight? fontWeight, + FontStyle? fontStyle, + double? height, + double? letterSpacing, +}) { + return TextStyle( + color: color, + fontFamily: fontFamily, + fontSize: fontSize, + fontWeight: fontWeight, + fontStyle: fontStyle, + height: height, + letterSpacing: letterSpacing, + ); +} diff --git a/lib/web_ui/test/text/canvas_paragraph_test.dart b/lib/web_ui/test/text/canvas_paragraph_test.dart new file mode 100644 index 0000000000000..6d287261782d3 --- /dev/null +++ b/lib/web_ui/test/text/canvas_paragraph_test.dart @@ -0,0 +1,746 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import '../html/paragraph/helper.dart'; + +const ui.Color white = ui.Color(0xFFFFFFFF); +const ui.Color black = ui.Color(0xFF000000); +const ui.Color red = ui.Color(0xFFFF0000); +const ui.Color green = ui.Color(0xFF00FF00); +const ui.Color blue = ui.Color(0xFF0000FF); + +final EngineParagraphStyle ahemStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, +); + +ui.ParagraphConstraints constrain(double width) { + return ui.ParagraphConstraints(width: width); +} + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + await ui.webOnlyInitializeTestDomRenderer(); + + group('$CanvasParagraph.getBoxesForRange', () { + test('return empty list for invalid ranges', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('Lorem ipsum'); + }) + ..layout(constrain(double.infinity)); + + expect(paragraph.getBoxesForRange(-1, 0), []); + expect(paragraph.getBoxesForRange(0, 0), []); + expect(paragraph.getBoxesForRange(11, 11), []); + expect(paragraph.getBoxesForRange(11, 12), []); + expect(paragraph.getBoxesForRange(4, 3), []); + }); + + test('handles single-line multi-span paragraphs', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('ipsum '); + builder.pop(); + builder.addText('.'); + }) + ..layout(constrain(double.infinity)); + + // Within the first span "Lorem ". + + expect( + // "or" + paragraph.getBoxesForRange(1, 3), + [ + box(10, 0, 30, 10), + ], + ); + expect( + // "Lorem" + paragraph.getBoxesForRange(0, 5), + [ + box(0, 0, 50, 10), + ], + ); + // Make sure the trailing space is also included in the box. + expect( + // "Lorem " + paragraph.getBoxesForRange(0, 6), + [ + // "Lorem" + box(0, 0, 50, 10), + // " " + box(50, 0, 60, 10), + ], + ); + + // Within the second span "ipsum ". + + expect( + // "psum" + paragraph.getBoxesForRange(7, 11), + [ + box(70, 0, 110, 10), + ], + ); + expect( + // "um " + paragraph.getBoxesForRange(9, 12), + [ + // "um" + box(90, 0, 110, 10), + // " " + box(110, 0, 120, 10), + ], + ); + + // Across the two spans "Lorem " and "ipsum ". + + expect( + // "rem ipsum" + paragraph.getBoxesForRange(2, 11), + [ + // "rem" + box(20, 0, 50, 10), + // " " + box(50, 0, 60, 10), + // "ipsum" + box(60, 0, 110, 10), + ], + ); + + // Across all spans "Lorem ", "ipsum ", ".". + + expect( + // "Lorem ipsum ." + paragraph.getBoxesForRange(0, 13), + [ + // "Lorem" + box(0, 0, 50, 10), + // " " + box(50, 0, 60, 10), + // "ipsum" + box(60, 0, 110, 10), + // " " + box(110, 0, 120, 10), + // "." + box(120, 0, 130, 10), + ], + ); + }); + + test('handles multi-line single-span paragraphs', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('Lorem ipsum dolor sit'); + }) + ..layout(constrain(90.0)); + + // Lines: + // "Lorem " + // "ipsum " + // "dolor sit" + + // Within the first line "Lorem ". + + expect( + // "or" + paragraph.getBoxesForRange(1, 3), + [ + box(10, 0, 30, 10), + ], + ); + // Make sure the trailing space at the end of line is also included in the + // box. + expect( + // "Lorem " + paragraph.getBoxesForRange(0, 6), + [ + // "Lorem" + box(0, 0, 50, 10), + // " " + box(50, 0, 60, 10), + ], + ); + + // Within the second line "ipsum ". + + expect( + // "psum " + paragraph.getBoxesForRange(7, 12), + [ + // "psum" + box(10, 10, 50, 20), + // " " + box(50, 10, 60, 20), + ], + ); + + // Across all lines. + + expect( + // "em " + // "ipsum " + // "dolor s" + paragraph.getBoxesForRange(3, 19), + [ + // "em" + box(30, 0, 50, 10), + // " " + box(50, 0, 60, 10), + // "ipsum" + box(0, 10, 50, 20), + // " " + box(50, 10, 60, 20), + // "dolor" + box(0, 20, 50, 30), + // " " + box(50, 20, 60, 30), + // "s" + box(60, 20, 70, 30), + ], + ); + }); + + test('handles multi-line multi-span paragraphs', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem ipsum '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('dolor '); + builder.pop(); + builder.addText('sit'); + }) + ..layout(constrain(90.0)); + + // Lines: + // "Lorem " + // "ipsum " + // "dolor sit" + + // Within the first line "Lorem ". + + expect( + // "ore" + paragraph.getBoxesForRange(1, 4), + [ + box(10, 0, 40, 10), + ], + ); + expect( + // "Lorem " + paragraph.getBoxesForRange(0, 6), + [ + // "Lorem" + box(0, 0, 50, 10), + // " " + box(50, 0, 60, 10), + ], + ); + + // Within the second line "ipsum ". + + expect( + // "psum " + paragraph.getBoxesForRange(7, 12), + [ + // "psum" + box(10, 10, 50, 20), + // " " + box(50, 10, 60, 20), + ], + ); + + // Within the third line "dolor sit" which is made of 2 spans. + + expect( + // "lor sit" + paragraph.getBoxesForRange(14, 21), + [ + // "lor" + box(20, 20, 50, 30), + // " " + box(50, 20, 60, 30), + // "sit" + box(60, 20, 90, 30), + ], + ); + + // Across all lines. + + expect( + // "em " + // "ipsum " + // "dolor s" + paragraph.getBoxesForRange(3, 19), + [ + // "em" + box(30, 0, 50, 10), + // " " + box(50, 0, 60, 10), + // "ipsum" + box(0, 10, 50, 20), + // " " + box(50, 10, 60, 20), + // "dolor" + box(0, 20, 50, 30), + // " " + box(50, 20, 60, 30), + // "s" + box(60, 20, 70, 30), + ], + ); + }); + + test('handles spans with varying heights/baselines', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(fontSize: 20.0)); + // width = 20.0 * 6 = 120.0 + // baseline = 20.0 * 80% = 16.0 + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(fontSize: 40.0)); + // width = 40.0 * 6 = 240.0 + // baseline = 40.0 * 80% = 32.0 + builder.addText('ipsum '); + builder.pushStyle(EngineTextStyle.only(fontSize: 10.0)); + // width = 10.0 * 6 = 60.0 + // baseline = 10.0 * 80% = 8.0 + builder.addText('dolor '); + builder.pushStyle(EngineTextStyle.only(fontSize: 30.0)); + // width = 30.0 * 4 = 120.0 + // baseline = 30.0 * 80% = 24.0 + builder.addText('sit '); + builder.pushStyle(EngineTextStyle.only(fontSize: 20.0)); + // width = 20.0 * 4 = 80.0 + // baseline = 20.0 * 80% = 16.0 + builder.addText('amet'); + }) + ..layout(constrain(420.0)); + + // Lines: + // "Lorem ipsum dolor " (width: 420, height: 40, baseline: 32) + // "sit amet" (width: 200, height: 30, baseline: 24) + + expect( + // "em ipsum dol" + paragraph.getBoxesForRange(3, 15), + [ + // "em" + box(60, 16, 100, 36), + // " " + box(100, 16, 120, 36), + // "ipsum" + box(120, 0, 320, 40), + // " " + box(320, 0, 360, 40), + // "dol" + box(360, 24, 390, 34), + ], + ); + + expect( + // "sum dolor " + // "sit amet" + paragraph.getBoxesForRange(8, 26), + [ + // "sum" + box(200, 0, 320, 40), + // " " + box(320, 0, 360, 40), + // "dolor" + box(360, 24, 410, 34), + // " " + box(410, 24, 420, 34), + // "sit" + box(0, 40, 90, 70), + // " " + box(90, 40, 120, 70), + // "amet" + box(120, 48, 200, 68), + ], + ); + }); + }); + + group('$CanvasParagraph.getPositionForOffset', () { + test('handles single-line multi-span paragraphs', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('ipsum '); + builder.pop(); + builder.addText('.'); + }) + ..layout(constrain(double.infinity)); + + // Above the line, at the beginning. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, -5)), + pos(0, ui.TextAffinity.downstream), + ); + // At the top left corner of the line. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 0)), + pos(0, ui.TextAffinity.downstream), + ); + // At the beginning of the line. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 5)), + pos(0, ui.TextAffinity.downstream), + ); + // Below the line, at the end. + expect( + paragraph.getPositionForOffset(const ui.Offset(130, 12)), + pos(13, ui.TextAffinity.upstream), + ); + // At the end of the line. + expect( + paragraph.getPositionForOffset(const ui.Offset(130, 5)), + pos(13, ui.TextAffinity.upstream), + ); + // On the left half of "p" in "ipsum". + expect( + paragraph.getPositionForOffset(const ui.Offset(74, 5)), + pos(7, ui.TextAffinity.downstream), + ); + // On the left half of "p" in "ipsum" (above the line). + expect( + paragraph.getPositionForOffset(const ui.Offset(74, -5)), + pos(7, ui.TextAffinity.downstream), + ); + // On the left half of "p" in "ipsum" (below the line). + expect( + paragraph.getPositionForOffset(const ui.Offset(74, 15)), + pos(7, ui.TextAffinity.downstream), + ); + // On the right half of "p" in "ipsum". + expect( + paragraph.getPositionForOffset(const ui.Offset(76, 5)), + pos(8, ui.TextAffinity.upstream), + ); + // At the top of the line, on the left half of "p" in "ipsum". + expect( + paragraph.getPositionForOffset(const ui.Offset(74, 0)), + pos(7, ui.TextAffinity.downstream), + ); + // At the top of the line, on the right half of "p" in "ipsum". + expect( + paragraph.getPositionForOffset(const ui.Offset(76, 0)), + pos(8, ui.TextAffinity.upstream), + ); + }); + + test('handles multi-line single-span paragraphs', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('Lorem ipsum dolor sit'); + }) + ..layout(constrain(90.0)); + + // Lines: + // "Lorem " + // "ipsum " + // "dolor sit" + + // Above the first line, at the beginning. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, -5)), + pos(0, ui.TextAffinity.downstream), + ); + // At the top left corner of the line. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 0)), + pos(0, ui.TextAffinity.downstream), + ); + // At the beginning of the first line. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 5)), + pos(0, ui.TextAffinity.downstream), + ); + // At the end of the first line. + expect( + paragraph.getPositionForOffset(const ui.Offset(60, 5)), + pos(6, ui.TextAffinity.upstream), + ); + // At the end of the first line (above the line). + expect( + paragraph.getPositionForOffset(const ui.Offset(60, -5)), + pos(6, ui.TextAffinity.upstream), + ); + // After the end of the first line to the right. + expect( + paragraph.getPositionForOffset(const ui.Offset(70, 5)), + pos(6, ui.TextAffinity.upstream), + ); + // On the left half of " " in "Lorem ". + expect( + paragraph.getPositionForOffset(const ui.Offset(54, 5)), + pos(5, ui.TextAffinity.downstream), + ); + // On the right half of " " in "Lorem ". + expect( + paragraph.getPositionForOffset(const ui.Offset(56, 5)), + pos(6, ui.TextAffinity.upstream), + ); + + // At the beginning of the second line "ipsum ". + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 15)), + pos(6, ui.TextAffinity.downstream), + ); + // At the end of the second line. + expect( + paragraph.getPositionForOffset(const ui.Offset(60, 15)), + pos(12, ui.TextAffinity.upstream), + ); + // After the end of the second line to the right. + expect( + paragraph.getPositionForOffset(const ui.Offset(70, 15)), + pos(12, ui.TextAffinity.upstream), + ); + + // Below the third line "dolor sit", at the end. + expect( + paragraph.getPositionForOffset(const ui.Offset(90, 40)), + pos(21, ui.TextAffinity.upstream), + ); + // At the end of the third line. + expect( + paragraph.getPositionForOffset(const ui.Offset(90, 25)), + pos(21, ui.TextAffinity.upstream), + ); + // After the end of the third line to the right. + expect( + paragraph.getPositionForOffset(const ui.Offset(100, 25)), + pos(21, ui.TextAffinity.upstream), + ); + // On the left half of " " in "dolor sit". + expect( + paragraph.getPositionForOffset(const ui.Offset(54, 25)), + pos(17, ui.TextAffinity.downstream), + ); + // On the right half of " " in "dolor sit". + expect( + paragraph.getPositionForOffset(const ui.Offset(56, 25)), + pos(18, ui.TextAffinity.upstream), + ); + }); + + test('handles multi-line multi-span paragraphs', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem ipsum '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('dolor '); + builder.pop(); + builder.addText('sit'); + }) + ..layout(constrain(90.0)); + + // Lines: + // "Lorem " + // "ipsum " + // "dolor sit" + + // Above the first line, at the beginning. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, -5)), + pos(0, ui.TextAffinity.downstream), + ); + // At the beginning of the first line. + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 5)), + pos(0, ui.TextAffinity.downstream), + ); + // At the end of the first line. + expect( + paragraph.getPositionForOffset(const ui.Offset(60, 5)), + pos(6, ui.TextAffinity.upstream), + ); + // After the end of the first line to the right. + expect( + paragraph.getPositionForOffset(const ui.Offset(70, 5)), + pos(6, ui.TextAffinity.upstream), + ); + // On the left half of " " in "Lorem ". + expect( + paragraph.getPositionForOffset(const ui.Offset(54, 5)), + pos(5, ui.TextAffinity.downstream), + ); + // On the right half of " " in "Lorem ". + expect( + paragraph.getPositionForOffset(const ui.Offset(56, 5)), + pos(6, ui.TextAffinity.upstream), + ); + + // At the beginning of the second line "ipsum ". + expect( + paragraph.getPositionForOffset(const ui.Offset(0, 15)), + pos(6, ui.TextAffinity.downstream), + ); + // At the end of the second line. + expect( + paragraph.getPositionForOffset(const ui.Offset(60, 15)), + pos(12, ui.TextAffinity.upstream), + ); + // After the end of the second line to the right. + expect( + paragraph.getPositionForOffset(const ui.Offset(70, 15)), + pos(12, ui.TextAffinity.upstream), + ); + + // Below the third line "dolor sit", at the end. + expect( + paragraph.getPositionForOffset(const ui.Offset(90, 40)), + pos(21, ui.TextAffinity.upstream), + ); + // At the end of the third line. + expect( + paragraph.getPositionForOffset(const ui.Offset(90, 25)), + pos(21, ui.TextAffinity.upstream), + ); + // After the end of the third line to the right. + expect( + paragraph.getPositionForOffset(const ui.Offset(100, 25)), + pos(21, ui.TextAffinity.upstream), + ); + // On the left half of " " in "dolor sit". + expect( + paragraph.getPositionForOffset(const ui.Offset(54, 25)), + pos(17, ui.TextAffinity.downstream), + ); + // On the right half of " " in "dolor sit". + expect( + paragraph.getPositionForOffset(const ui.Offset(56, 25)), + pos(18, ui.TextAffinity.upstream), + ); + }); + }); + + group('$CanvasParagraph.getLineBoundary', () { + test('single-line', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('One single line'); + }) + ..layout(constrain(400.0)); + + // "One single line".length == 15 + for (int i = 0; i < 15; i++) { + expect( + paragraph.getLineBoundary(ui.TextPosition(offset: i)), + const ui.TextRange(start: 0, end: 15), + reason: 'failed at offset $i', + ); + } + }); + + test('multi-line', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('First line\n'); + builder.addText('Second line\n'); + builder.addText('Third line'); + }) + ..layout(constrain(400.0)); + + // "First line\n".length == 11 + for (int i = 0; i < 11; i++) { + expect( + paragraph.getLineBoundary(ui.TextPosition(offset: i)), + const ui.TextRange(start: 0, end: 11), + reason: 'failed at offset $i', + ); + } + + // "Second line\n".length == 12 + for (int i = 11; i < 23; i++) { + expect( + paragraph.getLineBoundary(ui.TextPosition(offset: i)), + const ui.TextRange(start: 11, end: 23), + reason: 'failed at offset $i', + ); + } + + // "Third line".length == 10 + for (int i = 23; i < 33; i++) { + expect( + paragraph.getLineBoundary(ui.TextPosition(offset: i)), + const ui.TextRange(start: 23, end: 33), + reason: 'failed at offset $i', + ); + } + }); + }); + + test('$CanvasParagraph.getWordBoundary', () { + final ui.Paragraph paragraph = plain(ahemStyle, 'Lorem ipsum dolor'); + + const ui.TextRange loremRange = ui.TextRange(start: 0, end: 5); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 0)), loremRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 1)), loremRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 2)), loremRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 3)), loremRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 4)), loremRange); + + const ui.TextRange firstSpace = ui.TextRange(start: 5, end: 6); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 5)), firstSpace); + + const ui.TextRange ipsumRange = ui.TextRange(start: 6, end: 11); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 6)), ipsumRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 7)), ipsumRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 8)), ipsumRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 9)), ipsumRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 10)), ipsumRange); + + const ui.TextRange secondSpace = ui.TextRange(start: 11, end: 12); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 11)), secondSpace); + + const ui.TextRange dolorRange = ui.TextRange(start: 12, end: 17); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 12)), dolorRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 13)), dolorRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 14)), dolorRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 15)), dolorRange); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 16)), dolorRange); + + const ui.TextRange endRange = ui.TextRange(start: 17, end: 17); + expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 17)), endRange); + }); + + test('$CanvasParagraph.longestLine', () { + final ui.Paragraph paragraph = plain(ahemStyle, 'abcd\nabcde abc'); + paragraph.layout(const ui.ParagraphConstraints(width: 80.0)); + expect(paragraph.longestLine, 50.0); + }); + + test('$CanvasParagraph.width should be a whole integer', () { + final ui.Paragraph paragraph = plain(ahemStyle, 'abc'); + paragraph.layout(const ui.ParagraphConstraints(width: 30.8)); + + expect(paragraph.width, 30); + expect(paragraph.height, 10); + }); +} + +/// Shortcut to create a [ui.TextBox] with an optional [ui.TextDirection]. +ui.TextBox box( + double left, + double top, + double right, + double bottom, [ + ui.TextDirection direction = ui.TextDirection.ltr, +]) { + return ui.TextBox.fromLTRBD(left, top, right, bottom, direction); +} + +/// Shortcut to create a [ui.TextPosition]. +ui.TextPosition pos(int offset, ui.TextAffinity affinity) { + return ui.TextPosition(offset: offset, affinity: affinity); +} diff --git a/lib/web_ui/test/text/font_collection_test.dart b/lib/web_ui/test/text/font_collection_test.dart index b6423378ef6b0..c58e0add78d3f 100644 --- a/lib/web_ui/test/text/font_collection_test.dart +++ b/lib/web_ui/test/text/font_collection_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:html' as html; import 'package:test/bootstrap/browser.dart'; @@ -16,7 +15,7 @@ void main() { void testMain() { group('$FontManager', () { - FontManager fontManager; + late FontManager fontManager; const String _testFontUrl = 'packages/ui/assets/ahem.ttf'; setUp(() { @@ -24,78 +23,78 @@ void testMain() { }); tearDown(() { - html.document.fonts.clear(); + html.document.fonts!.clear(); }); group('regular special characters', () { test('Register Asset with no special characters', () async { - final String _testFontFamily = "Ahem"; - final List fontFamilyList = List(); + const String _testFontFamily = 'Ahem'; + final List fontFamilyList = []; fontManager.registerAsset( _testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); expect(fontFamilyList.length, equals(1)); expect(fontFamilyList.first, 'Ahem'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 skip: browserEngine == BrowserEngine.edge); test('Register Asset with white space in the family name', () async { - final String _testFontFamily = "Ahem ahem ahem"; - final List fontFamilyList = List(); + const String _testFontFamily = 'Ahem ahem ahem'; + final List fontFamilyList = []; fontManager.registerAsset( _testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); expect(fontFamilyList.length, equals(1)); expect(fontFamilyList.first, 'Ahem ahem ahem'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - // TODO(nurhan): https://github.com/flutter/flutter/issues/51142 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); test('Register Asset with capital case letters', () async { - final String _testFontFamily = "AhEm"; - final List fontFamilyList = List(); + const String _testFontFamily = 'AhEm'; + final List fontFamilyList = []; fontManager.registerAsset( _testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); expect(fontFamilyList.length, equals(1)); expect(fontFamilyList.first, 'AhEm'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 skip: browserEngine == BrowserEngine.edge); }); group('fonts with special characters', () { test('Register Asset twice with special character slash', () async { - final String _testFontFamily = '/Ahem'; - final List fontFamilyList = List(); + const String _testFontFamily = '/Ahem'; + final List fontFamilyList = []; fontManager.registerAsset( _testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); if (browserEngine != BrowserEngine.firefox) { @@ -104,24 +103,24 @@ void testMain() { expect(fontFamilyList, contains('/Ahem')); } else { expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '\"/Ahem\"'); + expect(fontFamilyList.first, '"/Ahem"'); } }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - // TODO(nurhan): https://github.com/flutter/flutter/issues/51142 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); test('Register Asset twice with exclamation mark', () async { - final String _testFontFamily = 'Ahem!!ahem'; - final List fontFamilyList = List(); + const String _testFontFamily = 'Ahem!!ahem'; + final List fontFamilyList = []; fontManager.registerAsset( _testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); if (browserEngine != BrowserEngine.firefox) { @@ -130,24 +129,24 @@ void testMain() { expect(fontFamilyList, contains('Ahem!!ahem')); } else { expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '\"Ahem!!ahem\"'); + expect(fontFamilyList.first, '"Ahem!!ahem"'); } }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - // TODO(nurhan): https://github.com/flutter/flutter/issues/51142 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); test('Register Asset twice with comma', () async { - final String _testFontFamily = 'Ahem ,ahem'; - final List fontFamilyList = List(); + const String _testFontFamily = 'Ahem ,ahem'; + final List fontFamilyList = []; fontManager.registerAsset( _testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); if (browserEngine != BrowserEngine.firefox) { @@ -156,25 +155,25 @@ void testMain() { expect(fontFamilyList, contains('Ahem ,ahem')); } else { expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '\"Ahem ,ahem\"'); + expect(fontFamilyList.first, '"Ahem ,ahem"'); } }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - // TODO(nurhan): https://github.com/flutter/flutter/issues/51142 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); test('Register Asset twice with a digit at the start of a token', () async { - final String testFontFamily = 'Ahem 1998'; - final List fontFamilyList = List(); + const String testFontFamily = 'Ahem 1998'; + final List fontFamilyList = []; fontManager.registerAsset( testFontFamily, 'url($_testFontUrl)', const {}); await fontManager.ensureFontsLoaded(); - html.document.fonts + html.document.fonts! .forEach((html.FontFace f, html.FontFace f2, html.FontFaceSet s) { - fontFamilyList.add(f.family); + fontFamilyList.add(f.family!); }); if (browserEngine != BrowserEngine.firefox) { @@ -183,11 +182,11 @@ void testMain() { expect(fontFamilyList, contains('\'Ahem 1998\'')); } else { expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '\"Ahem 1998\"'); + expect(fontFamilyList.first, '"Ahem 1998"'); } }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - // TODO(nurhan): https://github.com/flutter/flutter/issues/51142 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50770 + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 skip: browserEngine == BrowserEngine.edge || browserEngine == BrowserEngine.webkit); }); diff --git a/lib/web_ui/test/text/font_loading_test.dart b/lib/web_ui/test/text/font_loading_test.dart index 81ff90eb568b8..22e8d65271174 100644 --- a/lib/web_ui/test/text/font_loading_test.dart +++ b/lib/web_ui/test/text/font_loading_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; @@ -10,84 +9,84 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/ui.dart' as ui; import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; void main() { internalBootstrapBrowserTest(() => testMain); } -void testMain() async { +Future testMain() async { await ui.webOnlyInitializeTestDomRenderer(); group('loadFontFromList', () { const String _testFontUrl = 'packages/ui/assets/ahem.ttf'; tearDown(() { - html.document.fonts.clear(); + html.document.fonts!.clear(); }); test('surfaces error from invalid font buffer', () async { await expectLater( ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'), - throwsA(TypeMatcher())); + throwsA(const TypeMatcher())); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/56702 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - skip: (browserEngine == BrowserEngine.edge || - browserEngine == BrowserEngine.webkit)); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/50770 + skip: browserEngine == BrowserEngine.edge || + browserEngine == BrowserEngine.webkit); test('loads Blehm font from buffer', () async { - expect(_containsFontFamily('Blehm'), false); + expect(_containsFontFamily('Blehm'), isFalse); final html.HttpRequest response = await html.HttpRequest.request( _testFontUrl, responseType: 'arraybuffer'); - await ui.loadFontFromList(Uint8List.view(response.response), + await ui.loadFontFromList(Uint8List.view(response.response as ByteBuffer), fontFamily: 'Blehm'); - expect(_containsFontFamily('Blehm'), true); + expect(_containsFontFamily('Blehm'), isTrue); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/56702 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - skip: (browserEngine == BrowserEngine.edge || - browserEngine == BrowserEngine.webkit)); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/50770 + skip: browserEngine == BrowserEngine.edge || + browserEngine == BrowserEngine.webkit); test('loading font should clear measurement caches', () async { - final ui.ParagraphStyle style = ui.ParagraphStyle(); - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(style); - final ui.ParagraphConstraints constraints = + final EngineParagraphStyle style = EngineParagraphStyle(); + const ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 30.0); - builder.addText('test'); - final ui.Paragraph paragraph = builder.build(); - // Triggers the measuring and verifies the result has been cached. - paragraph.layout(constraints); - expect(TextMeasurementService.rulerManager.rulers.length, 1); + + final CanvasParagraphBuilder canvasBuilder = CanvasParagraphBuilder(style); + canvasBuilder.addText('test'); + // Triggers the measuring and verifies the ruler cache has been populated. + canvasBuilder.build().layout(constraints); + expect(Spanometer.rulers.length, 1); // Now, loads a new font using loadFontFromList. This should clear the // cache final html.HttpRequest response = await html.HttpRequest.request( _testFontUrl, responseType: 'arraybuffer'); - await ui.loadFontFromList(Uint8List.view(response.response), + await ui.loadFontFromList(Uint8List.view(response.response as ByteBuffer), fontFamily: 'Blehm'); // Verifies the font is loaded, and the cache is cleaned. - expect(_containsFontFamily('Blehm'), true); - expect(TextMeasurementService.rulerManager.rulers.length, 0); + expect(_containsFontFamily('Blehm'), isTrue); + expect(Spanometer.rulers.length, 0); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/56702 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - skip: (browserEngine == BrowserEngine.edge || - browserEngine == BrowserEngine.webkit)); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/50770 + skip: browserEngine == BrowserEngine.edge || + browserEngine == BrowserEngine.webkit); test('loading font should send font change message', () async { - final ui.PlatformMessageCallback oldHandler = ui.window.onPlatformMessage; - String actualName; - String message; - window.onPlatformMessage = (String name, ByteData data, - ui.PlatformMessageResponseCallback callback) { + final ui.PlatformMessageCallback? oldHandler = ui.window.onPlatformMessage; + String? actualName; + String? message; + window.onPlatformMessage = (String name, ByteData? data, + ui.PlatformMessageResponseCallback? callback) { actualName = name; - final buffer = data.buffer; + final ByteBuffer buffer = data!.buffer; final Uint8List list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); message = utf8.decode(list); @@ -95,25 +94,25 @@ void testMain() async { final html.HttpRequest response = await html.HttpRequest.request( _testFontUrl, responseType: 'arraybuffer'); - await ui.loadFontFromList(Uint8List.view(response.response), + await ui.loadFontFromList(Uint8List.view(response.response as ByteBuffer), fontFamily: 'Blehm'); - final Completer completer = Completer(); - html.window.requestAnimationFrame( (_) { completer.complete(true); } ); - await(completer.future); + final Completer completer = Completer(); + html.window.requestAnimationFrame( (_) { completer.complete(); } ); + await (completer.future); // ignore: unnecessary_parenthesis window.onPlatformMessage = oldHandler; expect(actualName, 'flutter/system'); expect(message, '{"type":"fontsChange"}'); }, - // TODO(nurhan): https://github.com/flutter/flutter/issues/56702 - // TODO(nurhan): https://github.com/flutter/flutter/issues/50770 - skip: (browserEngine == BrowserEngine.edge || - browserEngine == BrowserEngine.webkit)); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/50770 + skip: browserEngine == BrowserEngine.edge || + browserEngine == BrowserEngine.webkit); }); } bool _containsFontFamily(String family) { bool found = false; - html.document.fonts.forEach((html.FontFace fontFace, + html.document.fonts!.forEach((html.FontFace fontFace, html.FontFace fontFaceAgain, html.FontFaceSet fontFaceSet) { if (fontFace.family == family) { found = true; diff --git a/lib/web_ui/test/text/layout_service_helper.dart b/lib/web_ui/test/text/layout_service_helper.dart new file mode 100644 index 0000000000000..798c6e8e34766 --- /dev/null +++ b/lib/web_ui/test/text/layout_service_helper.dart @@ -0,0 +1,150 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +TestLine l( + String? displayText, + int? startIndex, + int? endIndex, { + int? endIndexWithoutNewlines, + bool? hardBreak, + double? height, + double? width, + double? widthWithTrailingSpaces, + double? left, + double? baseline, +}) { + return TestLine( + displayText: displayText, + startIndex: startIndex, + endIndex: endIndex, + endIndexWithoutNewlines: endIndexWithoutNewlines, + hardBreak: hardBreak, + height: height, + width: width, + widthWithTrailingSpaces: widthWithTrailingSpaces, + left: left, + baseline: baseline, + ); +} + +void expectLines(CanvasParagraph paragraph, List expectedLines) { + final String text = paragraph.toPlainText(); + final List lines = paragraph.computeLineMetrics(); + expect(lines, hasLength(expectedLines.length)); + for (int i = 0; i < lines.length; i++) { + final EngineLineMetrics line = lines[i]; + final TestLine expectedLine = expectedLines[i]; + + expect( + line.lineNumber, + i, + reason: '${i}th line had the wrong `lineNumber`. Expected: $i. Actual: ${line.lineNumber}', + ); + if (expectedLine.displayText != null) { + final String substring = + text.substring(line.startIndex, line.endIndexWithoutNewlines); + final String ellipsis = line.ellipsis ?? ''; + expect( + substring + ellipsis, + expectedLine.displayText, + reason: + '${i}th line had a different `displayText` value: "${line.displayText}" vs. "${expectedLine.displayText}"', + ); + } + if (expectedLine.startIndex != null) { + expect( + line.startIndex, + expectedLine.startIndex, + reason: + '${i}th line had a different `startIndex` value: "${line.startIndex}" vs. "${expectedLine.startIndex}"', + ); + } + if (expectedLine.endIndex != null) { + expect( + line.endIndex, + expectedLine.endIndex, + reason: + '${i}th line had a different `endIndex` value: "${line.endIndex}" vs. "${expectedLine.endIndex}"', + ); + } + if (expectedLine.endIndexWithoutNewlines != null) { + expect( + line.endIndexWithoutNewlines, + expectedLine.endIndexWithoutNewlines, + reason: + '${i}th line had a different `endIndexWithoutNewlines` value: "${line.endIndexWithoutNewlines}" vs. "${expectedLine.endIndexWithoutNewlines}"', + ); + } + if (expectedLine.hardBreak != null) { + expect( + line.hardBreak, + expectedLine.hardBreak, + reason: + '${i}th line had a different `hardBreak` value: "${line.hardBreak}" vs. "${expectedLine.hardBreak}"', + ); + } + if (expectedLine.height != null) { + expect( + line.height, + expectedLine.height, + reason: + '${i}th line had a different `height` value: "${line.height}" vs. "${expectedLine.height}"', + ); + } + if (expectedLine.width != null) { + expect( + line.width, + expectedLine.width, + reason: + '${i}th line had a different `width` value: "${line.width}" vs. "${expectedLine.width}"', + ); + } + if (expectedLine.widthWithTrailingSpaces != null) { + expect( + line.widthWithTrailingSpaces, + expectedLine.widthWithTrailingSpaces, + reason: + '${i}th line had a different `widthWithTrailingSpaces` value: "${line.widthWithTrailingSpaces}" vs. "${expectedLine.widthWithTrailingSpaces}"', + ); + } + if (expectedLine.left != null) { + expect( + line.left, + expectedLine.left, + reason: + '${i}th line had a different `left` value: "${line.left}" vs. "${expectedLine.left}"', + ); + } + } +} + +class TestLine { + TestLine({ + this.displayText, + this.startIndex, + this.endIndex, + this.endIndexWithoutNewlines, + this.hardBreak, + this.height, + this.width, + this.widthWithTrailingSpaces, + this.left, + this.baseline, + }); + + final String? displayText; + final int? startIndex; + final int? endIndex; + final int? endIndexWithoutNewlines; + final bool? hardBreak; + final double? height; + final double? width; + final double? widthWithTrailingSpaces; + final double? left; + final double? baseline; +} diff --git a/lib/web_ui/test/text/layout_service_plain_test.dart b/lib/web_ui/test/text/layout_service_plain_test.dart new file mode 100644 index 0000000000000..4e829eb16cb0d --- /dev/null +++ b/lib/web_ui/test/text/layout_service_plain_test.dart @@ -0,0 +1,714 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'layout_service_helper.dart'; + +const bool skipWordSpacing = true; + +final EngineParagraphStyle ahemStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, +); + +ui.ParagraphConstraints constrain(double width) { + return ui.ParagraphConstraints(width: width); +} + +CanvasParagraph plain( + EngineParagraphStyle style, + String text, { + EngineTextStyle? textStyle, +}) { + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + if (textStyle != null) { + builder.pushStyle(textStyle); + } + builder.addText(text); + return builder.build(); +} + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + await ui.webOnlyInitializeTestDomRenderer(); + + test('no text', () { + final CanvasParagraph paragraph = CanvasParagraphBuilder(ahemStyle).build(); + paragraph.layout(constrain(double.infinity)); + + expect(paragraph.maxIntrinsicWidth, 0); + expect(paragraph.minIntrinsicWidth, 0); + expect(paragraph.height, 0); + expect(paragraph.computeLineMetrics(), isEmpty); + }); + + test('preserves whitespace when measuring', () { + CanvasParagraph paragraph; + + // leading whitespaces + paragraph = plain(ahemStyle, ' abc')..layout(constrain(double.infinity)); + expect(paragraph.maxIntrinsicWidth, 60); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l(' abc', 0, 6, hardBreak: true, width: 60.0), + ]); + + // trailing whitespaces + paragraph = plain(ahemStyle, 'abc ')..layout(constrain(double.infinity)); + expect(paragraph.maxIntrinsicWidth, 60); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l('abc ', 0, 6, hardBreak: true, width: 30.0), + ]); + + // mixed whitespaces + paragraph = plain(ahemStyle, ' ab c ') + ..layout(constrain(double.infinity)); + expect(paragraph.maxIntrinsicWidth, 100); + expect(paragraph.minIntrinsicWidth, 20); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l(' ab c ', 0, 10, hardBreak: true, width: 80.0, left: 0.0), + ]); + + // single whitespace + paragraph = plain(ahemStyle, ' ')..layout(constrain(double.infinity)); + expect(paragraph.maxIntrinsicWidth, 10); + expect(paragraph.minIntrinsicWidth, 0); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l(' ', 0, 1, hardBreak: true, width: 0.0, left: 0.0), + ]); + + // whitespace only + paragraph = plain(ahemStyle, ' ')..layout(constrain(double.infinity)); + expect(paragraph.maxIntrinsicWidth, 50); + expect(paragraph.minIntrinsicWidth, 0); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l(' ', 0, 5, hardBreak: true, width: 0.0, left: 0.0), + ]); + }); + + test('uses single-line when text can fit without wrapping', () { + final CanvasParagraph paragraph = plain(ahemStyle, '12345') + ..layout(constrain(50.0)); + + // Should fit on a single line. + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 50); + expect(paragraph.minIntrinsicWidth, 50); + expect(paragraph.width, 50); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l('12345', 0, 5, hardBreak: true, width: 50.0, left: 0.0, height: 10.0, baseline: 8.0), + ]); + }); + + test('simple multi-line text', () { + final CanvasParagraph paragraph = plain(ahemStyle, 'foo bar baz') + ..layout(constrain(70.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 110); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.width, 70); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('foo bar ', 0, 8, hardBreak: false, width: 70.0, left: 0.0, height: 10.0, baseline: 8.0), + l('baz', 8, 11, hardBreak: true, width: 30.0, left: 0.0, height: 10.0, baseline: 18.0), + ]); + }); + + test('uses multi-line for long text', () { + CanvasParagraph paragraph; + + // The long text doesn't fit in 50px of width, so it needs to wrap. + paragraph = plain(ahemStyle, '1234567890')..layout(constrain(50.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 100); + expect(paragraph.minIntrinsicWidth, 100); + expect(paragraph.width, 50); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('12345', 0, 5, hardBreak: false, width: 50.0, left: 0.0, height: 10.0, baseline: 8.0), + l('67890', 5, 10, hardBreak: true, width: 50.0, left: 0.0, height: 10.0, baseline: 18.0), + ]); + + // The first word is force-broken twice. + paragraph = plain(ahemStyle, 'abcdefghijk lm')..layout(constrain(50.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 140); + expect(paragraph.minIntrinsicWidth, 110); + expect(paragraph.width, 50); + expect(paragraph.height, 30); + expectLines(paragraph, [ + l('abcde', 0, 5, hardBreak: false, width: 50.0, left: 0.0, height: 10.0, baseline: 8.0), + l('fghij', 5, 10, hardBreak: false, width: 50.0, left: 0.0, height: 10.0, baseline: 18.0), + l('k lm', 10, 14, hardBreak: true, width: 40.0, left: 0.0, height: 10.0, baseline: 28.0), + ]); + + // Constraints enough only for "abcdef" but not for the trailing space. + paragraph = plain(ahemStyle, 'abcdef gh')..layout(constrain(60.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 90); + expect(paragraph.minIntrinsicWidth, 60); + expect(paragraph.width, 60); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('abcdef ', 0, 7, hardBreak: false, width: 60.0, left: 0.0, height: 10.0, baseline: 8.0), + l('gh', 7, 9, hardBreak: true, width: 20.0, left: 0.0, height: 10.0, baseline: 18.0), + ]); + + // Constraints aren't enough even for a single character. In this case, + // we show a minimum of one character per line. + paragraph = plain(ahemStyle, 'AA')..layout(constrain(8.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 20); + expect(paragraph.minIntrinsicWidth, 20); + expect(paragraph.width, 8); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('A', 0, 1, hardBreak: false, width: 10.0, left: 0.0, height: 10.0, baseline: 8.0), + l('A', 1, 2, hardBreak: true, width: 10.0, left: 0.0, height: 10.0, baseline: 18.0), + ]); + + // Extremely narrow constraints with new line in the middle. + paragraph = plain(ahemStyle, 'AA\nA')..layout(constrain(8.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 20); + expect(paragraph.minIntrinsicWidth, 20); + expect(paragraph.width, 8); + expect(paragraph.height, 30); + expectLines(paragraph, [ + l('A', 0, 1, hardBreak: false, width: 10.0, left: 0.0, height: 10.0, baseline: 8.0), + l('A', 1, 3, hardBreak: true, width: 10.0, left: 0.0, height: 10.0, baseline: 18.0), + l('A', 3, 4, hardBreak: true, width: 10.0, left: 0.0, height: 10.0, baseline: 28.0), + ]); + + // Extremely narrow constraints with new line in the end. + paragraph = plain(ahemStyle, 'AAA\n')..layout(constrain(8.0)); + expect(paragraph.alphabeticBaseline, 8); + expect(paragraph.maxIntrinsicWidth, 30); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.width, 8); + expect(paragraph.height, 40); + expectLines(paragraph, [ + l('A', 0, 1, hardBreak: false, width: 10.0, left: 0.0, height: 10.0, baseline: 8.0), + l('A', 1, 2, hardBreak: false, width: 10.0, left: 0.0, height: 10.0, baseline: 18.0), + l('A', 2, 4, hardBreak: true, width: 10.0, left: 0.0, height: 10.0, baseline: 28.0), + l('', 4, 4, hardBreak: true, width: 0.0, left: 0.0, height: 10.0, baseline: 38.0), + ]); + }); + + test('uses multi-line for text that contains new-line', () { + final CanvasParagraph paragraph = plain(ahemStyle, '12\n34') + ..layout(constrain(50.0)); + + // Text containing newlines should always be drawn in multi-line mode. + expect(paragraph.maxIntrinsicWidth, 20); + expect(paragraph.minIntrinsicWidth, 20); + expect(paragraph.width, 50); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('12', 0, 3, hardBreak: true, width: 20.0, left: 0.0), + l('34', 3, 5, hardBreak: true, width: 20.0, left: 0.0), + ]); + }); + + test('empty lines', () { + CanvasParagraph paragraph; + + // Empty lines in the beginning. + paragraph = plain(ahemStyle, '\n\n1234')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 40); + expect(paragraph.minIntrinsicWidth, 40); + expect(paragraph.height, 30); + expectLines(paragraph, [ + l('', 0, 1, hardBreak: true, width: 0.0, left: 0.0), + l('', 1, 2, hardBreak: true, width: 0.0, left: 0.0), + l('1234', 2, 6, hardBreak: true, width: 40.0, left: 0.0), + ]); + + // Empty lines in the middle. + paragraph = plain(ahemStyle, '12\n\n345')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 30); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.height, 30); + expectLines(paragraph, [ + l('12', 0, 3, hardBreak: true, width: 20.0, left: 0.0), + l('', 3, 4, hardBreak: true, width: 0.0, left: 0.0), + l('345', 4, 7, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // Empty lines in the end. + paragraph = plain(ahemStyle, '1234\n\n')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 40); + expect(paragraph.minIntrinsicWidth, 40); + expect(paragraph.height, 30); + expectLines(paragraph, [ + l('1234', 0, 5, hardBreak: true, width: 40.0, left: 0.0), + l('', 5, 6, hardBreak: true, width: 0.0, left: 0.0), + l('', 6, 6, hardBreak: true, width: 0.0, left: 0.0), + ]); + }); + + test( + 'wraps multi-line text correctly when constraint width is infinite', + () { + final CanvasParagraph paragraph = plain(ahemStyle, '123\n456 789') + ..layout(constrain(double.infinity)); + + expect(paragraph.maxIntrinsicWidth, 70); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.width, double.infinity); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('123', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('456 789', 4, 11, hardBreak: true, width: 70.0, left: 0.0), + ]); + }, + ); + + test('takes letter spacing into account', () { + final EngineTextStyle spacedTextStyle = + EngineTextStyle.only(letterSpacing: 3); + final CanvasParagraph spacedText = + plain(ahemStyle, 'abc', textStyle: spacedTextStyle) + ..layout(constrain(100.0)); + + expect(spacedText.minIntrinsicWidth, 39); + expect(spacedText.maxIntrinsicWidth, 39); + }); + + test('takes word spacing into account', () { + final CanvasParagraph normalText = plain(ahemStyle, 'a b c') + ..layout(constrain(100.0)); + final CanvasParagraph spacedText = plain(ahemStyle, 'a b c', + textStyle: EngineTextStyle.only(wordSpacing: 1.5)) + ..layout(constrain(100.0)); + + expect( + normalText.maxIntrinsicWidth < spacedText.maxIntrinsicWidth, + isTrue, + ); + }, skip: skipWordSpacing); + + test('minIntrinsicWidth', () { + CanvasParagraph paragraph; + + // Simple case. + paragraph = plain(ahemStyle, 'abc de fghi')..layout(constrain(50.0)); + expect(paragraph.minIntrinsicWidth, 40); + expectLines(paragraph, [ + l('abc ', 0, 4, hardBreak: false, width: 30.0, left: 0.0), + l('de ', 4, 7, hardBreak: false, width: 20.0, left: 0.0), + l('fghi', 7, 11, hardBreak: true, width: 40.0, left: 0.0), + ]); + + // With new lines. + paragraph = plain(ahemStyle, 'abcd\nef\nghi')..layout(constrain(50.0)); + expect(paragraph.minIntrinsicWidth, 40); + expectLines(paragraph, [ + l('abcd', 0, 5, hardBreak: true, width: 40.0, left: 0.0), + l('ef', 5, 8, hardBreak: true, width: 20.0, left: 0.0), + l('ghi', 8, 11, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // With trailing whitespace. + paragraph = plain(ahemStyle, 'abcd efg')..layout(constrain(50.0)); + expect(paragraph.minIntrinsicWidth, 40); + expectLines(paragraph, [ + l('abcd ', 0, 10, hardBreak: false, width: 40.0, left: 0.0), + l('efg', 10, 13, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // With trailing whitespace and new lines. + paragraph = plain(ahemStyle, 'abc \ndefg')..layout(constrain(50.0)); + expect(paragraph.minIntrinsicWidth, 40); + expectLines(paragraph, [ + l('abc ', 0, 8, hardBreak: true, width: 30.0, left: 0.0), + l('defg', 8, 12, hardBreak: true, width: 40.0, left: 0.0), + ]); + + // Very long text. + paragraph = plain(ahemStyle, 'AAAAAAAAAAAA')..layout(constrain(50.0)); + expect(paragraph.minIntrinsicWidth, 120); + expectLines(paragraph, [ + l('AAAAA', 0, 5, hardBreak: false, width: 50.0, left: 0.0), + l('AAAAA', 5, 10, hardBreak: false, width: 50.0, left: 0.0), + l('AA', 10, 12, hardBreak: true, width: 20.0, left: 0.0), + ]); + }); + + test('maxIntrinsicWidth', () { + CanvasParagraph paragraph; + + // Simple case. + paragraph = plain(ahemStyle, 'abc de fghi')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 110); + expectLines(paragraph, [ + l('abc ', 0, 4, hardBreak: false, width: 30.0, left: 0.0), + l('de ', 4, 7, hardBreak: false, width: 20.0, left: 0.0), + l('fghi', 7, 11, hardBreak: true, width: 40.0, left: 0.0), + ]); + + // With new lines. + paragraph = plain(ahemStyle, 'abcd\nef\nghi')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 40); + expectLines(paragraph, [ + l('abcd', 0, 5, hardBreak: true, width: 40.0, left: 0.0), + l('ef', 5, 8, hardBreak: true, width: 20.0, left: 0.0), + l('ghi', 8, 11, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // With long whitespace. + paragraph = plain(ahemStyle, 'abcd efg')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 100); + expectLines(paragraph, [ + l('abcd ', 0, 7, hardBreak: false, width: 40.0, left: 0.0), + l('efg', 7, 10, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // With trailing whitespace. + paragraph = plain(ahemStyle, 'abc def ')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 100); + expectLines(paragraph, [ + l('abc ', 0, 4, hardBreak: false, width: 30.0, left: 0.0), + l('def ', 4, 10, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // With trailing whitespace and new lines. + paragraph = plain(ahemStyle, 'abc \ndef ')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 60); + expectLines(paragraph, [ + l('abc ', 0, 5, hardBreak: true, width: 30.0, left: 0.0), + l('def ', 5, 11, hardBreak: true, width: 30.0, left: 0.0), + ]); + + // Very long text. + paragraph = plain(ahemStyle, 'AAAAAAAAAAAA')..layout(constrain(50.0)); + expect(paragraph.maxIntrinsicWidth, 120); + expectLines(paragraph, [ + l('AAAAA', 0, 5, hardBreak: false, width: 50.0, left: 0.0), + l('AAAAA', 5, 10, hardBreak: false, width: 50.0, left: 0.0), + l('AA', 10, 12, hardBreak: true, width: 20.0, left: 0.0), + ]); + }); + + test('respects text overflow', () { + final EngineParagraphStyle overflowStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + ellipsis: '...', + ); + + // The text shouldn't be broken into multiple lines, so the height should + // be equal to a height of a single line. + final CanvasParagraph longText = plain( + overflowStyle, + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + )..layout(constrain(50.0)); + expect(longText.minIntrinsicWidth, 480); + expect(longText.maxIntrinsicWidth, 480); + expect(longText.height, 10); + expectLines(longText, [ + l('AA...', 0, 2, hardBreak: false, width: 50.0, left: 0.0), + ]); + + // The short prefix should make the text break into two lines, but the + // second line should remain unbroken. + final CanvasParagraph longTextShortPrefix = plain( + overflowStyle, + 'AAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + )..layout(constrain(50.0)); + expect(longTextShortPrefix.minIntrinsicWidth, 450); + expect(longTextShortPrefix.maxIntrinsicWidth, 450); + expect(longTextShortPrefix.height, 20); + expectLines(longTextShortPrefix, [ + l('AAA', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('AA...', 4, 6, hardBreak: false, width: 50.0, left: 0.0), + ]); + + // Constraints only enough to fit "AA" with the ellipsis, but not the + // trailing white space. + final CanvasParagraph trailingSpace = plain(overflowStyle, 'AA AAA') + ..layout(constrain(50.0)); + expect(trailingSpace.minIntrinsicWidth, 30); + expect(trailingSpace.maxIntrinsicWidth, 60); + expect(trailingSpace.height, 10); + expectLines(trailingSpace, [ + l('AA...', 0, 2, hardBreak: false, width: 50.0, left: 0.0), + ]); + + // Tiny constraints. + final CanvasParagraph paragraph = plain(overflowStyle, 'AAAA') + ..layout(constrain(30.0)); + expect(paragraph.minIntrinsicWidth, 40); + expect(paragraph.maxIntrinsicWidth, 40); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l('...', 0, 0, hardBreak: false, width: 30.0, left: 0.0), + ]); + + // Tinier constraints (not enough for the ellipsis). + paragraph.layout(constrain(10.0)); + expect(paragraph.minIntrinsicWidth, 40); + expect(paragraph.maxIntrinsicWidth, 40); + expect(paragraph.height, 10); + + // TODO(mdebbar): https://github.com/flutter/flutter/issues/34346 + // expectLines(paragraph, [ + // l('.', 0, 0, hardBreak: false, width: 10.0, left: 0.0), + // ]); + expectLines(paragraph, [ + l('...', 0, 0, hardBreak: false, width: 30.0, left: 0.0), + ]); + }); + + test('respects max lines', () { + final EngineParagraphStyle maxlinesStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + maxLines: 2, + ); + + // The height should be that of a single line. + final CanvasParagraph oneline = plain(maxlinesStyle, 'One line') + ..layout(constrain(double.infinity)); + expect(oneline.height, 10); + expectLines(oneline, [ + l('One line', 0, 8, hardBreak: true, width: 80.0, left: 0.0), + ]); + + // The height should respect max lines and be limited to two lines here. + final CanvasParagraph threelines = + plain(maxlinesStyle, 'First\nSecond\nThird') + ..layout(constrain(double.infinity)); + expect(threelines.height, 20); + expectLines(threelines, [ + l('First', 0, 6, hardBreak: true, width: 50.0, left: 0.0), + l('Second', 6, 13, hardBreak: true, width: 60.0, left: 0.0), + ]); + + // The height should respect max lines and be limited to two lines here. + final CanvasParagraph veryLong = plain( + maxlinesStyle, + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + )..layout(constrain(50.0)); + expect(veryLong.height, 20); + expectLines(veryLong, [ + l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0), + l('ipsum ', 6, 12, hardBreak: false, width: 50.0, left: 0.0), + ]); + + // Case when last line is a long unbreakable word. + final CanvasParagraph veryLongLastLine = plain( + maxlinesStyle, + 'AAA AAAAAAAAAAAAAAAAAAA', + )..layout(constrain(50.0)); + expect(veryLongLastLine.height, 20); + expectLines(veryLongLastLine, [ + l('AAA ', 0, 4, hardBreak: false, width: 30.0, left: 0.0), + l('AAAAA', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + ]); + }); + + test('respects text overflow and max lines combined', () { + final EngineParagraphStyle onelineStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + maxLines: 1, + ellipsis: '...', + ); + final EngineParagraphStyle multilineStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + maxLines: 2, + ellipsis: '...', + ); + + CanvasParagraph paragraph; + + // Simple no overflow case. + paragraph = plain(onelineStyle, 'abcdef')..layout(constrain(60.0)); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l('abcdef', 0, 6, hardBreak: true, width: 60.0, left: 0.0), + ]); + + // Simple overflow case. + paragraph = plain(onelineStyle, 'abcd efg')..layout(constrain(60.0)); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l('abc...', 0, 3, hardBreak: false, width: 60.0, left: 0.0), + ]); + + // Another simple overflow case. + paragraph = plain(onelineStyle, 'a bcde fgh')..layout(constrain(60.0)); + expect(paragraph.height, 10); + expectLines(paragraph, [ + l('a b...', 0, 3, hardBreak: false, width: 60.0, left: 0.0), + ]); + + // The ellipsis is supposed to go on the second line, but because the + // 2nd line doesn't overflow, no ellipsis is shown. + paragraph = plain(multilineStyle, 'abcdef ghijkl')..layout(constrain(60.0)); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('abcdef ', 0, 7, hardBreak: false, width: 60.0, left: 0.0), + l('ghijkl', 7, 13, hardBreak: true, width: 60.0, left: 0.0), + ]); + + // But when the 2nd line is long enough, the ellipsis is shown. + paragraph = plain(multilineStyle, 'abcd efghijkl')..layout(constrain(60.0)); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('abcd ', 0, 5, hardBreak: false, width: 40.0, left: 0.0), + l('efg...', 5, 8, hardBreak: false, width: 60.0, left: 0.0), + ]); + + // Even if the second line can be broken, we don't break it, we just + // insert the ellipsis. + paragraph = plain(multilineStyle, 'abcde f gh ijk') + ..layout(constrain(60.0)); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('abcde ', 0, 6, hardBreak: false, width: 50.0, left: 0.0), + l('f g...', 6, 9, hardBreak: false, width: 60.0, left: 0.0), + ]); + + // First line overflows but second line doesn't. + paragraph = plain(multilineStyle, 'abcdefg hijk')..layout(constrain(60.0)); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('abcdef', 0, 6, hardBreak: false, width: 60.0, left: 0.0), + l('g hijk', 6, 12, hardBreak: true, width: 60.0, left: 0.0), + ]); + + // Both first and second lines overflow. + paragraph = plain(multilineStyle, 'abcdefg hijklmnop') + ..layout(constrain(60.0)); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('abcdef', 0, 6, hardBreak: false, width: 60.0, left: 0.0), + l('g h...', 6, 9, hardBreak: false, width: 60.0, left: 0.0), + ]); + }); + + test('handles textAlign', () { + CanvasParagraph paragraph; + + EngineParagraphStyle createStyle(ui.TextAlign textAlign) { + return EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + textAlign: textAlign, + textDirection: ui.TextDirection.ltr, + ); + } + + paragraph = plain(createStyle(ui.TextAlign.start), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.end), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 40.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.center), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 10.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 20.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.left), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.right), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 40.0), + ]); + }); + + test('handles rtl with textAlign', () { + CanvasParagraph paragraph; + + EngineParagraphStyle createStyle(ui.TextAlign textAlign) { + return EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + textAlign: textAlign, + textDirection: ui.TextDirection.rtl, + ); + } + + paragraph = plain(createStyle(ui.TextAlign.start), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 40.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.end), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.center), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 10.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 20.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.left), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), + ]); + + paragraph = plain(createStyle(ui.TextAlign.right), 'abc\ndefghi') + ..layout(constrain(50.0)); + expectLines(paragraph, [ + l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), + l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), + l('i', 9, 10, hardBreak: true, width: 10.0, left: 40.0), + ]); + }); +} diff --git a/lib/web_ui/test/text/layout_service_rich_test.dart b/lib/web_ui/test/text/layout_service_rich_test.dart new file mode 100644 index 0000000000000..b59755edc3eea --- /dev/null +++ b/lib/web_ui/test/text/layout_service_rich_test.dart @@ -0,0 +1,226 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart' as ui; + +import 'layout_service_helper.dart'; + +const ui.Color white = ui.Color(0xFFFFFFFF); +const ui.Color black = ui.Color(0xFF000000); +const ui.Color red = ui.Color(0xFFFF0000); +const ui.Color green = ui.Color(0xFF00FF00); +const ui.Color blue = ui.Color(0xFF0000FF); + +final EngineParagraphStyle ahemStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, +); + +ui.ParagraphConstraints constrain(double width) { + return ui.ParagraphConstraints(width: width); +} + +CanvasParagraph rich( + EngineParagraphStyle style, + void Function(CanvasParagraphBuilder) callback, +) { + final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); + callback(builder); + return builder.build(); +} + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + await ui.webOnlyInitializeTestDomRenderer(); + + test('does not crash on empty spans', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText(''); + + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('Lorem ipsum'); + + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText(''); + }); + + expect(() => paragraph.layout(constrain(double.infinity)), returnsNormally); + }); + + test('measures spans in the same line correctly', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(fontSize: 12.0)); + // 12.0 * 6 = 72.0 (with spaces) + // 12.0 * 5 = 60.0 (without spaces) + builder.addText('Lorem '); + + builder.pushStyle(EngineTextStyle.only(fontSize: 13.0)); + // 13.0 * 6 = 78.0 (with spaces) + // 13.0 * 5 = 65.0 (without spaces) + builder.addText('ipsum '); + + builder.pushStyle(EngineTextStyle.only(fontSize: 11.0)); + // 11.0 * 5 = 55.0 + builder.addText('dolor'); + })..layout(constrain(double.infinity)); + + expect(paragraph.maxIntrinsicWidth, 205.0); + expect(paragraph.minIntrinsicWidth, 65.0); // "ipsum" + expect(paragraph.width, double.infinity); + expectLines(paragraph, [ + l('Lorem ipsum dolor', 0, 17, hardBreak: true, width: 205.0, left: 0.0), + ]); + }); + + test('breaks lines correctly at the end of spans', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(fontSize: 15.0)); + builder.addText('sit '); + builder.pop(); + builder.addText('.'); + })..layout(constrain(60.0)); + + expect(paragraph.maxIntrinsicWidth, 130.0); + expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem" + expect(paragraph.width, 60.0); + expectLines(paragraph, [ + l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0), + l('sit ', 6, 10, hardBreak: false, width: 45.0, left: 0.0), + l('.', 10, 11, hardBreak: true, width: 10.0, left: 0.0), + ]); + }); + + test('breaks lines correctly in the middle of spans', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.addText('Lorem ipsum '); + builder.pushStyle(EngineTextStyle.only(fontSize: 11.0)); + builder.addText('sit dolor'); + })..layout(constrain(100.0)); + + expect(paragraph.maxIntrinsicWidth, 219.0); + expect(paragraph.minIntrinsicWidth, 55.0); // "dolor" + expect(paragraph.width, 100.0); + expectLines(paragraph, [ + l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0), + l('ipsum sit ', 6, 16, hardBreak: false, width: 93.0, left: 0.0), + l('dolor', 16, 21, hardBreak: true, width: 55.0, left: 0.0), + ]); + }); + + test('handles space-only spans', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('Lorem '); + builder.pop(); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText(' '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText(' '); + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('ipsum'); + }); + paragraph.layout(constrain(80.0)); + + expect(paragraph.maxIntrinsicWidth, 160.0); + expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem" or "ipsum" + expect(paragraph.width, 80.0); + expectLines(paragraph, [ + l('Lorem ', 0, 11, hardBreak: false, width: 50.0, widthWithTrailingSpaces: 110.0, left: 0.0), + l('ipsum', 11, 16, hardBreak: true, width: 50.0, left: 0.0), + ]); + }); + + test('should not break at span end if it is not a line break', () { + final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('Lorem'); + builder.pop(); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText(' '); + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('ip'); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('su'); + builder.pushStyle(EngineTextStyle.only(color: white)); + builder.addText('m'); + })..layout(constrain(50.0)); + + expect(paragraph.maxIntrinsicWidth, 110.0); + expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem" or "ipsum" + expect(paragraph.width, 50.0); + expectLines(paragraph, [ + l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0), + l('ipsum', 6, 11, hardBreak: true, width: 50.0, left: 0.0), + ]); + }); + + test('should handle placeholder-only paragraphs', () { + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + textAlign: ui.TextAlign.center, + ); + final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { + builder.addPlaceholder(300.0, 50.0, ui.PlaceholderAlignment.baseline, baseline: ui.TextBaseline.alphabetic); + })..layout(constrain(500.0)); + + expect(paragraph.maxIntrinsicWidth, 300.0); + expect(paragraph.minIntrinsicWidth, 300.0); + expect(paragraph.height, 50.0); + expectLines(paragraph, [ + l('', 0, 0, hardBreak: true, width: 300.0, left: 100.0), + ]); + }); + + test('correct maxIntrinsicWidth when paragraph ends with placeholder', () { + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + textAlign: ui.TextAlign.center, + ); + final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { + builder.addText('abcd'); + builder.addPlaceholder(300.0, 50.0, ui.PlaceholderAlignment.bottom); + })..layout(constrain(400.0)); + + expect(paragraph.maxIntrinsicWidth, 340.0); + expect(paragraph.minIntrinsicWidth, 300.0); + expect(paragraph.height, 50.0); + expectLines(paragraph, [ + l('abcd', 0, 4, hardBreak: true, width: 340.0, left: 30.0), + ]); + }); + + test('handles new line followed by a placeholder', () { + final EngineParagraphStyle paragraphStyle = EngineParagraphStyle( + fontFamily: 'ahem', + fontSize: 10, + textAlign: ui.TextAlign.center, + ); + final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { + builder.addText('Lorem\n'); + builder.addPlaceholder(300.0, 40.0, ui.PlaceholderAlignment.bottom); + builder.addText('ipsum'); + })..layout(constrain(300.0)); + + // The placeholder's width + "ipsum" + expect(paragraph.maxIntrinsicWidth, 300.0 + 50.0); + expect(paragraph.minIntrinsicWidth, 300.0); + expect(paragraph.height, 10.0 + 40.0 + 10.0); + expectLines(paragraph, [ + l('Lorem', 0, 6, hardBreak: true, width: 50.0, height: 10.0, left: 125.0), + l('', 6, 6, hardBreak: false, width: 300.0, height: 40.0, left: 0.0), + l('ipsum', 6, 11, hardBreak: true, width: 50.0, height: 10.0, left: 125.0), + ]); + }); +} diff --git a/lib/web_ui/test/text/line_breaker_test.dart b/lib/web_ui/test/text/line_breaker_test.dart index 8dd5f65bb113f..8545b8cab6662 100644 --- a/lib/web_ui/test/text/line_breaker_test.dart +++ b/lib/web_ui/test/text/line_breaker_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -171,7 +170,7 @@ void testMain() { test('trailing spaces and new lines', () { expect( findBreaks('foo bar '), - [ + const [ LineBreakResult(4, 4, 3, LineBreakType.opportunity), LineBreakResult(9, 9, 7, LineBreakType.endOfText), ], @@ -179,7 +178,7 @@ void testMain() { expect( findBreaks('foo \nbar\nbaz \n'), - [ + const [ LineBreakResult(6, 5, 3, LineBreakType.mandatory), LineBreakResult(10, 9, 9, LineBreakType.mandatory), LineBreakResult(17, 16, 13, LineBreakType.mandatory), @@ -191,7 +190,7 @@ void testMain() { test('leading spaces', () { expect( findBreaks(' foo'), - [ + const [ LineBreakResult(1, 1, 0, LineBreakType.opportunity), LineBreakResult(4, 4, 4, LineBreakType.endOfText), ], @@ -199,7 +198,7 @@ void testMain() { expect( findBreaks(' foo'), - [ + const [ LineBreakResult(3, 3, 0, LineBreakType.opportunity), LineBreakResult(6, 6, 6, LineBreakType.endOfText), ], @@ -207,7 +206,7 @@ void testMain() { expect( findBreaks(' foo bar'), - [ + const [ LineBreakResult(2, 2, 0, LineBreakType.opportunity), LineBreakResult(8, 8, 5, LineBreakType.opportunity), LineBreakResult(11, 11, 11, LineBreakType.endOfText), @@ -216,7 +215,7 @@ void testMain() { expect( findBreaks(' \n foo'), - [ + const [ LineBreakResult(3, 2, 0, LineBreakType.mandatory), LineBreakResult(6, 6, 3, LineBreakType.opportunity), LineBreakResult(9, 9, 9, LineBreakType.endOfText), @@ -252,6 +251,14 @@ void testMain() { '"$text"\n' '\nExpected line break at {$lastLineBreak - $i} but found line break at {$lastLineBreak - ${result.index}}.', ); + + // Since this is a line break, passing a `maxEnd` that's greater + // should return the same line break. + final LineBreakResult maxEndResult = + nextLineBreak(text, lastLineBreak, maxEnd: i + 1); + expect(maxEndResult.index, i); + expect(maxEndResult.type, isNot(LineBreakType.prohibited)); + lastLineBreak = i; } else { // This isn't a line break opportunity so the line break should be @@ -264,6 +271,13 @@ void testMain() { '"$text"\n' '\nUnexpected line break found at {$lastLineBreak - $i}.', ); + + // Since this isn't a line break, passing it as a `maxEnd` should + // return `maxEnd` as a prohibited line break type. + final LineBreakResult maxEndResult = + nextLineBreak(text, lastLineBreak, maxEnd: i); + expect(maxEndResult.index, i); + expect(maxEndResult.type, LineBreakType.prohibited); } } } @@ -290,9 +304,9 @@ class Line { final String bk = String.fromCharCode(0x000B); final String nl = String.fromCharCode(0x0085); return text - .replaceAll('"', '\\"') - .replaceAll('\n', '\\n') - .replaceAll('\r', '\\r') + .replaceAll('"', r'\"') + .replaceAll('\n', r'\n') + .replaceAll('\r', r'\r') .replaceAll(bk, '{BK}') .replaceAll(nl, '{NL}'); } @@ -307,7 +321,7 @@ List split(String text) { final List lines = []; int lastIndex = 0; - for (LineBreakResult brk in findBreaks(text)) { + for (final LineBreakResult brk in findBreaks(text)) { lines.add(Line(text.substring(lastIndex, brk.index), brk.type)); lastIndex = brk.index; } diff --git a/lib/web_ui/test/text/line_breaker_test_helper.dart b/lib/web_ui/test/text/line_breaker_test_helper.dart index 07a4229d23837..7af7122050609 100644 --- a/lib/web_ui/test/text/line_breaker_test_helper.dart +++ b/lib/web_ui/test/text/line_breaker_test_helper.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 // The following test cases contradict rule LB25, so we are replacing them // with correct expectations. @@ -186,6 +185,7 @@ class TestCase { return String.fromCharCodes(charCodes); } + @override String toString() { return raw; } diff --git a/lib/web_ui/test/text/line_breaker_test_raw_data.dart b/lib/web_ui/test/text/line_breaker_test_raw_data.dart index 13b3e1e791f9a..c0c667d03ac9b 100644 --- a/lib/web_ui/test/text/line_breaker_test_raw_data.dart +++ b/lib/web_ui/test/text/line_breaker_test_raw_data.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.10 /// The list of test cases from: /// - https://www.unicode.org/Public/13.0.0/ucd/auxiliary/LineBreakTest.txt diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart deleted file mode 100644 index ca4258c89cc77..0000000000000 --- a/lib/web_ui/test/text/measurement_test.dart +++ /dev/null @@ -1,1167 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.6 -@TestOn('chrome || firefox') - -import 'package:test/bootstrap/browser.dart'; -import 'package:test/test.dart'; - -import 'package:ui/ui.dart' as ui; -import 'package:ui/src/engine.dart'; - - -final ui.ParagraphStyle ahemStyle = ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, -); -const ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 50); -const ui.ParagraphConstraints infiniteConstraints = - ui.ParagraphConstraints(width: double.infinity); - -ui.Paragraph build(ui.ParagraphStyle style, String text, - {ui.TextStyle textStyle}) { - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(style); - if (textStyle != null) { - builder.pushStyle(textStyle); - } - builder.addText(text); - return builder.build(); -} - -typedef MeasurementTestBody = void Function(TextMeasurementService instance); - -/// Runs the same test twice - once with dom measurement and once with canvas -/// measurement. -void testMeasurements(String description, MeasurementTestBody body, { - bool skipDom, - bool skipCanvas, -}) { - test( - '$description (dom)', - () => body(TextMeasurementService.domInstance), - skip: skipDom, - ); - test( - '$description (canvas)', - () => body(TextMeasurementService.canvasInstance), - skip: skipCanvas, - ); -} -void main() { - internalBootstrapBrowserTest(() => testMain); -} - -void testMain() async { - await ui.webOnlyInitializeTestDomRenderer(); - - group('$RulerManager', () { - final ui.ParagraphStyle s1 = ui.ParagraphStyle(fontFamily: 'sans-serif'); - final ui.ParagraphStyle s2 = ui.ParagraphStyle( - fontWeight: ui.FontWeight.bold, - ); - final ui.ParagraphStyle s3 = ui.ParagraphStyle(fontSize: 22.0); - - ParagraphGeometricStyle style1, style2, style3; - EngineParagraph style1Text1, style1Text2; // two paragraphs sharing style - EngineParagraph style2Text1, style3Text3; - - setUp(() { - style1Text1 = build(s1, '1'); - style1Text2 = build(s1, '2'); - style2Text1 = build(s2, '1'); - style3Text3 = build(s3, '3'); - - style1 = style1Text1.geometricStyle; - style2 = style2Text1.geometricStyle; - style3 = style3Text3.geometricStyle; - - final ParagraphGeometricStyle style1_2 = style1Text2.geometricStyle; - expect(style1_2, style1); // styles must be equal despite different text - }); - - test('caches rulers', () { - final RulerManager rulerManager = RulerManager(rulerCacheCapacity: 2); - ParagraphRuler ruler1, ruler2, ruler3; - - expect(rulerManager.rulerCacheCapacity, 2); - expect(rulerManager.rulers.length, 0); - - // First ruler cached - ruler1 = rulerManager.findOrCreateRuler(style1); - expect(rulerManager.rulers.length, 1); - expect(ruler1.hitCount, 1); - - // Increase hit count for style 1 - ruler1 = rulerManager.findOrCreateRuler(style1); - expect(rulerManager.rulers.length, 1); - expect(ruler1.hitCount, 2); - - // Previous ruler reused - rulerManager.findOrCreateRuler(style1); - expect(rulerManager.rulers.length, 1); - expect(ruler1.hitCount, 3); - - // Second ruler created and cached - ruler2 = rulerManager.findOrCreateRuler(style2); - expect(rulerManager.rulers.length, 2); - expect(ruler1.hitCount, 3); - expect(ruler2.hitCount, 1); - - // Increase hit count for style 2 - rulerManager.findOrCreateRuler(style2); - rulerManager.findOrCreateRuler(style2); - rulerManager.findOrCreateRuler(style2); - expect(rulerManager.rulers.length, 2); - expect(ruler2.hitCount, 4); - - // Third ruler cached: it is ok to store more rulers that the cache - // capacity because the cache is cleaned-up at the next microtask. - ruler3 = rulerManager.findOrCreateRuler(style3); - - // Final ruler states - expect(rulerManager.rulers.length, 3); - expect(ruler1.hitCount, 3); - expect(ruler2.hitCount, 4); - expect(ruler3.hitCount, 1); - // The least hit ruler isn't disposed yet. - expect(ruler3.debugIsDisposed, isFalse); - - // Cleaning up the cache should bring its size down to capacity limit. - rulerManager.cleanUpRulerCache(); - expect(rulerManager.rulers.length, 2); - expect(rulerManager.rulers, containsValue(ruler1)); // retained - expect(rulerManager.rulers, containsValue(ruler2)); // retained - expect(rulerManager.rulers, isNot(containsValue(ruler3))); // evicted - expect(ruler1.debugIsDisposed, isFalse); - expect(ruler2.debugIsDisposed, isFalse); - expect(ruler3.debugIsDisposed, isTrue); - - ruler1 = rulerManager.rulers[style1]; - expect(ruler1.style, style1); - expect(ruler1.hitCount, 0); // hit counts are reset - - ruler2 = rulerManager.rulers[style2]; - expect(ruler2.style, style2); - expect(ruler2.hitCount, 0); // hit counts are reset - }); - }); - - group('$TextMeasurementService', () { - setUp(() { - TextMeasurementService.initialize(rulerCacheCapacity: 2); - }); - tearDown(() { - TextMeasurementService.clearCache(); - }); - - testMeasurements( - 'preserves whitespace when measuring', - (TextMeasurementService instance) { - ui.Paragraph text; - MeasurementResult result; - - // leading whitespaces - text = build(ahemStyle, ' abc'); - result = instance.measure(text, infiniteConstraints); - expect(result.maxIntrinsicWidth, 60); - expect(result.minIntrinsicWidth, 30); - expect(result.height, 10); - expect(result.lines, [ - line(' abc', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0), - ]); - - // trailing whitespaces - text = build(ahemStyle, 'abc '); - result = instance.measure(text, infiniteConstraints); - expect(result.maxIntrinsicWidth, 60); - expect(result.minIntrinsicWidth, 30); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc ', 0, 6, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement always includes trailing whitespace in the - // width, while Flutter and Canvas-based measurement don't. - expect(result.lines, [ - line('abc ', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0), - ]); - } - - // mixed whitespaces - text = build(ahemStyle, ' ab c '); - result = instance.measure(text, infiniteConstraints); - expect(result.maxIntrinsicWidth, 100); - expect(result.minIntrinsicWidth, 20); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line(' ab c ', 0, 10, hardBreak: true, width: 80.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement always includes trailing whitespace in the - // width, while Flutter and Canvas-based measurement don't. - expect(result.lines, [ - line(' ab c ', 0, 10, hardBreak: true, width: 100.0, lineNumber: 0, left: 0.0), - ]); - } - - // single whitespace - text = build(ahemStyle, ' '); - result = instance.measure(text, infiniteConstraints); - expect(result.maxIntrinsicWidth, 10); - expect(result.minIntrinsicWidth, 0); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line(' ', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement always includes trailing whitespace in the - // width, while Flutter and Canvas-based measurement don't. - expect(result.lines, [ - line(' ', 0, 1, hardBreak: true, width: 10.0, lineNumber: 0, left: 0.0), - ]); - } - - // whitespace only - text = build(ahemStyle, ' '); - result = instance.measure(text, infiniteConstraints); - expect(result.maxIntrinsicWidth, 50); - expect(result.minIntrinsicWidth, 0); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line(' ', 0, 5, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement always includes trailing whitespace in the - // width, while Flutter and Canvas-based measurement don't. - expect(result.lines, [ - line(' ', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0), - ]); - } - }, - ); - - testMeasurements( - 'uses single-line when text can fit without wrapping', - (TextMeasurementService instance) { - final MeasurementResult result = - instance.measure(build(ahemStyle, '12345'), constraints); - - // Should fit on a single line. - expect(result.isSingleLine, true); - expect(result.maxIntrinsicWidth, 50); - expect(result.minIntrinsicWidth, 50); - expect(result.width, 50); - expect(result.height, 10); - expect(result.lines, [ - line('12345', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0), - ]); - }, - ); - - testMeasurements( - 'simple multi-line text', - (TextMeasurementService instance) { - const ui.ParagraphConstraints constraints = - ui.ParagraphConstraints(width: 70); - MeasurementResult result; - - // The long text doesn't fit in 70px of width, so it needs to wrap. - result = instance.measure(build(ahemStyle, 'foo bar baz'), constraints); - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 110); - expect(result.minIntrinsicWidth, 30); - expect(result.width, 70); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('foo bar ', 0, 8, hardBreak: false, width: 70.0, lineNumber: 0, left: 0.0), - line('baz', 8, 11, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }, - ); - - testMeasurements( - 'uses multi-line for long text', - (TextMeasurementService instance) { - MeasurementResult result; - - // The long text doesn't fit in 50px of width, so it needs to wrap. - result = instance.measure(build(ahemStyle, '1234567890'), constraints); - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 100); - expect(result.minIntrinsicWidth, 100); - expect(result.width, 50); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('12345', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - line('67890', 5, 10, hardBreak: true, width: 50.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // The first word is force-broken twice. - result = - instance.measure(build(ahemStyle, 'abcdefghijk lm'), constraints); - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 140); - expect(result.minIntrinsicWidth, 110); - expect(result.width, 50); - expect(result.height, 30); - if (instance.isCanvas) { - expect(result.lines, [ - line('abcde', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - line('fghij', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('k lm', 10, 14, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Constraints aren't enough even for a single character. In this case, - // we show a minimum of one character per line. - const ui.ParagraphConstraints narrowConstraints = - ui.ParagraphConstraints(width: 8); - result = instance.measure(build(ahemStyle, 'AA'), narrowConstraints); - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 20); - expect(result.minIntrinsicWidth, 20); - expect(result.width, 8); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), - line('A', 1, 2, hardBreak: true, width: 10.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Extremely narrow constraints with new line in the middle. - result = instance.measure(build(ahemStyle, 'AA\nA'), narrowConstraints); - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 20); - expect(result.minIntrinsicWidth, 20); - expect(result.width, 8); - expect(result.height, 30); - if (instance.isCanvas) { - expect(result.lines, [ - line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), - line('A', 1, 3, hardBreak: true, width: 10.0, lineNumber: 1, left: 0.0), - line('A', 3, 4, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Extremely narrow constraints with new line in the end. - result = instance.measure(build(ahemStyle, 'AAA\n'), narrowConstraints); - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 30); - expect(result.minIntrinsicWidth, 30); - expect(result.width, 8); - expect(result.height, 40); - if (instance.isCanvas) { - expect(result.lines, [ - line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), - line('A', 1, 2, hardBreak: false, width: 10.0, lineNumber: 1, left: 0.0), - line('A', 2, 4, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), - line('', 4, 4, hardBreak: true, width: 0.0, lineNumber: 3, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }, - skipDom: browserEngine == BrowserEngine.webkit, - ); - - testMeasurements( - 'uses multi-line for text that contains new-line', - (TextMeasurementService instance) { - final MeasurementResult result = - instance.measure(build(ahemStyle, '12\n34'), constraints); - - // Text containing newlines should always be drawn in multi-line mode. - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 20); - expect(result.minIntrinsicWidth, 20); - expect(result.width, 50); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0, left: 0.0), - line('34', 3, 5, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }, - ); - - testMeasurements('empty lines', (TextMeasurementService instance) { - MeasurementResult result; - - // Empty lines in the beginning. - result = instance.measure(build(ahemStyle, '\n\n1234'), constraints); - expect(result.maxIntrinsicWidth, 40); - expect(result.minIntrinsicWidth, 40); - expect(result.height, 30); - if (instance.isCanvas) { - expect(result.lines, [ - line('', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0), - line('', 1, 2, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0), - line('1234', 2, 6, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Empty lines in the middle. - result = instance.measure(build(ahemStyle, '12\n\n345'), constraints); - expect(result.maxIntrinsicWidth, 30); - expect(result.minIntrinsicWidth, 30); - expect(result.height, 30); - if (instance.isCanvas) { - expect(result.lines, [ - line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0, left: 0.0), - line('', 3, 4, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0), - line('345', 4, 7, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Empty lines in the end. - result = instance.measure(build(ahemStyle, '1234\n\n'), constraints); - expect(result.maxIntrinsicWidth, 40); - expect(result.minIntrinsicWidth, 40); - if (instance.isCanvas) { - // This can only be done correctly in the canvas-based implementation. - expect(result.height, 30); - expect(result.lines, [ - line('1234', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0), - line('', 5, 6, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0), - line('', 6, 6, hardBreak: true, width: 0.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }); - - testMeasurements( - 'wraps multi-line text correctly when constraint width is infinite', - (TextMeasurementService instance) { - final EngineParagraph paragraph = build(ahemStyle, '123\n456 789'); - final MeasurementResult result = - instance.measure(paragraph, infiniteConstraints); - - expect(result.isSingleLine, false); - expect(result.maxIntrinsicWidth, 70); - expect(result.minIntrinsicWidth, 30); - expect(result.width, double.infinity); - expect(result.height, 20); - - if (instance.isCanvas) { - expect(result.lines, [ - line('123', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('456 789', 4, 11, hardBreak: true, width: 70.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }, - ); - - testMeasurements( - 'takes letter spacing into account', - (TextMeasurementService instance) { - const ui.ParagraphConstraints constraints = - ui.ParagraphConstraints(width: 100); - final ui.TextStyle spacedTextStyle = ui.TextStyle(letterSpacing: 3); - final ui.Paragraph spacedText = - build(ahemStyle, 'abc', textStyle: spacedTextStyle); - - final MeasurementResult spacedResult = - instance.measure(spacedText, constraints); - - expect(spacedResult.minIntrinsicWidth, 39); - expect(spacedResult.maxIntrinsicWidth, 39); - }, - ); - - test('takes word spacing into account', () { - const ui.ParagraphConstraints constraints = - ui.ParagraphConstraints(width: 100); - - final ui.ParagraphBuilder normalBuilder = ui.ParagraphBuilder(ahemStyle); - normalBuilder.addText('a b c'); - final ui.Paragraph normalText = normalBuilder.build(); - - final ui.ParagraphBuilder spacedBuilder = ui.ParagraphBuilder(ahemStyle); - spacedBuilder.pushStyle(ui.TextStyle(wordSpacing: 1.5)); - spacedBuilder.addText('a b c'); - final ui.Paragraph spacedText = spacedBuilder.build(); - - // Word spacing is only supported via DOM measurement. - final TextMeasurementService instance = - TextMeasurementService.forParagraph(spacedText); - expect(instance, const TypeMatcher()); - - final MeasurementResult normalResult = - instance.measure(normalText, constraints); - final MeasurementResult spacedResult = - instance.measure(spacedText, constraints); - - expect( - normalResult.maxIntrinsicWidth < spacedResult.maxIntrinsicWidth, - isTrue, - ); - }); - - testMeasurements('minIntrinsicWidth', (TextMeasurementService instance) { - MeasurementResult result; - - // Simple case. - result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); - expect(result.minIntrinsicWidth, 40); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), - line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1, left: 0.0), - line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With new lines. - result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); - expect(result.minIntrinsicWidth, 40); - if (instance.isCanvas) { - expect(result.lines, [ - line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0), - line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0), - line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With trailing whitespace. - result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); - expect(result.minIntrinsicWidth, 40); - if (instance.isCanvas) { - expect(result.lines, [ - line('abcd ', 0, 10, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0), - line('efg', 10, 13, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With trailing whitespace and new lines. - result = instance.measure(build(ahemStyle, 'abc \ndefg'), constraints); - expect(result.minIntrinsicWidth, 40); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc ', 0, 8, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('defg', 8, 12, hardBreak: true, width: 40.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Very long text. - result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); - expect(result.minIntrinsicWidth, 120); - if (instance.isCanvas) { - expect(result.lines, [ - line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }); - - testMeasurements('maxIntrinsicWidth', (TextMeasurementService instance) { - MeasurementResult result; - - // Simple case. - result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); - expect(result.maxIntrinsicWidth, 110); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), - line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1, left: 0.0), - line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With new lines. - result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); - expect(result.maxIntrinsicWidth, 40); - if (instance.isCanvas) { - expect(result.lines, [ - line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0), - line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0), - line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With long whitespace. - result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); - expect(result.maxIntrinsicWidth, 100); - if (instance.isCanvas) { - expect(result.lines, [ - line('abcd ', 0, 7, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0), - line('efg', 7, 10, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With trailing whitespace. - result = instance.measure(build(ahemStyle, 'abc def '), constraints); - expect(result.maxIntrinsicWidth, 100); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), - line('def ', 4, 10, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // With trailing whitespace and new lines. - result = instance.measure(build(ahemStyle, 'abc \ndef '), constraints); - expect(result.maxIntrinsicWidth, 60); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc ', 0, 5, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('def ', 5, 11, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Very long text. - result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); - expect(result.maxIntrinsicWidth, 120); - if (instance.isCanvas) { - expect(result.lines, [ - line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }); - - testMeasurements( - 'respects text overflow', - (TextMeasurementService instance) { - final ui.ParagraphStyle overflowStyle = ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, - ellipsis: '...', - ); - - MeasurementResult result; - - // The text shouldn't be broken into multiple lines, so the height should - // be equal to a height of a single line. - final ui.Paragraph longText = build( - overflowStyle, - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - ); - result = instance.measure(longText, constraints); - expect(result.minIntrinsicWidth, 480); - expect(result.maxIntrinsicWidth, 480); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line('AA...', 0, 48, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement can't handle the ellipsis case very well. The - // text wraps into multiple lines instead. - expect(result.lines, isNull); - } - - // The short prefix should make the text break into two lines, but the - // second line should remain unbroken. - final ui.Paragraph longTextShortPrefix = build( - overflowStyle, - 'AAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - ); - result = instance.measure(longTextShortPrefix, constraints); - expect(result.minIntrinsicWidth, 450); - expect(result.maxIntrinsicWidth, 450); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('AAA', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('AA...', 4, 49, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't handle the ellipsis case very well. The - // text wraps into multiple lines instead. - expect(result.lines, isNull); - } - - // Tiny constraints. - const ui.ParagraphConstraints tinyConstraints = - ui.ParagraphConstraints(width: 30); - final ui.Paragraph text = build(overflowStyle, 'AAAA'); - result = instance.measure(text, tinyConstraints); - expect(result.minIntrinsicWidth, 40); - expect(result.maxIntrinsicWidth, 40); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement can't handle the ellipsis case very well. The - // text wraps into multiple lines instead. - expect(result.lines, isNull); - } - - // Tinier constraints (not enough for the ellipsis). - const ui.ParagraphConstraints tinierConstraints = - ui.ParagraphConstraints(width: 10); - result = instance.measure(text, tinierConstraints); - expect(result.minIntrinsicWidth, 40); - expect(result.maxIntrinsicWidth, 40); - expect(result.height, 10); - if (instance.isCanvas) { - // TODO(flutter_web): https://github.com/flutter/flutter/issues/34346 - // expect(result.lines, [ - // line('.', 0, 4, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), - // ]); - expect(result.lines, [ - line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement can't handle the ellipsis case very well. The - // text wraps into multiple lines instead. - expect(result.lines, isNull); - } - }, - skipDom: browserEngine == BrowserEngine.webkit, - ); - - testMeasurements('respects max lines', (TextMeasurementService instance) { - final ui.ParagraphStyle maxlinesStyle = ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, - maxLines: 2, - ); - - MeasurementResult result; - - // The height should be that of a single line. - final ui.Paragraph oneline = build(maxlinesStyle, 'One line'); - result = instance.measure(oneline, infiniteConstraints); - expect(result.height, 10); - expect(result.lines, [ - line('One line', 0, 8, hardBreak: true, width: 80.0, lineNumber: 0, left: 0.0), - ]); - - // The height should respect max lines and be limited to two lines here. - final ui.Paragraph threelines = - build(maxlinesStyle, 'First\nSecond\nThird'); - result = instance.measure(threelines, infiniteConstraints); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('First', 0, 6, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0), - line('Second', 6, 13, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // The height should respect max lines and be limited to two lines here. - final ui.Paragraph veryLong = build( - maxlinesStyle, - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - ); - result = instance.measure(veryLong, constraints); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('Lorem ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - line('ipsum ', 6, 12, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Case when last line is a long unbreakable word. - final ui.Paragraph veryLongLastLine = build( - maxlinesStyle, - 'AAA AAAAAAAAAAAAAAAAAAA', - ); - result = instance.measure(veryLongLastLine, constraints); - expect(result.height, 20); - if (instance.isCanvas) { - expect(result.lines, [ - line('AAA ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), - line('AAAAA', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }); - - testMeasurements( - 'respects text overflow and max lines combined', - (TextMeasurementService instance) { - const ui.ParagraphConstraints constraints = - ui.ParagraphConstraints(width: 60); - final ui.ParagraphStyle onelineStyle = ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, - maxLines: 1, - ellipsis: '...', - ); - final ui.ParagraphStyle multilineStyle = ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, - maxLines: 2, - ellipsis: '...', - ); - - ui.Paragraph p; - MeasurementResult result; - - // Simple no overflow case. - p = build(onelineStyle, 'abcdef'); - result = instance.measure(p, constraints); - expect(result.height, 10); - expect(result.lines, [ - line('abcdef', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0), - ]); - - // Simple overflow case. - p = build(onelineStyle, 'abcd efg'); - result = instance.measure(p, constraints); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line('abc...', 0, 8, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement can't handle the ellipsis case very well. The - // text wraps into multiple lines instead. - expect(result.lines, isNull); - } - - // Another simple overflow case. - p = build(onelineStyle, 'a bcde fgh'); - result = instance.measure(p, constraints); - expect(result.height, 10); - if (instance.isCanvas) { - expect(result.lines, [ - line('a b...', 0, 10, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), - ]); - } else { - // DOM-based measurement can't handle the ellipsis case very well. The - // text wraps into multiple lines instead. - expect(result.lines, isNull); - } - - // The ellipsis is supposed to go on the second line, but because the - // 2nd line doesn't overflow, no ellipsis is shown. - p = build(multilineStyle, 'abcdef ghijkl'); - result = instance.measure(p, constraints); - // This can only be done correctly in the canvas-based implementation. - if (instance.isCanvas) { - expect(result.height, 20); - - expect(result.lines, [ - line('abcdef ', 0, 7, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), - line('ghijkl', 7, 13, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // But when the 2nd line is long enough, the ellipsis is shown. - p = build(multilineStyle, 'abcd efghijkl'); - result = instance.measure(p, constraints); - // This can only be done correctly in the canvas-based implementation. - if (instance.isCanvas) { - expect(result.height, 20); - - expect(result.lines, [ - line('abcd ', 0, 5, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0), - line('efg...', 5, 13, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Even if the second line can be broken, we don't break it, we just - // insert the ellipsis. - p = build(multilineStyle, 'abcde f gh ijk'); - result = instance.measure(p, constraints); - // This can only be done correctly in the canvas-based implementation. - if (instance.isCanvas) { - expect(result.height, 20); - - expect(result.lines, [ - line('abcde ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), - line('f g...', 6, 14, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // First line overflows but second line doesn't. - p = build(multilineStyle, 'abcdefg hijk'); - result = instance.measure(p, constraints); - // This can only be done correctly in the canvas-based implementation. - if (instance.isCanvas) { - expect(result.height, 20); - - expect(result.lines, [ - line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), - line('g hijk', 6, 12, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - - // Both first and second lines overflow. - p = build(multilineStyle, 'abcdefg hijklmnop'); - result = instance.measure(p, constraints); - // This can only be done correctly in the canvas-based implementation. - if (instance.isCanvas) { - expect(result.height, 20); - - expect(result.lines, [ - line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), - line('g h...', 6, 17, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0), - ]); - } else { - // DOM-based measurement can't produce line metrics for multi-line - // paragraphs. - expect(result.lines, isNull); - } - }, - ); - - test('handles textAlign', () { - TextMeasurementService instance = TextMeasurementService.canvasInstance; - ui.Paragraph p; - MeasurementResult result; - - ui.ParagraphStyle createStyle(ui.TextAlign textAlign) { - return ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, - textAlign: textAlign, - textDirection: ui.TextDirection.ltr, - ); - } - - p = build(createStyle(ui.TextAlign.start), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), - ]); - - p = build(createStyle(ui.TextAlign.end), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), - ]); - - p = build(createStyle(ui.TextAlign.center), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 10.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 20.0), - ]); - - p = build(createStyle(ui.TextAlign.left), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), - ]); - - p = build(createStyle(ui.TextAlign.right), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), - ]); - }); - - testMeasurements( - 'handles rtl with textAlign', - (TextMeasurementService instance) { - TextMeasurementService instance = TextMeasurementService.canvasInstance; - ui.Paragraph p; - MeasurementResult result; - - ui.ParagraphStyle createStyle(ui.TextAlign textAlign) { - return ui.ParagraphStyle( - fontFamily: 'ahem', - fontSize: 10, - textAlign: textAlign, - textDirection: ui.TextDirection.rtl, - ); - } - - p = build(createStyle(ui.TextAlign.start), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), - ]); - - p = build(createStyle(ui.TextAlign.end), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), - ]); - - p = build(createStyle(ui.TextAlign.center), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 10.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 20.0), - ]); - - p = build(createStyle(ui.TextAlign.left), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), - ]); - - p = build(createStyle(ui.TextAlign.right), 'abc\ndefghi'); - result = instance.measure(p, constraints); - expect(result.lines, [ - line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), - line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), - line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), - ]); - }, - ); - }); -} - -/// Shortcut to avoid many line wraps in the tests above. -EngineLineMetrics line( - String displayText, - int startIndex, - int endIndex, { - double width, - int lineNumber, - bool hardBreak, - double left, -}) { - return EngineLineMetrics.withText( - displayText, - startIndex: startIndex, - endIndex: endIndex, - hardBreak: hardBreak, - width: width, - lineNumber: lineNumber, - left: left, - endIndexWithoutNewlines: -1, - widthWithTrailingSpaces: width, - ); -} diff --git a/lib/web_ui/test/text/text_direction_test.dart b/lib/web_ui/test/text/text_direction_test.dart new file mode 100644 index 0000000000000..61b7b44e4df00 --- /dev/null +++ b/lib/web_ui/test/text/text_direction_test.dart @@ -0,0 +1,126 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +// Two RTL strings, 5 characters each, to match the length of "$rtl1" and "$rtl2". +const String rtl1 = 'واحدة'; +const String rtl2 = 'ثنتان'; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +Future testMain() async { + group('$getDirectionalBlockEnd', () { + + test('basic cases', () { + const String text = 'Lorem 12 $rtl1 ipsum34'; + const LineBreakResult start = LineBreakResult.sameIndex(0, LineBreakType.prohibited); + const LineBreakResult end = LineBreakResult.sameIndex(text.length, LineBreakType.endOfText); + const LineBreakResult loremMiddle = LineBreakResult.sameIndex(3, LineBreakType.prohibited); + const LineBreakResult loremEnd = LineBreakResult.sameIndex(5, LineBreakType.prohibited); + const LineBreakResult twelveStart = LineBreakResult(6, 6, 5, LineBreakType.opportunity); + const LineBreakResult twelveEnd = LineBreakResult.sameIndex(8, LineBreakType.prohibited); + const LineBreakResult rtl1Start = LineBreakResult(9, 9, 8, LineBreakType.opportunity); + const LineBreakResult rtl1End = LineBreakResult.sameIndex(14, LineBreakType.prohibited); + const LineBreakResult ipsumStart = LineBreakResult(17, 17, 15, LineBreakType.opportunity); + const LineBreakResult ipsumEnd = LineBreakResult.sameIndex(22, LineBreakType.prohibited); + + DirectionalPosition blockEnd; + + blockEnd = getDirectionalBlockEnd(text, start, end); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.ltr); + expect(blockEnd.lineBreak, loremEnd); + + blockEnd = getDirectionalBlockEnd(text, start, loremMiddle); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.ltr); + expect(blockEnd.lineBreak, loremMiddle); + + blockEnd = getDirectionalBlockEnd(text, loremMiddle, loremEnd); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.ltr); + expect(blockEnd.lineBreak, loremEnd); + + blockEnd = getDirectionalBlockEnd(text, loremEnd, twelveStart); + expect(blockEnd.isSpaceOnly, isTrue); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, twelveStart); + + blockEnd = getDirectionalBlockEnd(text, twelveStart, rtl1Start); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, twelveEnd); + + blockEnd = getDirectionalBlockEnd(text, rtl1Start, end); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.rtl); + expect(blockEnd.lineBreak, rtl1End); + + blockEnd = getDirectionalBlockEnd(text, ipsumStart, end); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.ltr); + expect(blockEnd.lineBreak, ipsumEnd); + + blockEnd = getDirectionalBlockEnd(text, ipsumEnd, end); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, end); + }); + + test('handles new lines', () { + const String text = 'Lorem\n12\nipsum \n'; + const LineBreakResult start = LineBreakResult.sameIndex(0, LineBreakType.prohibited); + const LineBreakResult end = LineBreakResult( + text.length, + text.length - 1, + text.length - 3, + LineBreakType.mandatory, + ); + const LineBreakResult loremEnd = LineBreakResult.sameIndex(5, LineBreakType.prohibited); + const LineBreakResult twelveStart = LineBreakResult(6, 5, 5, LineBreakType.mandatory); + const LineBreakResult twelveEnd = LineBreakResult.sameIndex(8, LineBreakType.prohibited); + const LineBreakResult ipsumStart = LineBreakResult(9, 8, 8, LineBreakType.mandatory); + const LineBreakResult ipsumEnd = LineBreakResult.sameIndex(14, LineBreakType.prohibited); + + DirectionalPosition blockEnd; + + blockEnd = getDirectionalBlockEnd(text, start, twelveStart); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.ltr); + expect(blockEnd.lineBreak, twelveStart); + + blockEnd = getDirectionalBlockEnd(text, loremEnd, twelveStart); + expect(blockEnd.isSpaceOnly, isTrue); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, twelveStart); + + blockEnd = getDirectionalBlockEnd(text, twelveStart, ipsumStart); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, ipsumStart); + + blockEnd = getDirectionalBlockEnd(text, twelveEnd, ipsumStart); + expect(blockEnd.isSpaceOnly, isTrue); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, ipsumStart); + + blockEnd = getDirectionalBlockEnd(text, ipsumStart, end); + expect(blockEnd.isSpaceOnly, isFalse); + expect(blockEnd.textDirection, TextDirection.ltr); + expect(blockEnd.lineBreak, ipsumEnd); + + blockEnd = getDirectionalBlockEnd(text, ipsumEnd, end); + expect(blockEnd.isSpaceOnly, isTrue); + expect(blockEnd.textDirection, isNull); + expect(blockEnd.lineBreak, end); + }); + }); +} diff --git a/lib/web_ui/test/text/word_breaker_test.dart b/lib/web_ui/test/text/word_breaker_test.dart index e6d8a24c25976..4b37f85d25d11 100644 --- a/lib/web_ui/test/text/word_breaker_test.dart +++ b/lib/web_ui/test/text/word_breaker_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -76,7 +75,7 @@ void testMain() { expectWords("Students' grades", ['Students', "'", ' ', 'grades']); expectWords( 'Joe said: "I\'m here"', - ['Joe', ' ', 'said', ':', ' ', '"', "I\'m", ' ', 'here', '"'], + ['Joe', ' ', 'said', ':', ' ', '"', "I'm", ' ', 'here', '"'], ); }); @@ -141,7 +140,7 @@ void expectWords(String text, List expectedWords) { int strIndex = 0; // Forward word break lookup. - for (String word in expectedWords) { + for (final String word in expectedWords) { final int nextBreak = WordBreaker.nextBreakIndex(text, strIndex); expect( nextBreak, strIndex + word.length, @@ -152,7 +151,7 @@ void expectWords(String text, List expectedWords) { // Backward word break lookup. strIndex = text.length; - for (String word in expectedWords.reversed) { + for (final String word in expectedWords.reversed) { final int prevBreak = WordBreaker.prevBreakIndex(text, strIndex); expect( prevBreak, strIndex - word.length, diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index a5bc7fa21fe70..acc84cef5e1e4 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 import 'dart:async'; import 'dart:html'; import 'dart:js_util' as js_util; @@ -11,9 +10,15 @@ import 'dart:typed_data'; import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' hide window; +import 'package:ui/src/engine.dart' show domRenderer; +import 'package:ui/src/engine/browser_detection.dart'; +import 'package:ui/src/engine/services.dart'; +import 'package:ui/src/engine/text_editing/autofill_hint.dart'; +import 'package:ui/src/engine/text_editing/input_type.dart'; +import 'package:ui/src/engine/text_editing/text_editing.dart'; +import 'package:ui/src/engine/util.dart'; +import 'package:ui/src/engine/vector_math.dart'; -import 'matchers.dart'; import 'spy.dart'; /// The `keyCode` of the "Enter" key. @@ -22,11 +27,11 @@ const int _kReturnKeyCode = 13; const MethodCodec codec = JSONMethodCodec(); /// Add unit tests for [FirefoxTextEditingStrategy]. -/// TODO(nurhan): https://github.com/flutter/flutter/issues/46891 +// TODO(mdebbar): https://github.com/flutter/flutter/issues/46891 -DefaultTextEditingStrategy editingElement; -EditingState lastEditingState; -String lastInputAction; +DefaultTextEditingStrategy? editingStrategy; +EditingState? lastEditingState; +String? lastInputAction; final InputConfiguration singlelineConfig = InputConfiguration( inputType: EngineInputType.text, @@ -41,11 +46,11 @@ final InputConfiguration multilineConfig = InputConfiguration( final Map flutterMultilineConfig = createFlutterConfig('multiline'); -void trackEditingState(EditingState editingState) { +void trackEditingState(EditingState? editingState) { lastEditingState = editingState; } -void trackInputAction(String inputAction) { +void trackInputAction(String? inputAction) { lastInputAction = inputAction; } @@ -57,18 +62,21 @@ void testMain() { tearDown(() { lastEditingState = null; lastInputAction = null; - cleanTextEditingElement(); + cleanTextEditingStrategy(); cleanTestFlags(); clearBackUpDomElementIfExists(); }); group('$GloballyPositionedTextEditingStrategy', () { - HybridTextEditing testTextEditing; + late HybridTextEditing testTextEditing; setUp(() { testTextEditing = HybridTextEditing(); - editingElement = GloballyPositionedTextEditingStrategy(testTextEditing); - testTextEditing.useCustomEditableElement(editingElement); + editingStrategy = GloballyPositionedTextEditingStrategy(testTextEditing); + testTextEditing.debugTextEditingStrategyOverride = editingStrategy; + testTextEditing.configuration = singlelineConfig; + // Ensure the glass-pane and its shadow root exist. + domRenderer.reset(); }); test('Creates element when enabled and removes it when disabled', () { @@ -78,103 +86,116 @@ void testMain() { ); // The focus initially is on the body. expect(document.activeElement, document.body); + expect(defaultTextEditingRoot.activeElement, null); - editingElement.enable( + editingStrategy!.enable( singlelineConfig, onChange: trackEditingState, onAction: trackInputAction, ); + expect( - document.getElementsByTagName('input'), + defaultTextEditingRoot.querySelectorAll('input'), hasLength(1), ); - final InputElement input = document.getElementsByTagName('input')[0]; + final Element input = defaultTextEditingRoot.querySelector('input')!; // Now the editing element should have focus. - expect(document.activeElement, input); - expect(editingElement.domElement, input); + + expect(document.activeElement, domRenderer.glassPaneElement); + expect(defaultTextEditingRoot.activeElement, input); + + expect(editingStrategy!.domElement, input); expect(input.getAttribute('type'), null); - // Input is appended to the glass pane. - expect(domRenderer.glassPaneElement.contains(editingElement.domElement), - isTrue); + // Input is appended to the right point of the DOM. + expect(defaultTextEditingRoot.contains(editingStrategy!.domElement), isTrue); - editingElement.disable(); + editingStrategy!.disable(); expect( - document.getElementsByTagName('input'), + defaultTextEditingRoot.querySelectorAll('input'), hasLength(0), ); // The focus is back to the body. expect(document.activeElement, document.body); + expect(defaultTextEditingRoot.activeElement, null); }); test('Respects read-only config', () { - final InputConfiguration config = InputConfiguration(readOnly: true); - editingElement.enable( + final InputConfiguration config = InputConfiguration( + readOnly: true, + ); + editingStrategy!.enable( config, onChange: trackEditingState, onAction: trackInputAction, ); - expect(document.getElementsByTagName('input'), hasLength(1)); - final InputElement input = document.getElementsByTagName('input')[0]; - expect(editingElement.domElement, input); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); + final Element input = defaultTextEditingRoot.querySelector('input')!; + expect(editingStrategy!.domElement, input); expect(input.getAttribute('readonly'), 'readonly'); - editingElement.disable(); + editingStrategy!.disable(); }); test('Knows how to create password fields', () { - final InputConfiguration config = InputConfiguration(obscureText: true); - editingElement.enable( + final InputConfiguration config = InputConfiguration( + obscureText: true, + ); + editingStrategy!.enable( config, onChange: trackEditingState, onAction: trackInputAction, ); - expect(document.getElementsByTagName('input'), hasLength(1)); - final InputElement input = document.getElementsByTagName('input')[0]; - expect(editingElement.domElement, input); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); + final Element input = defaultTextEditingRoot.querySelector('input')!; + expect(editingStrategy!.domElement, input); expect(input.getAttribute('type'), 'password'); - editingElement.disable(); + editingStrategy!.disable(); }); test('Knows to turn autocorrect off', () { - final InputConfiguration config = InputConfiguration(autocorrect: false); - editingElement.enable( + final InputConfiguration config = InputConfiguration( + autocorrect: false, + ); + editingStrategy!.enable( config, onChange: trackEditingState, onAction: trackInputAction, ); - expect(document.getElementsByTagName('input'), hasLength(1)); - final InputElement input = document.getElementsByTagName('input')[0]; - expect(editingElement.domElement, input); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); + final Element input = defaultTextEditingRoot.querySelector('input')!; + expect(editingStrategy!.domElement, input); expect(input.getAttribute('autocorrect'), 'off'); - editingElement.disable(); + editingStrategy!.disable(); }); test('Knows to turn autocorrect on', () { - final InputConfiguration config = InputConfiguration(autocorrect: true); - editingElement.enable( + final InputConfiguration config = InputConfiguration( + autocorrect: true, + ); + editingStrategy!.enable( config, onChange: trackEditingState, onAction: trackInputAction, ); - expect(document.getElementsByTagName('input'), hasLength(1)); - final InputElement input = document.getElementsByTagName('input')[0]; - expect(editingElement.domElement, input); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); + final Element input = defaultTextEditingRoot.querySelector('input')!; + expect(editingStrategy!.domElement, input); expect(input.getAttribute('autocorrect'), 'on'); - editingElement.disable(); + editingStrategy!.disable(); }); test('Can read editing state correctly', () { - editingElement.enable( + editingStrategy!.enable( singlelineConfig, onChange: trackEditingState, onAction: trackInputAction, ); - final InputElement input = editingElement.domElement; + final InputElement input = editingStrategy!.domElement! as InputElement; input.value = 'foo bar'; input.dispatchEvent(Event.eventType('Event', 'input')); expect( @@ -194,15 +215,15 @@ void testMain() { }); test('Can set editing state correctly', () { - editingElement.enable( + editingStrategy!.enable( singlelineConfig, onChange: trackEditingState, onAction: trackInputAction, ); - editingElement.setEditingState( + editingStrategy!.setEditingState( EditingState(text: 'foo bar baz', baseOffset: 2, extentOffset: 7)); - checkInputEditingState(editingElement.domElement, 'foo bar baz', 2, 7); + checkInputEditingState(editingStrategy!.domElement!, 'foo bar baz', 2, 7); // There should be no input action. expect(lastInputAction, isNull); @@ -211,23 +232,23 @@ void testMain() { test('Multi-line mode also works', () { // The textarea element is created lazily. expect(document.getElementsByTagName('textarea'), hasLength(0)); - editingElement.enable( + editingStrategy!.enable( multilineConfig, onChange: trackEditingState, onAction: trackInputAction, ); - expect(document.getElementsByTagName('textarea'), hasLength(1)); + expect(defaultTextEditingRoot.querySelectorAll('textarea'), hasLength(1)); final TextAreaElement textarea = - document.getElementsByTagName('textarea')[0]; + defaultTextEditingRoot.querySelector('textarea')! as TextAreaElement; // Now the textarea should have focus. - expect(document.activeElement, textarea); - expect(editingElement.domElement, textarea); + expect(defaultTextEditingRoot.activeElement, textarea); + expect(editingStrategy!.domElement, textarea); textarea.value = 'foo\nbar'; textarea.dispatchEvent(Event.eventType('Event', 'input')); textarea.setSelectionRange(4, 6); - textarea.dispatchEvent(Event.eventType('Event', 'selectionchange')); + document.dispatchEvent(Event.eventType('Event', 'selectionchange')); // Can read textarea state correctly (and preserves new lines). expect( lastEditingState, @@ -235,15 +256,15 @@ void testMain() { ); // Can set textarea state correctly (and preserves new lines). - editingElement.setEditingState( + editingStrategy!.setEditingState( EditingState(text: 'bar\nbaz', baseOffset: 2, extentOffset: 7)); checkTextAreaEditingState(textarea, 'bar\nbaz', 2, 7); - editingElement.disable(); + editingStrategy!.disable(); // The textarea should be cleaned up. - expect(document.getElementsByTagName('textarea'), hasLength(0)); + expect(defaultTextEditingRoot.querySelectorAll('textarea'), hasLength(0)); // The focus is back to the body. - expect(document.activeElement, document.body); + expect(defaultTextEditingRoot.activeElement, null); // There should be no input action. expect(lastInputAction, isNull); @@ -255,41 +276,42 @@ void testMain() { expect(document.getElementsByTagName('textarea'), hasLength(0)); // Use single-line config and expect an `` to be created. - editingElement.enable( + editingStrategy!.enable( singlelineConfig, onChange: trackEditingState, onAction: trackInputAction, ); - expect(document.getElementsByTagName('input'), hasLength(1)); - expect(document.getElementsByTagName('textarea'), hasLength(0)); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); + expect(defaultTextEditingRoot.querySelectorAll('textarea'), hasLength(0)); // Disable and check that all DOM elements were removed. - editingElement.disable(); - expect(document.getElementsByTagName('input'), hasLength(0)); - expect(document.getElementsByTagName('textarea'), hasLength(0)); + editingStrategy!.disable(); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(0)); + expect(defaultTextEditingRoot.querySelectorAll('textarea'), hasLength(0)); // Use multi-line config and expect an `